summaryrefslogtreecommitdiff
path: root/internal/api
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api')
-rw-r--r--internal/api/activitypub.go66
-rw-r--r--internal/api/activitypub/emoji/emoji.go (renamed from internal/api/client/emoji/emoji.go)19
-rw-r--r--internal/api/activitypub/emoji/emojiget.go (renamed from internal/api/s2s/emoji/emojiget.go)29
-rw-r--r--internal/api/activitypub/emoji/emojiget_test.go (renamed from internal/api/s2s/emoji/emojiget_test.go)31
-rw-r--r--internal/api/activitypub/users/common.go (renamed from internal/api/s2s/user/common.go)26
-rw-r--r--internal/api/activitypub/users/followers.go (renamed from internal/api/s2s/user/followers.go)18
-rw-r--r--internal/api/activitypub/users/following.go (renamed from internal/api/s2s/user/following.go)18
-rw-r--r--internal/api/activitypub/users/inboxpost.go (renamed from internal/api/s2s/user/inboxpost.go)14
-rw-r--r--internal/api/activitypub/users/inboxpost_test.go (renamed from internal/api/s2s/user/inboxpost_test.go)28
-rw-r--r--internal/api/activitypub/users/outboxget.go (renamed from internal/api/s2s/user/outboxget.go)20
-rw-r--r--internal/api/activitypub/users/outboxget_test.go (renamed from internal/api/s2s/user/outboxget_test.go)22
-rw-r--r--internal/api/activitypub/users/publickeyget.go (renamed from internal/api/s2s/user/publickeyget.go)18
-rw-r--r--internal/api/activitypub/users/repliesget.go (renamed from internal/api/s2s/user/repliesget.go)24
-rw-r--r--internal/api/activitypub/users/repliesget_test.go (renamed from internal/api/s2s/user/repliesget_test.go)26
-rw-r--r--internal/api/activitypub/users/statusget.go (renamed from internal/api/s2s/user/statusget.go)20
-rw-r--r--internal/api/activitypub/users/statusget_test.go (renamed from internal/api/s2s/user/statusget_test.go)16
-rw-r--r--internal/api/activitypub/users/user.go80
-rw-r--r--internal/api/activitypub/users/user_test.go (renamed from internal/api/s2s/user/user_test.go)34
-rw-r--r--internal/api/activitypub/users/userget.go (renamed from internal/api/s2s/user/userget.go)18
-rw-r--r--internal/api/activitypub/users/userget_test.go (renamed from internal/api/s2s/user/userget_test.go)14
-rw-r--r--internal/api/apimodule.go37
-rw-r--r--internal/api/auth.go64
-rw-r--r--internal/api/auth/auth.go117
-rw-r--r--internal/api/auth/auth_test.go (renamed from internal/api/client/auth/auth_test.go)24
-rw-r--r--internal/api/auth/authorize.go (renamed from internal/api/client/auth/authorize.go)46
-rw-r--r--internal/api/auth/authorize_test.go (renamed from internal/api/client/auth/authorize_test.go)10
-rw-r--r--internal/api/auth/callback.go (renamed from internal/api/client/auth/callback.go)48
-rw-r--r--internal/api/auth/oob.go (renamed from internal/api/client/auth/oob.go)18
-rw-r--r--internal/api/auth/signin.go (renamed from internal/api/client/auth/signin.go)20
-rw-r--r--internal/api/auth/token.go (renamed from internal/api/client/auth/token.go)12
-rw-r--r--internal/api/auth/token_test.go (renamed from internal/api/client/auth/token_test.go)0
-rw-r--r--internal/api/client.go129
-rw-r--r--internal/api/client/accounts/account_test.go (renamed from internal/api/client/account/account_test.go)8
-rw-r--r--internal/api/client/accounts/accountcreate.go (renamed from internal/api/client/account/accountcreate.go)24
-rw-r--r--internal/api/client/accounts/accountcreate_test.go (renamed from internal/api/client/account/accountcreate_test.go)2
-rw-r--r--internal/api/client/accounts/accountdelete.go (renamed from internal/api/client/account/accountdelete.go)16
-rw-r--r--internal/api/client/accounts/accountdelete_test.go (renamed from internal/api/client/account/accountdelete_test.go)16
-rw-r--r--internal/api/client/accounts/accountget.go (renamed from internal/api/client/account/accountget.go)14
-rw-r--r--internal/api/client/accounts/accounts.go (renamed from internal/api/client/account/account.go)64
-rw-r--r--internal/api/client/accounts/accountupdate.go (renamed from internal/api/client/account/accountupdate.go)22
-rw-r--r--internal/api/client/accounts/accountupdate_test.go (renamed from internal/api/client/account/accountupdate_test.go)44
-rw-r--r--internal/api/client/accounts/accountverify.go (renamed from internal/api/client/account/accountverify.go)12
-rw-r--r--internal/api/client/accounts/accountverify_test.go (renamed from internal/api/client/account/accountverify_test.go)8
-rw-r--r--internal/api/client/accounts/block.go (renamed from internal/api/client/account/block.go)14
-rw-r--r--internal/api/client/accounts/block_test.go (renamed from internal/api/client/account/block_test.go)10
-rw-r--r--internal/api/client/accounts/follow.go (renamed from internal/api/client/account/follow.go)20
-rw-r--r--internal/api/client/accounts/follow_test.go (renamed from internal/api/client/account/follow_test.go)10
-rw-r--r--internal/api/client/accounts/followers.go (renamed from internal/api/client/account/followers.go)14
-rw-r--r--internal/api/client/accounts/following.go (renamed from internal/api/client/account/following.go)14
-rw-r--r--internal/api/client/accounts/relationships.go (renamed from internal/api/client/account/relationships.go)18
-rw-r--r--internal/api/client/accounts/statuses.go (renamed from internal/api/client/account/statuses.go)26
-rw-r--r--internal/api/client/accounts/statuses_test.go (renamed from internal/api/client/account/statuses_test.go)12
-rw-r--r--internal/api/client/accounts/unblock.go (renamed from internal/api/client/account/unblock.go)14
-rw-r--r--internal/api/client/accounts/unfollow.go (renamed from internal/api/client/account/unfollow.go)14
-rw-r--r--internal/api/client/admin/accountaction.go18
-rw-r--r--internal/api/client/admin/admin.go41
-rw-r--r--internal/api/client/admin/admin_test.go2
-rw-r--r--internal/api/client/admin/domainblockcreate.go26
-rw-r--r--internal/api/client/admin/domainblockdelete.go14
-rw-r--r--internal/api/client/admin/domainblockget.go16
-rw-r--r--internal/api/client/admin/domainblocksget.go14
-rw-r--r--internal/api/client/admin/emojicategoriesget.go12
-rw-r--r--internal/api/client/admin/emojicreate.go22
-rw-r--r--internal/api/client/admin/emojidelete.go14
-rw-r--r--internal/api/client/admin/emojiget.go14
-rw-r--r--internal/api/client/admin/emojisget.go16
-rw-r--r--internal/api/client/admin/emojiupdate.go36
-rw-r--r--internal/api/client/admin/mediacleanup.go14
-rw-r--r--internal/api/client/admin/mediarefetch.go8
-rw-r--r--internal/api/client/app/app_test.go21
-rw-r--r--internal/api/client/apps/appcreate.go (renamed from internal/api/client/app/appcreate.go)26
-rw-r--r--internal/api/client/apps/apps.go (renamed from internal/api/security/useragentblock.go)24
-rw-r--r--internal/api/client/auth/auth.go105
-rw-r--r--internal/api/client/blocks/blocks.go17
-rw-r--r--internal/api/client/blocks/blocksget.go12
-rw-r--r--internal/api/client/bookmarks/bookmarks.go13
-rw-r--r--internal/api/client/bookmarks/bookmarks_test.go10
-rw-r--r--internal/api/client/bookmarks/bookmarksget.go14
-rw-r--r--internal/api/client/customemojis/customemojis.go (renamed from internal/api/client/app/app.go)21
-rw-r--r--internal/api/client/customemojis/customemojisget.go76
-rw-r--r--internal/api/client/emoji/emojisget.go58
-rw-r--r--internal/api/client/favourites/favourites.go17
-rw-r--r--internal/api/client/favourites/favourites_test.go2
-rw-r--r--internal/api/client/favourites/favouritesget.go12
-rw-r--r--internal/api/client/filters/filter.go (renamed from internal/api/client/filter/filter.go)17
-rw-r--r--internal/api/client/filters/filtersget.go (renamed from internal/api/client/filter/filtersget.go)8
-rw-r--r--internal/api/client/followrequests/authorize.go (renamed from internal/api/client/followrequest/authorize.go)14
-rw-r--r--internal/api/client/followrequests/authorize_test.go (renamed from internal/api/client/followrequest/authorize_test.go)8
-rw-r--r--internal/api/client/followrequests/followrequest.go (renamed from internal/api/client/followrequest/followrequest.go)23
-rw-r--r--internal/api/client/followrequests/followrequest_test.go (renamed from internal/api/client/followrequest/followrequest_test.go)8
-rw-r--r--internal/api/client/followrequests/get.go (renamed from internal/api/client/followrequest/get.go)12
-rw-r--r--internal/api/client/followrequests/get_test.go (renamed from internal/api/client/followrequest/get_test.go)2
-rw-r--r--internal/api/client/followrequests/reject.go (renamed from internal/api/client/followrequest/reject.go)14
-rw-r--r--internal/api/client/followrequests/reject_test.go (renamed from internal/api/client/followrequest/reject_test.go)6
-rw-r--r--internal/api/client/instance/instance.go21
-rw-r--r--internal/api/client/instance/instance_test.go2
-rw-r--r--internal/api/client/instance/instanceget.go8
-rw-r--r--internal/api/client/instance/instancepatch.go22
-rw-r--r--internal/api/client/instance/instancepeersget.go12
-rw-r--r--internal/api/client/list/listsgets.go25
-rw-r--r--internal/api/client/lists/list.go (renamed from internal/api/client/list/list.go)19
-rw-r--r--internal/api/client/lists/listsgets.go (renamed from internal/api/security/robots.go)49
-rw-r--r--internal/api/client/media/media.go27
-rw-r--r--internal/api/client/media/mediacreate.go34
-rw-r--r--internal/api/client/media/mediacreate_test.go53
-rw-r--r--internal/api/client/media/mediaget.go18
-rw-r--r--internal/api/client/media/mediaupdate.go28
-rw-r--r--internal/api/client/media/mediaupdate_test.go31
-rw-r--r--internal/api/client/notifications/notifications.go (renamed from internal/api/client/notification/notification.go)21
-rw-r--r--internal/api/client/notifications/notificationsclear.go (renamed from internal/api/client/notification/notificationsclear.go)12
-rw-r--r--internal/api/client/notifications/notificationsget.go (renamed from internal/api/client/notification/notificationsget.go)14
-rw-r--r--internal/api/client/search/search.go23
-rw-r--r--internal/api/client/search/search_test.go2
-rw-r--r--internal/api/client/search/searchget.go26
-rw-r--r--internal/api/client/statuses/status.go (renamed from internal/api/client/status/status.go)77
-rw-r--r--internal/api/client/statuses/status_test.go (renamed from internal/api/client/status/status_test.go)8
-rw-r--r--internal/api/client/statuses/statusbookmark.go (renamed from internal/api/client/status/statusbookmark.go)14
-rw-r--r--internal/api/client/statuses/statusbookmark_test.go (renamed from internal/api/client/status/statusbookmark_test.go)8
-rw-r--r--internal/api/client/statuses/statusboost.go (renamed from internal/api/client/status/statusboost.go)14
-rw-r--r--internal/api/client/statuses/statusboost_test.go (renamed from internal/api/client/status/statusboost_test.go)28
-rw-r--r--internal/api/client/statuses/statusboostedby.go (renamed from internal/api/client/status/statusboostedby.go)10
-rw-r--r--internal/api/client/statuses/statusboostedby_test.go (renamed from internal/api/client/status/statusboostedby_test.go)8
-rw-r--r--internal/api/client/statuses/statuscontext.go (renamed from internal/api/client/status/statuscontext.go)14
-rw-r--r--internal/api/client/statuses/statuscreate.go (renamed from internal/api/client/status/statuscreate.go)22
-rw-r--r--internal/api/client/statuses/statuscreate_test.go (renamed from internal/api/client/status/statuscreate_test.go)52
-rw-r--r--internal/api/client/statuses/statusdelete.go (renamed from internal/api/client/status/statusdelete.go)14
-rw-r--r--internal/api/client/statuses/statusdelete_test.go (renamed from internal/api/client/status/statusdelete_test.go)12
-rw-r--r--internal/api/client/statuses/statusfave.go (renamed from internal/api/client/status/statusfave.go)14
-rw-r--r--internal/api/client/statuses/statusfave_test.go (renamed from internal/api/client/status/statusfave_test.go)19
-rw-r--r--internal/api/client/statuses/statusfavedby.go (renamed from internal/api/client/status/statusfavedby.go)14
-rw-r--r--internal/api/client/statuses/statusfavedby_test.go (renamed from internal/api/client/status/statusfavedby_test.go)12
-rw-r--r--internal/api/client/statuses/statusget.go (renamed from internal/api/client/status/statusget.go)14
-rw-r--r--internal/api/client/statuses/statusget_test.go (renamed from internal/api/client/status/statusget_test.go)2
-rw-r--r--internal/api/client/statuses/statusunbookmark.go (renamed from internal/api/client/status/statusunbookmark.go)14
-rw-r--r--internal/api/client/statuses/statusunbookmark_test.go (renamed from internal/api/client/status/statusunbookmark_test.go)8
-rw-r--r--internal/api/client/statuses/statusunboost.go (renamed from internal/api/client/status/statusunboost.go)14
-rw-r--r--internal/api/client/statuses/statusunfave.go (renamed from internal/api/client/status/statusunfave.go)14
-rw-r--r--internal/api/client/statuses/statusunfave_test.go (renamed from internal/api/client/status/statusunfave_test.go)22
-rw-r--r--internal/api/client/streaming/stream.go60
-rw-r--r--internal/api/client/streaming/streaming.go19
-rw-r--r--internal/api/client/streaming/streaming_test.go2
-rw-r--r--internal/api/client/timelines/home.go (renamed from internal/api/client/timeline/home.go)16
-rw-r--r--internal/api/client/timelines/public.go (renamed from internal/api/client/timeline/public.go)16
-rw-r--r--internal/api/client/timelines/timeline.go (renamed from internal/api/client/timeline/timeline.go)21
-rw-r--r--internal/api/client/user/passwordchange.go20
-rw-r--r--internal/api/client/user/user.go17
-rw-r--r--internal/api/client/user/user_test.go2
-rw-r--r--internal/api/fileserver.go55
-rw-r--r--internal/api/fileserver/fileserver.go (renamed from internal/api/client/fileserver/fileserver.go)28
-rw-r--r--internal/api/fileserver/fileserver_test.go (renamed from internal/api/client/fileserver/fileserver_test.go)12
-rw-r--r--internal/api/fileserver/servefile.go (renamed from internal/api/client/fileserver/servefile.go)33
-rw-r--r--internal/api/fileserver/servefile_test.go (renamed from internal/api/client/fileserver/servefile_test.go)2
-rw-r--r--internal/api/nodeinfo.go50
-rw-r--r--internal/api/nodeinfo/nodeinfo.go46
-rw-r--r--internal/api/nodeinfo/nodeinfoget.go (renamed from internal/api/s2s/nodeinfo/nodeinfoget.go)20
-rw-r--r--internal/api/s2s/emoji/emoji.go53
-rw-r--r--internal/api/s2s/nodeinfo/nodeinfo.go53
-rw-r--r--internal/api/s2s/user/user.go89
-rw-r--r--internal/api/security/extraheaders.go26
-rw-r--r--internal/api/security/flocblock.go31
-rw-r--r--internal/api/security/ratelimit.go70
-rw-r--r--internal/api/security/security.go65
-rw-r--r--internal/api/security/signaturecheck.go51
-rw-r--r--internal/api/security/tokencheck.go120
-rw-r--r--internal/api/util/errorhandling.go (renamed from internal/api/errorhandling.go)2
-rw-r--r--internal/api/util/mime.go (renamed from internal/api/mime.go)2
-rw-r--r--internal/api/util/negotiate.go (renamed from internal/api/negotiate.go)2
-rw-r--r--internal/api/util/signaturectx.go (renamed from internal/api/client/auth/util.go)22
-rw-r--r--internal/api/wellknown.go55
-rw-r--r--internal/api/wellknown/nodeinfo/nodeinfo.go47
-rw-r--r--internal/api/wellknown/nodeinfo/nodeinfoget.go (renamed from internal/api/s2s/nodeinfo/wellknownget.go)16
-rw-r--r--internal/api/wellknown/webfinger/webfinger.go (renamed from internal/api/s2s/webfinger/webfinger.go)18
-rw-r--r--internal/api/wellknown/webfinger/webfinger_test.go (renamed from internal/api/s2s/webfinger/webfinger_test.go)23
-rw-r--r--internal/api/wellknown/webfinger/webfingerget.go (renamed from internal/api/s2s/webfinger/webfingerget.go)45
-rw-r--r--internal/api/wellknown/webfinger/webfingerget_test.go (renamed from internal/api/s2s/webfinger/webfingerget_test.go)6
175 files changed, 2080 insertions, 2279 deletions
diff --git a/internal/api/activitypub.go b/internal/api/activitypub.go
new file mode 100644
index 000000000..68c3b81e0
--- /dev/null
+++ b/internal/api/activitypub.go
@@ -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,
+ }
+}
diff --git a/internal/api/client/emoji/emoji.go b/internal/api/activitypub/emoji/emoji.go
index 871a12854..f0eab0f02 100644
--- a/internal/api/client/emoji/emoji.go
+++ b/internal/api/activitypub/emoji/emoji.go
@@ -21,30 +21,27 @@ package emoji
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base path for serving the emoji API
- BasePath = "/api/v1/custom_emojis"
+ // EmojiIDKey is for emoji IDs
+ EmojiIDKey = "id"
+ // EmojiBasePath is the base path for serving AP Emojis, minus the "emoji" prefix
+ EmojiWithIDPath = "/:" + EmojiIDKey
)
-// Module implements the ClientAPIModule interface for everything related to emoji
type Module struct {
processor processing.Processor
}
-// New returns a new emoji module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.EmojisGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, EmojiWithIDPath, m.EmojiGetHandler)
}
diff --git a/internal/api/s2s/emoji/emojiget.go b/internal/api/activitypub/emoji/emojiget.go
index 28a737f9a..4e5ca25f4 100644
--- a/internal/api/s2s/emoji/emojiget.go
+++ b/internal/api/activitypub/emoji/emojiget.go
@@ -19,54 +19,39 @@
package emoji
import (
- "context"
"encoding/json"
"errors"
"net/http"
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/ap"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
-// EmojiGetHandler
func (m *Module) EmojiGetHandler(c *gin.Context) {
- // usernames on our instance are always lowercase
requestedEmojiID := strings.ToUpper(c.Param(EmojiIDKey))
if requestedEmojiID == "" {
err := errors.New("no emoji id specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.ActivityPubAcceptHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- ctx := c.Request.Context()
- verifier, signed := c.Get(string(ap.ContextRequestingPublicKeyVerifier))
- if signed {
- ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeyVerifier, verifier)
- }
-
- signature, signed := c.Get(string(ap.ContextRequestingPublicKeySignature))
- if signed {
- ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeySignature, signature)
- }
-
- resp, errWithCode := m.processor.GetFediEmoji(ctx, requestedEmojiID, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediEmoji(apiutil.TransferSignatureContext(c), requestedEmojiID, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/emoji/emojiget_test.go b/internal/api/activitypub/emoji/emojiget_test.go
index 16bc9dc1a..5b06f7a33 100644
--- a/internal/api/s2s/emoji/emojiget_test.go
+++ b/internal/api/activitypub/emoji/emojiget_test.go
@@ -26,8 +26,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/emoji"
- "github.com/superseriousbusiness/gotosocial/internal/api/security"
+ "github.com/superseriousbusiness/gotosocial/internal/api/activitypub/emoji"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
@@ -35,7 +34,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
+ "github.com/superseriousbusiness/gotosocial/internal/middleware"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
@@ -44,20 +43,20 @@ import (
type EmojiGetTestSuite struct {
suite.Suite
- db db.DB
- tc typeutils.TypeConverter
- mediaManager media.Manager
- federator federation.Federator
- emailSender email.Sender
- processor processing.Processor
- storage *storage.Driver
- oauthServer oauth.Server
- securityModule *security.Module
+ db db.DB
+ tc typeutils.TypeConverter
+ mediaManager media.Manager
+ federator federation.Federator
+ emailSender email.Sender
+ processor processing.Processor
+ storage *storage.Driver
testEmojis map[string]*gtsmodel.Emoji
testAccounts map[string]*gtsmodel.Account
emojiModule *emoji.Module
+
+ signatureCheck gin.HandlerFunc
}
func (suite *EmojiGetTestSuite) SetupSuite() {
@@ -79,12 +78,12 @@ func (suite *EmojiGetTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.emojiModule = emoji.New(suite.processor).(*emoji.Module)
- suite.oauthServer = testrig.NewTestOauthServer(suite.db)
- suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
+ suite.emojiModule = emoji.New(suite.processor)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
+ suite.signatureCheck = middleware.SignatureCheck(suite.db.IsURIBlocked)
+
suite.NoError(suite.processor.Start())
}
@@ -108,7 +107,7 @@ func (suite *EmojiGetTestSuite) TestGetEmoji() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
diff --git a/internal/api/s2s/user/common.go b/internal/api/activitypub/users/common.go
index 0a215ebbd..bf7623774 100644
--- a/internal/api/s2s/user/common.go
+++ b/internal/api/activitypub/users/common.go
@@ -16,31 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
-
-import (
- "context"
-
- "github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/ap"
-)
-
-// transferContext transfers the signature verifier and signature from the gin context to the request context
-func transferContext(c *gin.Context) context.Context {
- ctx := c.Request.Context()
-
- verifier, signed := c.Get(string(ap.ContextRequestingPublicKeyVerifier))
- if signed {
- ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeyVerifier, verifier)
- }
-
- signature, signed := c.Get(string(ap.ContextRequestingPublicKeySignature))
- if signed {
- ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeySignature, signature)
- }
-
- return ctx
-}
+package users
// SwaggerCollection represents an activitypub collection.
// swagger:model swaggerCollection
diff --git a/internal/api/s2s/user/followers.go b/internal/api/activitypub/users/followers.go
index 675688311..69b9f52fd 100644
--- a/internal/api/s2s/user/followers.go
+++ b/internal/api/activitypub/users/followers.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"encoding/json"
@@ -25,7 +25,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -35,31 +35,31 @@ func (m *Module) FollowersGETHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if format == string(api.TextHTML) {
+ if format == string(apiutil.TextHTML) {
// redirect to the user's profile
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
return
}
- resp, errWithCode := m.processor.GetFediFollowers(transferContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediFollowers(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/user/following.go b/internal/api/activitypub/users/following.go
index d4404ea08..64337d4cd 100644
--- a/internal/api/s2s/user/following.go
+++ b/internal/api/activitypub/users/following.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"encoding/json"
@@ -25,7 +25,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -35,31 +35,31 @@ func (m *Module) FollowingGETHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if format == string(api.TextHTML) {
+ if format == string(apiutil.TextHTML) {
// redirect to the user's profile
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
return
}
- resp, errWithCode := m.processor.GetFediFollowing(transferContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediFollowing(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/user/inboxpost.go b/internal/api/activitypub/users/inboxpost.go
index fa6792cd2..1d8f9f923 100644
--- a/internal/api/s2s/user/inboxpost.go
+++ b/internal/api/activitypub/users/inboxpost.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"errors"
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror" //nolint:typecheck
)
@@ -34,18 +34,18 @@ func (m *Module) InboxPOSTHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- if posted, err := m.processor.InboxPost(transferContext(c), c.Writer, c.Request); err != nil {
+ if posted, err := m.processor.InboxPost(apiutil.TransferSignatureContext(c), c.Writer, c.Request); err != nil {
if withCode, ok := err.(gtserror.WithCode); ok {
- api.ErrorHandler(c, withCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, withCode, m.processor.InstanceGet)
} else {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
}
} else if !posted {
err := errors.New("unable to process request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
}
}
diff --git a/internal/api/s2s/user/inboxpost_test.go b/internal/api/activitypub/users/inboxpost_test.go
index 3821406be..53b6b8aac 100644
--- a/internal/api/s2s/user/inboxpost_test.go
+++ b/internal/api/activitypub/users/inboxpost_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user_test
+package users_test
import (
"bytes"
@@ -32,7 +32,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/activity/pub"
"github.com/superseriousbusiness/activity/streams"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
+ "github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
@@ -92,7 +92,7 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
suite.NoError(processor.Start())
// setup request
@@ -105,13 +105,13 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
ctx.Request.Header.Set("Content-Type", "application/activity+json")
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: blockedAccount.Username,
},
}
@@ -196,7 +196,7 @@ func (suite *InboxPostTestSuite) TestPostUnblock() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
suite.NoError(processor.Start())
// setup request
@@ -209,13 +209,13 @@ func (suite *InboxPostTestSuite) TestPostUnblock() {
ctx.Request.Header.Set("Content-Type", "application/activity+json")
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: blockedAccount.Username,
},
}
@@ -298,7 +298,7 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
suite.NoError(processor.Start())
// setup request
@@ -311,13 +311,13 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
ctx.Request.Header.Set("Content-Type", "application/activity+json")
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: receivingAccount.Username,
},
}
@@ -431,7 +431,7 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
suite.NoError(processor.Start())
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
// setup request
recorder := httptest.NewRecorder()
@@ -443,13 +443,13 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
ctx.Request.Header.Set("Content-Type", "application/activity+json")
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: receivingAccount.Username,
},
}
diff --git a/internal/api/s2s/user/outboxget.go b/internal/api/activitypub/users/outboxget.go
index 726f86237..8e5d1a751 100644
--- a/internal/api/s2s/user/outboxget.go
+++ b/internal/api/activitypub/users/outboxget.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"encoding/json"
@@ -27,7 +27,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -90,17 +90,17 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if format == string(api.TextHTML) {
+ if format == string(apiutil.TextHTML) {
// redirect to the user's profile
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
return
@@ -111,7 +111,7 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(pageString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", PageKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
page = i
@@ -129,15 +129,15 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
maxID = maxIDString
}
- resp, errWithCode := m.processor.GetFediOutbox(transferContext(c), requestedUsername, page, maxID, minID, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediOutbox(apiutil.TransferSignatureContext(c), requestedUsername, page, maxID, minID, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/user/outboxget_test.go b/internal/api/activitypub/users/outboxget_test.go
index 35a048323..cd0518350 100644
--- a/internal/api/s2s/user/outboxget_test.go
+++ b/internal/api/activitypub/users/outboxget_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user_test
+package users_test
import (
"context"
@@ -30,7 +30,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
+ "github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -55,13 +55,13 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
}
@@ -102,7 +102,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
suite.NoError(processor.Start())
// setup request
@@ -114,13 +114,13 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
}
@@ -161,7 +161,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
suite.NoError(processor.Start())
// setup request
@@ -173,17 +173,17 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
gin.Param{
- Key: user.MaxIDKey,
+ Key: users.MaxIDKey,
Value: "01F8MHAMCHF6Y650WCRSCP4WMY",
},
}
diff --git a/internal/api/s2s/user/publickeyget.go b/internal/api/activitypub/users/publickeyget.go
index 265c01ee5..d57aa2f9d 100644
--- a/internal/api/s2s/user/publickeyget.go
+++ b/internal/api/activitypub/users/publickeyget.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"encoding/json"
@@ -25,7 +25,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -39,31 +39,31 @@ func (m *Module) PublicKeyGETHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if format == string(api.TextHTML) {
+ if format == string(apiutil.TextHTML) {
// redirect to the user's profile
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
return
}
- resp, errWithCode := m.processor.GetFediUser(transferContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediUser(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/user/repliesget.go b/internal/api/activitypub/users/repliesget.go
index b3b20d0c2..253166446 100644
--- a/internal/api/s2s/user/repliesget.go
+++ b/internal/api/activitypub/users/repliesget.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"encoding/json"
@@ -27,7 +27,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -97,7 +97,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -105,17 +105,17 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
if requestedStatusID == "" {
err := errors.New("no status id specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if format == string(api.TextHTML) {
+ if format == string(apiutil.TextHTML) {
// redirect to the status
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
return
@@ -126,7 +126,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(pageString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", PageKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
page = i
@@ -138,7 +138,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(onlyOtherAccountsString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", OnlyOtherAccountsKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
onlyOtherAccounts = i
@@ -150,15 +150,15 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
minID = minIDString
}
- resp, errWithCode := m.processor.GetFediStatusReplies(transferContext(c), requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediStatusReplies(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/user/repliesget_test.go b/internal/api/activitypub/users/repliesget_test.go
index 5ab7f1ebd..3d8e0b12a 100644
--- a/internal/api/s2s/user/repliesget_test.go
+++ b/internal/api/activitypub/users/repliesget_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user_test
+package users_test
import (
"context"
@@ -32,7 +32,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
+ "github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -58,17 +58,17 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
gin.Param{
- Key: user.StatusIDKey,
+ Key: users.StatusIDKey,
Value: targetStatus.ID,
},
}
@@ -111,7 +111,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
suite.NoError(processor.Start())
// setup request
@@ -123,17 +123,17 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
gin.Param{
- Key: user.StatusIDKey,
+ Key: users.StatusIDKey,
Value: targetStatus.ID,
},
}
@@ -179,7 +179,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
- userModule := user.New(processor).(*user.Module)
+ userModule := users.New(processor)
suite.NoError(processor.Start())
// setup request
@@ -191,17 +191,17 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
gin.Param{
- Key: user.StatusIDKey,
+ Key: users.StatusIDKey,
Value: targetStatus.ID,
},
}
diff --git a/internal/api/s2s/user/statusget.go b/internal/api/activitypub/users/statusget.go
index 3e3d6ea56..a72f8fd16 100644
--- a/internal/api/s2s/user/statusget.go
+++ b/internal/api/activitypub/users/statusget.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"encoding/json"
@@ -25,7 +25,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -35,7 +35,7 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -43,31 +43,31 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
if requestedStatusID == "" {
err := errors.New("no status id specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if format == string(api.TextHTML) {
+ if format == string(apiutil.TextHTML) {
// redirect to the status
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
return
}
- resp, errWithCode := m.processor.GetFediStatus(transferContext(c), requestedUsername, requestedStatusID, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediStatus(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/user/statusget_test.go b/internal/api/activitypub/users/statusget_test.go
index 42f7dbb1b..37d311e7b 100644
--- a/internal/api/s2s/user/statusget_test.go
+++ b/internal/api/activitypub/users/statusget_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user_test
+package users_test
import (
"context"
@@ -31,7 +31,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
+ "github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -55,17 +55,17 @@ func (suite *StatusGetTestSuite) TestGetStatus() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
gin.Param{
- Key: user.StatusIDKey,
+ Key: users.StatusIDKey,
Value: targetStatus.ID,
},
}
@@ -114,17 +114,17 @@ func (suite *StatusGetTestSuite) TestGetStatusLowercase() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: strings.ToLower(targetAccount.Username),
},
gin.Param{
- Key: user.StatusIDKey,
+ Key: users.StatusIDKey,
Value: strings.ToLower(targetStatus.ID),
},
}
diff --git a/internal/api/activitypub/users/user.go b/internal/api/activitypub/users/user.go
new file mode 100644
index 000000000..d2e02fb08
--- /dev/null
+++ b/internal/api/activitypub/users/user.go
@@ -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)
+}
diff --git a/internal/api/s2s/user/user_test.go b/internal/api/activitypub/users/user_test.go
index 444e9cab5..0f08366f0 100644
--- a/internal/api/s2s/user/user_test.go
+++ b/internal/api/activitypub/users/user_test.go
@@ -16,12 +16,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user_test
+package users_test
import (
+ "github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
- "github.com/superseriousbusiness/gotosocial/internal/api/security"
+ "github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
@@ -29,7 +29,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
+ "github.com/superseriousbusiness/gotosocial/internal/middleware"
"github.com/superseriousbusiness/gotosocial/internal/processing"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
@@ -39,15 +39,13 @@ import (
type UserStandardTestSuite struct {
// standard suite interfaces
suite.Suite
- db db.DB
- tc typeutils.TypeConverter
- mediaManager media.Manager
- federator federation.Federator
- emailSender email.Sender
- processor processing.Processor
- storage *storage.Driver
- oauthServer oauth.Server
- securityModule *security.Module
+ db db.DB
+ tc typeutils.TypeConverter
+ mediaManager media.Manager
+ federator federation.Federator
+ emailSender email.Sender
+ processor processing.Processor
+ storage *storage.Driver
// standard suite models
testTokens map[string]*gtsmodel.Token
@@ -60,7 +58,9 @@ type UserStandardTestSuite struct {
testBlocks map[string]*gtsmodel.Block
// module being tested
- userModule *user.Module
+ userModule *users.Module
+
+ signatureCheck gin.HandlerFunc
}
func (suite *UserStandardTestSuite) SetupSuite() {
@@ -88,12 +88,12 @@ func (suite *UserStandardTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.userModule = user.New(suite.processor).(*user.Module)
- suite.oauthServer = testrig.NewTestOauthServer(suite.db)
- suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
+ suite.userModule = users.New(suite.processor)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
+ suite.signatureCheck = middleware.SignatureCheck(suite.db.IsURIBlocked)
+
suite.NoError(suite.processor.Start())
}
diff --git a/internal/api/s2s/user/userget.go b/internal/api/activitypub/users/userget.go
index 508c8be7d..51b0ebd72 100644
--- a/internal/api/s2s/user/userget.go
+++ b/internal/api/activitypub/users/userget.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user
+package users
import (
"encoding/json"
@@ -25,7 +25,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
@@ -43,31 +43,31 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
requestedUsername := strings.ToLower(c.Param(UsernameKey))
if requestedUsername == "" {
err := errors.New("no username specified in request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
+ format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if format == string(api.TextHTML) {
+ if format == string(apiutil.TextHTML) {
// redirect to the user's profile
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
return
}
- resp, errWithCode := m.processor.GetFediUser(transferContext(c), requestedUsername, c.Request.URL)
+ resp, errWithCode := m.processor.GetFediUser(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
b, err := json.Marshal(resp)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/user/userget_test.go b/internal/api/activitypub/users/userget_test.go
index c656911d7..511634fc7 100644
--- a/internal/api/s2s/user/userget_test.go
+++ b/internal/api/activitypub/users/userget_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package user_test
+package users_test
import (
"context"
@@ -30,8 +30,8 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
+ "github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -55,13 +55,13 @@ func (suite *UserGetTestSuite) TestGetUser() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
}
@@ -98,7 +98,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
// TestGetUserPublicKeyDeleted checks whether the public key of a deleted account can still be dereferenced.
// This is needed by remote instances for authenticating delete requests and stuff like that.
func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
- userModule := user.New(suite.processor).(*user.Module)
+ userModule := users.New(suite.processor)
targetAccount := suite.testAccounts["local_account_1"]
// first delete the account, as though zork had deleted himself
@@ -133,13 +133,13 @@ func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
// we need to pass the context through signature check first to set appropriate values on it
- suite.securityModule.SignatureCheck(ctx)
+ suite.signatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: user.UsernameKey,
+ Key: users.UsernameKey,
Value: targetAccount.Username,
},
}
diff --git a/internal/api/apimodule.go b/internal/api/apimodule.go
deleted file mode 100644
index 67dc3a850..000000000
--- a/internal/api/apimodule.go
+++ /dev/null
@@ -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
-}
diff --git a/internal/api/auth.go b/internal/api/auth.go
new file mode 100644
index 000000000..472c6922a
--- /dev/null
+++ b/internal/api/auth.go
@@ -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),
+ }
+}
diff --git a/internal/api/auth/auth.go b/internal/api/auth/auth.go
new file mode 100644
index 000000000..7ce992466
--- /dev/null
+++ b/internal/api/auth/auth.go
@@ -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)
+ }
+}
diff --git a/internal/api/client/auth/auth_test.go b/internal/api/auth/auth_test.go
index 75e958418..cb92850d0 100644
--- a/internal/api/client/auth/auth_test.go
+++ b/internal/api/auth/auth_test.go
@@ -20,7 +20,6 @@ package auth_test
import (
"bytes"
- "context"
"fmt"
"net/http/httptest"
@@ -28,7 +27,7 @@ import (
"github.com/gin-contrib/sessions/memstore"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/auth"
+ "github.com/superseriousbusiness/gotosocial/internal/api/auth"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -37,10 +36,9 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/messages"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
+ "github.com/superseriousbusiness/gotosocial/internal/middleware"
"github.com/superseriousbusiness/gotosocial/internal/oidc"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -54,7 +52,6 @@ type AuthStandardTestSuite struct {
processor processing.Processor
emailSender email.Sender
idp oidc.IDP
- oauthServer oauth.Server
// standard suite models
testTokens map[string]*gtsmodel.Token
@@ -90,17 +87,10 @@ func (suite *AuthStandardTestSuite) SetupTest() {
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewInMemoryStorage()
suite.mediaManager = testrig.NewTestMediaManager(suite.db, suite.storage)
- suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
- suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
+ suite.emailSender = testrig.NewEmailSender("../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
-
- suite.oauthServer = testrig.NewTestOauthServer(suite.db)
- var err error
- suite.idp, err = oidc.NewIDP(context.Background())
- if err != nil {
- panic(err)
- }
- suite.authModule = auth.New(suite.db, suite.idp, suite.processor).(*auth.Module)
+ suite.authModule = auth.New(suite.db, suite.processor, suite.idp)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
}
@@ -114,7 +104,7 @@ func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath
ctx, engine := testrig.CreateGinTestContext(recorder, nil)
// load templates into the engine
- testrig.ConfigureTemplatesWithGin(engine, "../../../../web/template")
+ testrig.ConfigureTemplatesWithGin(engine, "../../../web/template")
// create the request
protocol := config.GetProtocol()
@@ -131,7 +121,7 @@ func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath
// trigger the session middleware on the context
store := memstore.NewStore(make([]byte, 32), make([]byte, 32))
- store.Options(router.SessionOptions())
+ store.Options(middleware.SessionOptions())
sessionMiddleware := sessions.Sessions("gotosocial-localhost", store)
sessionMiddleware(ctx)
diff --git a/internal/api/client/auth/authorize.go b/internal/api/auth/authorize.go
index f28d1dfc9..e504f6be2 100644
--- a/internal/api/client/auth/authorize.go
+++ b/internal/api/auth/authorize.go
@@ -27,8 +27,8 @@ import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
@@ -42,8 +42,8 @@ import (
func (m *Module) AuthorizeGETHandler(c *gin.Context) {
s := sessions.Default(c)
- if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.HTMLAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -51,20 +51,20 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
// If it's not set, then we don't know yet who the user is, so we need to redirect them to the sign in page.
userID, ok := s.Get(sessionUserID).(string)
if !ok || userID == "" {
- form := &model.OAuthAuthorize{}
+ form := &apimodel.OAuthAuthorize{}
if err := c.ShouldBind(form); err != nil {
m.clearSession(s)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
if errWithCode := saveAuthFormToSession(s, form); errWithCode != nil {
m.clearSession(s)
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
- c.Redirect(http.StatusSeeOther, AuthSignInPath)
+ c.Redirect(http.StatusSeeOther, "/auth"+AuthSignInPath)
return
}
@@ -73,7 +73,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
if !ok || clientID == "" {
m.clearSession(s)
err := fmt.Errorf("key %s was not found in session", sessionClientID)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
@@ -87,7 +87,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -101,7 +101,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -115,7 +115,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -128,7 +128,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
if !ok || redirect == "" {
m.clearSession(s)
err := fmt.Errorf("key %s was not found in session", sessionRedirectURI)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
@@ -136,13 +136,13 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
if !ok || scope == "" {
m.clearSession(s)
err := fmt.Errorf("key %s was not found in session", sessionScope)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -206,7 +206,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
if len(errs) != 0 {
errs = append(errs, oauth.HelpfulAdvice)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during AuthorizePOSTHandler"), errs...), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during AuthorizePOSTHandler"), errs...), m.processor.InstanceGet)
return
}
@@ -220,7 +220,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -234,7 +234,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -263,13 +263,13 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
}
if errWithCode := m.processor.OAuthHandleAuthorizeRequest(c.Writer, c.Request); errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
}
}
// saveAuthFormToSession checks the given OAuthAuthorize form,
// and stores the values in the form into the session.
-func saveAuthFormToSession(s sessions.Session, form *model.OAuthAuthorize) gtserror.WithCode {
+func saveAuthFormToSession(s sessions.Session, form *apimodel.OAuthAuthorize) gtserror.WithCode {
if form == nil {
err := errors.New("OAuthAuthorize form was nil")
return gtserror.NewErrorBadRequest(err, err.Error(), oauth.HelpfulAdvice)
@@ -314,19 +314,19 @@ func saveAuthFormToSession(s sessions.Session, form *model.OAuthAuthorize) gtser
func ensureUserIsAuthorizedOrRedirect(ctx *gin.Context, user *gtsmodel.User, account *gtsmodel.Account) (redirected bool) {
if user.ConfirmedAt.IsZero() {
- ctx.Redirect(http.StatusSeeOther, CheckYourEmailPath)
+ ctx.Redirect(http.StatusSeeOther, "/auth"+AuthCheckYourEmailPath)
redirected = true
return
}
if !*user.Approved {
- ctx.Redirect(http.StatusSeeOther, WaitForApprovalPath)
+ ctx.Redirect(http.StatusSeeOther, "/auth"+AuthWaitForApprovalPath)
redirected = true
return
}
if *user.Disabled || !account.SuspendedAt.IsZero() {
- ctx.Redirect(http.StatusSeeOther, AccountDisabledPath)
+ ctx.Redirect(http.StatusSeeOther, "/auth"+AuthAccountDisabledPath)
redirected = true
return
}
diff --git a/internal/api/client/auth/authorize_test.go b/internal/api/auth/authorize_test.go
index 738b3b910..ff65d041b 100644
--- a/internal/api/client/auth/authorize_test.go
+++ b/internal/api/auth/authorize_test.go
@@ -9,7 +9,7 @@ import (
"github.com/gin-contrib/sessions"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/auth"
+ "github.com/superseriousbusiness/gotosocial/internal/api/auth"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -34,7 +34,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
return []string{"confirmed_at"}
},
expectedStatusCode: http.StatusSeeOther,
- expectedLocationHeader: auth.CheckYourEmailPath,
+ expectedLocationHeader: "/auth" + auth.AuthCheckYourEmailPath,
},
{
description: "user has their email confirmed but is not approved",
@@ -44,7 +44,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
return []string{"confirmed_at", "email"}
},
expectedStatusCode: http.StatusSeeOther,
- expectedLocationHeader: auth.WaitForApprovalPath,
+ expectedLocationHeader: "/auth" + auth.AuthWaitForApprovalPath,
},
{
description: "user has their email confirmed and is approved, but User entity has been disabled",
@@ -56,7 +56,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
return []string{"confirmed_at", "email", "approved", "disabled"}
},
expectedStatusCode: http.StatusSeeOther,
- expectedLocationHeader: auth.AccountDisabledPath,
+ expectedLocationHeader: "/auth" + auth.AuthAccountDisabledPath,
},
{
description: "user has their email confirmed and is approved, but Account entity has been suspended",
@@ -69,7 +69,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
return []string{"confirmed_at", "email", "approved", "disabled"}
},
expectedStatusCode: http.StatusSeeOther,
- expectedLocationHeader: auth.AccountDisabledPath,
+ expectedLocationHeader: "/auth" + auth.AuthAccountDisabledPath,
},
}
diff --git a/internal/api/client/auth/callback.go b/internal/api/auth/callback.go
index c97abf7aa..d344b5d5f 100644
--- a/internal/api/client/auth/callback.go
+++ b/internal/api/auth/callback.go
@@ -29,7 +29,7 @@ import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
@@ -47,6 +47,12 @@ type extraInfo struct {
// CallbackGETHandler parses a token from an external auth provider.
func (m *Module) CallbackGETHandler(c *gin.Context) {
+ if !config.GetOIDCEnabled() {
+ err := errors.New("oidc is not enabled for this server")
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet)
+ return
+ }
+
s := sessions.Default(c)
// check the query vs session state parameter to mitigate csrf
@@ -56,7 +62,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
if returnedInternalState == "" {
m.clearSession(s)
err := fmt.Errorf("%s parameter not found on callback query", callbackStateParam)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -65,14 +71,14 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
if !ok {
m.clearSession(s)
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if returnedInternalState != savedInternalState {
m.clearSession(s)
err := errors.New("mismatch between callback state and saved state")
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -81,14 +87,14 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
if code == "" {
m.clearSession(s)
err := fmt.Errorf("%s parameter not found on callback query", callbackCodeParam)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
claims, errWithCode := m.idp.HandleCallback(c.Request.Context(), code)
if errWithCode != nil {
m.clearSession(s)
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -98,7 +104,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
if !ok || clientID == "" {
m.clearSession(s)
err := fmt.Errorf("key %s was not found in session", sessionClientID)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
@@ -112,21 +118,21 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
user, errWithCode := m.fetchUserForClaims(c.Request.Context(), claims, net.IP(c.ClientIP()), app.ID)
if errWithCode != nil {
m.clearSession(s)
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
if user == nil {
// no user exists yet - let's ask them for their preferred username
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -135,7 +141,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
s.Set(sessionAppID, app.ID)
if err := s.Save(); err != nil {
m.clearSession(s)
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
c.HTML(http.StatusOK, "finalize.tmpl", gin.H{
@@ -148,10 +154,10 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
s.Set(sessionUserID, user.ID)
if err := s.Save(); err != nil {
m.clearSession(s)
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
- c.Redirect(http.StatusFound, OauthAuthorizePath)
+ c.Redirect(http.StatusFound, "/oauth"+OauthAuthorizePath)
}
// FinalizePOSTHandler registers the user after additional data has been provided
@@ -161,7 +167,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
form := &extraInfo{}
if err := c.ShouldBind(form); err != nil {
m.clearSession(s)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
@@ -169,7 +175,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
validationError := func(err error) {
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.HTML(http.StatusOK, "finalize.tmpl", gin.H{
@@ -189,7 +195,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
// see if the username is still available
usernameAvailable, err := m.db.IsUsernameAvailable(c.Request.Context(), form.Username)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
if !usernameAvailable {
@@ -201,7 +207,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
appID, ok := s.Get(sessionAppID).(string)
if !ok {
err := fmt.Errorf("key %s was not found in session", sessionAppID)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
@@ -209,7 +215,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
claims, ok := s.Get(sessionClaims).(*oidc.Claims)
if !ok {
err := fmt.Errorf("key %s was not found in session", sessionClaims)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
@@ -217,7 +223,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
user, errWithCode := m.createUserFromOIDC(c.Request.Context(), claims, form, net.IP(c.ClientIP()), appID)
if errWithCode != nil {
m.clearSession(s)
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
s.Delete(sessionClaims)
@@ -225,10 +231,10 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
s.Set(sessionUserID, user.ID)
if err := s.Save(); err != nil {
m.clearSession(s)
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
- c.Redirect(http.StatusFound, OauthAuthorizePath)
+ c.Redirect(http.StatusFound, "/oauth"+OauthAuthorizePath)
}
func (m *Module) fetchUserForClaims(ctx context.Context, claims *oidc.Claims, ip net.IP, appID string) (*gtsmodel.User, gtserror.WithCode) {
diff --git a/internal/api/client/auth/oob.go b/internal/api/auth/oob.go
index 92e49d328..97f9c0f8c 100644
--- a/internal/api/client/auth/oob.go
+++ b/internal/api/auth/oob.go
@@ -26,8 +26,8 @@ import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
@@ -38,16 +38,18 @@ func (m *Module) OobHandler(c *gin.Context) {
host := config.GetHost()
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), host)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
- instanceGet := func(ctx context.Context, domain string) (*model.Instance, gtserror.WithCode) { return instance, nil }
+ instanceGet := func(ctx context.Context, domain string) (*apimodel.Instance, gtserror.WithCode) {
+ return instance, nil
+ }
oobToken := c.Query("code")
if oobToken == "" {
err := errors.New("no 'code' query value provided in callback redirect")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error(), oauth.HelpfulAdvice), instanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error(), oauth.HelpfulAdvice), instanceGet)
return
}
@@ -67,7 +69,7 @@ func (m *Module) OobHandler(c *gin.Context) {
if len(errs) != 0 {
errs = append(errs, oauth.HelpfulAdvice)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during OobHandler"), errs...), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during OobHandler"), errs...), m.processor.InstanceGet)
return
}
@@ -81,7 +83,7 @@ func (m *Module) OobHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, instanceGet)
+ apiutil.ErrorHandler(c, errWithCode, instanceGet)
return
}
@@ -95,7 +97,7 @@ func (m *Module) OobHandler(c *gin.Context) {
} else {
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
}
- api.ErrorHandler(c, errWithCode, instanceGet)
+ apiutil.ErrorHandler(c, errWithCode, instanceGet)
return
}
diff --git a/internal/api/client/auth/signin.go b/internal/api/auth/signin.go
index 73a5de398..bae33a43b 100644
--- a/internal/api/client/auth/signin.go
+++ b/internal/api/auth/signin.go
@@ -26,7 +26,7 @@ import (
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -44,15 +44,15 @@ type login struct {
// The form will then POST to the sign in page, which will be handled by SignInPOSTHandler.
// If an idp provider is set, then the user will be redirected to that to do their sign in.
func (m *Module) SignInGETHandler(c *gin.Context) {
- if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.HTMLAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- if m.idp == nil {
+ if !config.GetOIDCEnabled() {
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -71,7 +71,7 @@ func (m *Module) SignInGETHandler(c *gin.Context) {
if !ok {
m.clearSession(s)
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -87,7 +87,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
form := &login{}
if err := c.ShouldBind(form); err != nil {
m.clearSession(s)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
return
}
@@ -95,17 +95,17 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
if errWithCode != nil {
// don't clear session here, so the user can just press back and try again
// if they accidentally gave the wrong password or something
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
s.Set(sessionUserID, userid)
if err := s.Save(); err != nil {
err := fmt.Errorf("error saving user id onto session: %s", err)
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
}
- c.Redirect(http.StatusFound, OauthAuthorizePath)
+ c.Redirect(http.StatusFound, "/oauth"+OauthAuthorizePath)
}
// ValidatePassword takes an email address and a password.
diff --git a/internal/api/client/auth/token.go b/internal/api/auth/token.go
index fbbd08404..17c4d8d8b 100644
--- a/internal/api/client/auth/token.go
+++ b/internal/api/auth/token.go
@@ -22,7 +22,7 @@ import (
"net/http"
"net/url"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -41,8 +41,8 @@ type tokenRequestForm struct {
// TokenPOSTHandler should be served as a POST at https://example.org/oauth/token
// The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs.
func (m *Module) TokenPOSTHandler(c *gin.Context) {
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -50,7 +50,7 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
form := &tokenRequestForm{}
if err := c.ShouldBind(form); err != nil {
- api.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), err.Error()))
+ apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), err.Error()))
return
}
@@ -99,13 +99,13 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
}
if len(help) != 0 {
- api.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...))
+ apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...))
return
}
token, errWithCode := m.processor.OAuthHandleTokenRequest(c.Request)
if errWithCode != nil {
- api.OAuthErrorHandler(c, errWithCode)
+ apiutil.OAuthErrorHandler(c, errWithCode)
return
}
diff --git a/internal/api/client/auth/token_test.go b/internal/api/auth/token_test.go
index 50bbd6918..50bbd6918 100644
--- a/internal/api/client/auth/token_test.go
+++ b/internal/api/auth/token_test.go
diff --git a/internal/api/client.go b/internal/api/client.go
new file mode 100644
index 000000000..7736a99f4
--- /dev/null
+++ b/internal/api/client.go
@@ -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),
+ }
+}
diff --git a/internal/api/client/account/account_test.go b/internal/api/client/accounts/account_test.go
index 90dbd6249..57d1e6c04 100644
--- a/internal/api/client/account/account_test.go
+++ b/internal/api/client/accounts/account_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account_test
+package accounts_test
import (
"bytes"
@@ -26,7 +26,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -62,7 +62,7 @@ type AccountStandardTestSuite struct {
testStatuses map[string]*gtsmodel.Status
// module being tested
- accountModule *account.Module
+ accountsModule *accounts.Module
}
func (suite *AccountStandardTestSuite) SetupSuite() {
@@ -89,7 +89,7 @@ func (suite *AccountStandardTestSuite) SetupTest() {
suite.sentEmails = make(map[string]string)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.accountModule = account.New(suite.processor).(*account.Module)
+ suite.accountsModule = accounts.New(suite.processor)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/client/account/accountcreate.go b/internal/api/client/accounts/accountcreate.go
index e7b6c642d..041ca7fc4 100644
--- a/internal/api/client/account/accountcreate.go
+++ b/internal/api/client/accounts/accountcreate.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
@@ -24,8 +24,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -73,23 +73,23 @@ import (
func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, false, false)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.AccountCreateRequest{}
+ form := &apimodel.AccountCreateRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if err := validateCreateAccount(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -97,14 +97,14 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
signUpIP := net.ParseIP(clientIP)
if signUpIP == nil {
err := errors.New("ip address could not be parsed from request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
form.IP = signUpIP
ti, errWithCode := m.processor.AccountCreate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -113,7 +113,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
// validateCreateAccount checks through all the necessary prerequisites for creating a new account,
// according to the provided account create request. If the account isn't eligible, an error will be returned.
-func validateCreateAccount(form *model.AccountCreateRequest) error {
+func validateCreateAccount(form *apimodel.AccountCreateRequest) error {
if form == nil {
return errors.New("form was nil")
}
diff --git a/internal/api/client/account/accountcreate_test.go b/internal/api/client/accounts/accountcreate_test.go
index a4fc165bf..b2b8c715f 100644
--- a/internal/api/client/account/accountcreate_test.go
+++ b/internal/api/client/accounts/accountcreate_test.go
@@ -16,4 +16,4 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// */
-package account_test
+package accounts_test
diff --git a/internal/api/client/account/accountdelete.go b/internal/api/client/accounts/accountdelete.go
index 53bdedd0f..f1b95e95a 100644
--- a/internal/api/client/account/accountdelete.go
+++ b/internal/api/client/accounts/accountdelete.go
@@ -16,15 +16,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -68,26 +68,26 @@ import (
func (m *Module) AccountDeletePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.AccountDeleteRequest{}
+ form := &apimodel.AccountDeleteRequest{}
if err := c.ShouldBind(&form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if form.Password == "" {
err = errors.New("no password provided in account delete request")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
form.DeleteOriginID = authed.Account.ID
if errWithCode := m.processor.AccountDeleteLocal(c.Request.Context(), authed, form); errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/accountdelete_test.go b/internal/api/client/accounts/accountdelete_test.go
index 78348eabc..31559d59a 100644
--- a/internal/api/client/account/accountdelete_test.go
+++ b/internal/api/client/accounts/accountdelete_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account_test
+package accounts_test
import (
"net/http"
@@ -24,7 +24,7 @@ import (
"testing"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -45,10 +45,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandler() {
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, account.DeleteAccountPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountDeletePOSTHandler(ctx)
+ suite.accountsModule.AccountDeletePOSTHandler(ctx)
// 1. we should have Accepted because our request was valid
suite.Equal(http.StatusAccepted, recorder.Code)
@@ -67,10 +67,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerWrongPassword()
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, account.DeleteAccountPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountDeletePOSTHandler(ctx)
+ suite.accountsModule.AccountDeletePOSTHandler(ctx)
// 1. we should have Forbidden because we supplied the wrong password
suite.Equal(http.StatusForbidden, recorder.Code)
@@ -87,10 +87,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerNoPassword() {
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, account.DeleteAccountPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountDeletePOSTHandler(ctx)
+ suite.accountsModule.AccountDeletePOSTHandler(ctx)
// 1. we should have StatusBadRequest because our request was invalid
suite.Equal(http.StatusBadRequest, recorder.Code)
diff --git a/internal/api/client/account/accountget.go b/internal/api/client/accounts/accountget.go
index c9aae5b2b..1a6354490 100644
--- a/internal/api/client/account/accountget.go
+++ b/internal/api/client/accounts/accountget.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -69,25 +69,25 @@ import (
func (m *Module) AccountGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
acctInfo, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, targetAcctID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/account.go b/internal/api/client/accounts/accounts.go
index 4205baa2c..54c6c5f22 100644
--- a/internal/api/client/account/account.go
+++ b/internal/api/client/accounts/accounts.go
@@ -16,17 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"net/http"
- "strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/processing"
-
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
@@ -49,8 +45,8 @@ const (
// IDKey is the key to use for retrieving account ID in requests
IDKey = "id"
- // BasePath is the base API path for this module
- BasePath = "/api/v1/accounts"
+ // BasePath is the base API path for this module, excluding the 'api' prefix
+ BasePath = "/v1/accounts"
// BasePathWithID is the base path for this module with the ID key
BasePathWithID = BasePath + "/:" + IDKey
// VerifyPath is for verifying account credentials
@@ -77,65 +73,47 @@ const (
DeleteAccountPath = BasePath + "/delete"
)
-// Module implements the ClientAPIModule interface for account-related actions
type Module struct {
processor processing.Processor
}
-// New returns a new account module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
// create account
- r.AttachHandler(http.MethodPost, BasePath, m.AccountCreatePOSTHandler)
+ attachHandler(http.MethodPost, BasePath, m.AccountCreatePOSTHandler)
+
+ // get account
+ attachHandler(http.MethodGet, BasePathWithID, m.AccountGETHandler)
// delete account
- r.AttachHandler(http.MethodPost, DeleteAccountPath, m.AccountDeletePOSTHandler)
+ attachHandler(http.MethodPost, DeleteAccountPath, m.AccountDeletePOSTHandler)
- // get account
- r.AttachHandler(http.MethodGet, BasePathWithID, m.muxHandler)
+ // verify account
+ attachHandler(http.MethodGet, VerifyPath, m.AccountVerifyGETHandler)
// modify account
- r.AttachHandler(http.MethodPatch, BasePathWithID, m.muxHandler)
+ attachHandler(http.MethodPatch, UpdateCredentialsPath, m.AccountUpdateCredentialsPATCHHandler)
// get account's statuses
- r.AttachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler)
+ attachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler)
// get following or followers
- r.AttachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler)
- r.AttachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler)
+ attachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler)
+ attachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler)
// get relationship with account
- r.AttachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler)
+ attachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler)
// follow or unfollow account
- r.AttachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler)
- r.AttachHandler(http.MethodPost, UnfollowPath, m.AccountUnfollowPOSTHandler)
+ attachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler)
+ attachHandler(http.MethodPost, UnfollowPath, m.AccountUnfollowPOSTHandler)
// block or unblock account
- r.AttachHandler(http.MethodPost, BlockPath, m.AccountBlockPOSTHandler)
- r.AttachHandler(http.MethodPost, UnblockPath, m.AccountUnblockPOSTHandler)
-
- return nil
-}
-
-func (m *Module) muxHandler(c *gin.Context) {
- ru := c.Request.RequestURI
- switch c.Request.Method {
- case http.MethodGet:
- if strings.HasPrefix(ru, VerifyPath) {
- m.AccountVerifyGETHandler(c)
- } else {
- m.AccountGETHandler(c)
- }
- case http.MethodPatch:
- if strings.HasPrefix(ru, UpdateCredentialsPath) {
- m.AccountUpdateCredentialsPATCHHandler(c)
- }
- }
+ attachHandler(http.MethodPost, BlockPath, m.AccountBlockPOSTHandler)
+ attachHandler(http.MethodPost, UnblockPath, m.AccountUnblockPOSTHandler)
}
diff --git a/internal/api/client/account/accountupdate.go b/internal/api/client/accounts/accountupdate.go
index f89259a96..5dbf0ce46 100644
--- a/internal/api/client/account/accountupdate.go
+++ b/internal/api/client/accounts/accountupdate.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
@@ -25,8 +25,8 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -138,33 +138,33 @@ import (
func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
form, err := parseUpdateAccountForm(c)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
acctSensitive, errWithCode := m.processor.AccountUpdate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, acctSensitive)
}
-func parseUpdateAccountForm(c *gin.Context) (*model.UpdateCredentialsRequest, error) {
- form := &model.UpdateCredentialsRequest{
- Source: &model.UpdateSource{},
+func parseUpdateAccountForm(c *gin.Context) (*apimodel.UpdateCredentialsRequest, error) {
+ form := &apimodel.UpdateCredentialsRequest{
+ Source: &apimodel.UpdateSource{},
}
if err := c.ShouldBind(&form); err != nil {
diff --git a/internal/api/client/account/accountupdate_test.go b/internal/api/client/accounts/accountupdate_test.go
index 259bb69e9..45a287ec8 100644
--- a/internal/api/client/account/accountupdate_test.go
+++ b/internal/api/client/accounts/accountupdate_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account_test
+package accounts_test
import (
"context"
@@ -27,7 +27,7 @@ import (
"testing"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -50,10 +50,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler()
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
@@ -89,10 +89,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUnl
}
bodyBytes1 := requestBody1.Bytes()
recorder1 := httptest.NewRecorder()
- ctx1 := suite.newContext(recorder1, http.MethodPatch, bodyBytes1, account.UpdateCredentialsPath, w1.FormDataContentType())
+ ctx1 := suite.newContext(recorder1, http.MethodPatch, bodyBytes1, accounts.UpdateCredentialsPath, w1.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx1)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx1)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder1.Code)
@@ -125,10 +125,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUnl
}
bodyBytes2 := requestBody2.Bytes()
recorder2 := httptest.NewRecorder()
- ctx2 := suite.newContext(recorder2, http.MethodPatch, bodyBytes2, account.UpdateCredentialsPath, w2.FormDataContentType())
+ ctx2 := suite.newContext(recorder2, http.MethodPatch, bodyBytes2, accounts.UpdateCredentialsPath, w2.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx2)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx2)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder1.Code)
@@ -170,10 +170,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGet
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
@@ -212,10 +212,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwo
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
@@ -266,10 +266,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerWit
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
@@ -308,10 +308,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerEmp
// set up the request
bodyBytes := []byte{}
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, "")
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, "")
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusBadRequest, recorder.Code)
@@ -343,10 +343,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
@@ -385,10 +385,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
@@ -430,10 +430,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
}
bodyBytes := requestBody.Bytes()
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
+ ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
// call the handler
- suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
+ suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
suite.Equal(http.StatusBadRequest, recorder.Code)
diff --git a/internal/api/client/account/accountverify.go b/internal/api/client/accounts/accountverify.go
index 916d0a322..2b39d5ab2 100644
--- a/internal/api/client/account/accountverify.go
+++ b/internal/api/client/accounts/accountverify.go
@@ -16,13 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -59,18 +59,18 @@ import (
func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
acctSensitive, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/accountverify_test.go b/internal/api/client/accounts/accountverify_test.go
index 886272865..e74c30aba 100644
--- a/internal/api/client/account/accountverify_test.go
+++ b/internal/api/client/accounts/accountverify_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account_test
+package accounts_test
import (
"encoding/json"
@@ -28,7 +28,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
@@ -42,10 +42,10 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
// set up the request
recorder := httptest.NewRecorder()
- ctx := suite.newContext(recorder, http.MethodGet, nil, account.VerifyPath, "")
+ ctx := suite.newContext(recorder, http.MethodGet, nil, accounts.VerifyPath, "")
// call the handler
- suite.accountModule.AccountVerifyGETHandler(ctx)
+ suite.accountsModule.AccountVerifyGETHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
diff --git a/internal/api/client/account/block.go b/internal/api/client/accounts/block.go
index 9840c96ab..9e14ecb6e 100644
--- a/internal/api/client/account/block.go
+++ b/internal/api/client/accounts/block.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -69,25 +69,25 @@ import (
func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
relationship, errWithCode := m.processor.AccountBlockCreate(c.Request.Context(), authed, targetAcctID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/block_test.go b/internal/api/client/accounts/block_test.go
index 9c75330aa..474a53eb8 100644
--- a/internal/api/client/account/block_test.go
+++ b/internal/api/client/accounts/block_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account_test
+package accounts_test
import (
"fmt"
@@ -29,7 +29,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -46,16 +46,16 @@ func (suite *BlockTestSuite) TestBlockSelf() {
ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(account.BlockPath, ":id", testAcct.ID, 1)), nil)
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(accounts.BlockPath, ":id", testAcct.ID, 1)), nil)
ctx.Params = gin.Params{
gin.Param{
- Key: account.IDKey,
+ Key: accounts.IDKey,
Value: testAcct.ID,
},
}
- suite.accountModule.AccountBlockPOSTHandler(ctx)
+ suite.accountsModule.AccountBlockPOSTHandler(ctx)
// 1. status should be Not Acceptable due to attempted self-block
suite.Equal(http.StatusNotAcceptable, recorder.Code)
diff --git a/internal/api/client/account/follow.go b/internal/api/client/accounts/follow.go
index cc523a7f8..d2a8af886 100644
--- a/internal/api/client/account/follow.go
+++ b/internal/api/client/accounts/follow.go
@@ -16,15 +16,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -91,32 +91,32 @@ import (
func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.AccountFollowRequest{}
+ form := &apimodel.AccountFollowRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
form.ID = targetAcctID
relationship, errWithCode := m.processor.AccountFollowCreate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/follow_test.go b/internal/api/client/accounts/follow_test.go
index fad67b185..fd15c3734 100644
--- a/internal/api/client/account/follow_test.go
+++ b/internal/api/client/accounts/follow_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account_test
+package accounts_test
import (
"fmt"
@@ -29,7 +29,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -46,17 +46,17 @@ func (suite *FollowTestSuite) TestFollowSelf() {
ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(account.FollowPath, ":id", testAcct.ID, 1)), nil)
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(accounts.FollowPath, ":id", testAcct.ID, 1)), nil)
ctx.Params = gin.Params{
gin.Param{
- Key: account.IDKey,
+ Key: accounts.IDKey,
Value: testAcct.ID,
},
}
// call the handler
- suite.accountModule.AccountFollowPOSTHandler(ctx)
+ suite.accountsModule.AccountFollowPOSTHandler(ctx)
// 1. status should be Not Acceptable due to self-follow attempt
suite.Equal(http.StatusNotAcceptable, recorder.Code)
diff --git a/internal/api/client/account/followers.go b/internal/api/client/accounts/followers.go
index cb2f4bfa6..b464a5ad6 100644
--- a/internal/api/client/account/followers.go
+++ b/internal/api/client/accounts/followers.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,25 +72,25 @@ import (
func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
followers, errWithCode := m.processor.AccountFollowersGet(c.Request.Context(), authed, targetAcctID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/following.go b/internal/api/client/accounts/following.go
index 3d69739c3..4589ad07a 100644
--- a/internal/api/client/account/following.go
+++ b/internal/api/client/accounts/following.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,25 +72,25 @@ import (
func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
following, errWithCode := m.processor.AccountFollowingGet(c.Request.Context(), authed, targetAcctID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/relationships.go b/internal/api/client/accounts/relationships.go
index 56159d48e..60e7b517c 100644
--- a/internal/api/client/account/relationships.go
+++ b/internal/api/client/accounts/relationships.go
@@ -1,12 +1,12 @@
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -57,12 +57,12 @@ import (
func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -72,18 +72,18 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
id := c.Query("id")
if id == "" {
err = errors.New("no account id(s) specified in query")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
targetAccountIDs = append(targetAccountIDs, id)
}
- relationships := []model.Relationship{}
+ relationships := []apimodel.Relationship{}
for _, targetAccountID := range targetAccountIDs {
r, errWithCode := m.processor.AccountRelationshipGet(c.Request.Context(), authed, targetAccountID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
relationships = append(relationships, *r)
diff --git a/internal/api/client/account/statuses.go b/internal/api/client/accounts/statuses.go
index 7ecf3ba9f..a04517feb 100644
--- a/internal/api/client/account/statuses.go
+++ b/internal/api/client/accounts/statuses.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
@@ -25,7 +25,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -133,19 +133,19 @@ import (
func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, false, false, false, false)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -155,7 +155,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -167,7 +167,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(excludeRepliesString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", ExcludeRepliesKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
excludeReplies = i
@@ -179,7 +179,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(excludeReblogsString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", ExcludeReblogsKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
excludeReblogs = i
@@ -203,7 +203,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(pinnedString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", PinnedKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
pinnedOnly = i
@@ -215,7 +215,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(mediaOnlyString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", OnlyMediaKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
mediaOnly = i
@@ -227,7 +227,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(publicOnlyString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", OnlyPublicKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
publicOnly = i
@@ -235,7 +235,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.AccountStatusesGet(c.Request.Context(), authed, targetAcctID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/statuses_test.go b/internal/api/client/accounts/statuses_test.go
index 1f935896c..92ca9d925 100644
--- a/internal/api/client/account/statuses_test.go
+++ b/internal/api/client/accounts/statuses_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account_test
+package accounts_test
import (
"encoding/json"
@@ -29,7 +29,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/account"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
)
@@ -45,13 +45,13 @@ func (suite *AccountStatusesTestSuite) TestGetStatusesPublicOnly() {
ctx := suite.newContext(recorder, http.MethodGet, nil, fmt.Sprintf("/api/v1/accounts/%s/statuses?limit=20&only_media=false&only_public=true", targetAccount.ID), "")
ctx.Params = gin.Params{
gin.Param{
- Key: account.IDKey,
+ Key: accounts.IDKey,
Value: targetAccount.ID,
},
}
// call the handler
- suite.accountModule.AccountStatusesGETHandler(ctx)
+ suite.accountsModule.AccountStatusesGETHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
@@ -85,13 +85,13 @@ func (suite *AccountStatusesTestSuite) TestGetStatusesPublicOnlyMediaOnly() {
ctx := suite.newContext(recorder, http.MethodGet, nil, fmt.Sprintf("/api/v1/accounts/%s/statuses?limit=20&only_media=true&only_public=true", targetAccount.ID), "")
ctx.Params = gin.Params{
gin.Param{
- Key: account.IDKey,
+ Key: accounts.IDKey,
Value: targetAccount.ID,
},
}
// call the handler
- suite.accountModule.AccountStatusesGETHandler(ctx)
+ suite.accountsModule.AccountStatusesGETHandler(ctx)
// 1. we should have OK because our request was valid
suite.Equal(http.StatusOK, recorder.Code)
diff --git a/internal/api/client/account/unblock.go b/internal/api/client/accounts/unblock.go
index 451b7fd27..e0a0a978e 100644
--- a/internal/api/client/account/unblock.go
+++ b/internal/api/client/accounts/unblock.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -70,25 +70,25 @@ import (
func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
relationship, errWithCode := m.processor.AccountBlockRemove(c.Request.Context(), authed, targetAcctID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/account/unfollow.go b/internal/api/client/accounts/unfollow.go
index fafba99fd..95c819903 100644
--- a/internal/api/client/account/unfollow.go
+++ b/internal/api/client/accounts/unfollow.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package account
+package accounts
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -70,25 +70,25 @@ import (
func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
relationship, errWithCode := m.processor.AccountFollowRemove(c.Request.Context(), authed, targetAcctID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/accountaction.go b/internal/api/client/admin/accountaction.go
index 2dc84a2d0..d40404b15 100644
--- a/internal/api/client/admin/accountaction.go
+++ b/internal/api/client/admin/accountaction.go
@@ -24,8 +24,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -85,38 +85,38 @@ import (
func (m *Module) AccountActionPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.AdminAccountActionRequest{}
+ form := &apimodel.AdminAccountActionRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if form.Type == "" {
err := errors.New("no type specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
form.TargetAccountID = targetAcctID
if errWithCode := m.processor.AdminAccountAction(c.Request.Context(), authed, form); errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/admin.go b/internal/api/client/admin/admin.go
index 569354c96..b4fb4d6d1 100644
--- a/internal/api/client/admin/admin.go
+++ b/internal/api/client/admin/admin.go
@@ -21,14 +21,13 @@ package admin
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base API path for this module.
- BasePath = "/api/v1/admin"
+ // BasePath is the base API path for this module, excluding the api prefix
+ BasePath = "/v1/admin"
// EmojiPath is used for posting/deleting custom emojis.
EmojiPath = BasePath + "/custom_emojis"
// EmojiPathWithID is used for interacting with a single emoji.
@@ -68,32 +67,28 @@ const (
DomainQueryKey = "domain"
)
-// Module implements the ClientAPIModule interface for admin-related actions (reports, emojis, etc)
type Module struct {
processor processing.Processor
}
-// New returns a new admin module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodPost, EmojiPath, m.EmojiCreatePOSTHandler)
- r.AttachHandler(http.MethodGet, EmojiPath, m.EmojisGETHandler)
- r.AttachHandler(http.MethodDelete, EmojiPathWithID, m.EmojiDELETEHandler)
- r.AttachHandler(http.MethodGet, EmojiPathWithID, m.EmojiGETHandler)
- r.AttachHandler(http.MethodPatch, EmojiPathWithID, m.EmojiPATCHHandler)
- r.AttachHandler(http.MethodPost, DomainBlocksPath, m.DomainBlocksPOSTHandler)
- r.AttachHandler(http.MethodGet, DomainBlocksPath, m.DomainBlocksGETHandler)
- r.AttachHandler(http.MethodGet, DomainBlocksPathWithID, m.DomainBlockGETHandler)
- r.AttachHandler(http.MethodDelete, DomainBlocksPathWithID, m.DomainBlockDELETEHandler)
- r.AttachHandler(http.MethodPost, AccountsActionPath, m.AccountActionPOSTHandler)
- r.AttachHandler(http.MethodPost, MediaCleanupPath, m.MediaCleanupPOSTHandler)
- r.AttachHandler(http.MethodPost, MediaRefetchPath, m.MediaRefetchPOSTHandler)
- r.AttachHandler(http.MethodGet, EmojiCategoriesPath, m.EmojiCategoriesGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodPost, EmojiPath, m.EmojiCreatePOSTHandler)
+ attachHandler(http.MethodGet, EmojiPath, m.EmojisGETHandler)
+ attachHandler(http.MethodDelete, EmojiPathWithID, m.EmojiDELETEHandler)
+ attachHandler(http.MethodGet, EmojiPathWithID, m.EmojiGETHandler)
+ attachHandler(http.MethodPatch, EmojiPathWithID, m.EmojiPATCHHandler)
+ attachHandler(http.MethodPost, DomainBlocksPath, m.DomainBlocksPOSTHandler)
+ attachHandler(http.MethodGet, DomainBlocksPath, m.DomainBlocksGETHandler)
+ attachHandler(http.MethodGet, DomainBlocksPathWithID, m.DomainBlockGETHandler)
+ attachHandler(http.MethodDelete, DomainBlocksPathWithID, m.DomainBlockDELETEHandler)
+ attachHandler(http.MethodPost, AccountsActionPath, m.AccountActionPOSTHandler)
+ attachHandler(http.MethodPost, MediaCleanupPath, m.MediaCleanupPOSTHandler)
+ attachHandler(http.MethodPost, MediaRefetchPath, m.MediaRefetchPOSTHandler)
+ attachHandler(http.MethodGet, EmojiCategoriesPath, m.EmojiCategoriesGETHandler)
}
diff --git a/internal/api/client/admin/admin_test.go b/internal/api/client/admin/admin_test.go
index 52c2630d9..ac3bbcb98 100644
--- a/internal/api/client/admin/admin_test.go
+++ b/internal/api/client/admin/admin_test.go
@@ -93,7 +93,7 @@ func (suite *AdminStandardTestSuite) SetupTest() {
suite.sentEmails = make(map[string]string)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.adminModule = admin.New(suite.processor).(*admin.Module)
+ suite.adminModule = admin.New(suite.processor)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}
diff --git a/internal/api/client/admin/domainblockcreate.go b/internal/api/client/admin/domainblockcreate.go
index 034ea8682..44410abe3 100644
--- a/internal/api/client/admin/domainblockcreate.go
+++ b/internal/api/client/admin/domainblockcreate.go
@@ -25,8 +25,8 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -126,18 +126,18 @@ import (
func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -147,21 +147,21 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
i, err := strconv.ParseBool(importString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", ImportQueryKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
imp = i
}
- form := &model.DomainBlockCreateRequest{}
+ form := &apimodel.DomainBlockCreateRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if err := validateCreateDomainBlock(form, imp); err != nil {
err := fmt.Errorf("error validating form: %s", err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -169,7 +169,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
// we're importing multiple blocks
domainBlocks, errWithCode := m.processor.AdminDomainBlocksImport(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, domainBlocks)
@@ -179,13 +179,13 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
// we're just creating one block
domainBlock, errWithCode := m.processor.AdminDomainBlockCreate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, domainBlock)
}
-func validateCreateDomainBlock(form *model.DomainBlockCreateRequest, imp bool) error {
+func validateCreateDomainBlock(form *apimodel.DomainBlockCreateRequest, imp bool) error {
if imp {
if form.Domains.Size == 0 {
return errors.New("import was specified but list of domains is empty")
diff --git a/internal/api/client/admin/domainblockdelete.go b/internal/api/client/admin/domainblockdelete.go
index 6f3684418..ddb07e6f6 100644
--- a/internal/api/client/admin/domainblockdelete.go
+++ b/internal/api/client/admin/domainblockdelete.go
@@ -24,7 +24,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,31 +72,31 @@ import (
func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
domainBlockID := c.Param(IDKey)
if domainBlockID == "" {
err := errors.New("no domain block id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
domainBlock, errWithCode := m.processor.AdminDomainBlockDelete(c.Request.Context(), authed, domainBlockID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/domainblockget.go b/internal/api/client/admin/domainblockget.go
index 3d27b585e..b9d365caa 100644
--- a/internal/api/client/admin/domainblockget.go
+++ b/internal/api/client/admin/domainblockget.go
@@ -25,7 +25,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -73,25 +73,25 @@ import (
func (m *Module) DomainBlockGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
domainBlockID := c.Param(IDKey)
if domainBlockID == "" {
err := errors.New("no domain block id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -101,7 +101,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(exportString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", ExportQueryKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
export = i
@@ -109,7 +109,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
domainBlock, errWithCode := m.processor.AdminDomainBlockGet(c.Request.Context(), authed, domainBlockID, export)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/domainblocksget.go b/internal/api/client/admin/domainblocksget.go
index a4ab4ac1c..fea0ca35e 100644
--- a/internal/api/client/admin/domainblocksget.go
+++ b/internal/api/client/admin/domainblocksget.go
@@ -24,7 +24,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -78,18 +78,18 @@ import (
func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -99,7 +99,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(exportString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", ExportQueryKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
export = i
@@ -107,7 +107,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
domainBlocks, errWithCode := m.processor.AdminDomainBlocksGet(c.Request.Context(), authed, export)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/emojicategoriesget.go b/internal/api/client/admin/emojicategoriesget.go
index d8b379674..e69506413 100644
--- a/internal/api/client/admin/emojicategoriesget.go
+++ b/internal/api/client/admin/emojicategoriesget.go
@@ -23,7 +23,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -69,24 +69,24 @@ import (
func (m *Module) EmojiCategoriesGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
categories, errWithCode := m.processor.AdminEmojiCategoriesGet(c.Request.Context())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/emojicreate.go b/internal/api/client/admin/emojicreate.go
index 2a075708f..8368a12b0 100644
--- a/internal/api/client/admin/emojicreate.go
+++ b/internal/api/client/admin/emojicreate.go
@@ -24,8 +24,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -100,42 +100,42 @@ import (
func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.EmojiCreateRequest{}
+ form := &apimodel.EmojiCreateRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if err := validateCreateEmoji(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
apiEmoji, errWithCode := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, apiEmoji)
}
-func validateCreateEmoji(form *model.EmojiCreateRequest) error {
+func validateCreateEmoji(form *apimodel.EmojiCreateRequest) error {
if form.Image == nil || form.Image.Size == 0 {
return errors.New("no emoji given")
}
diff --git a/internal/api/client/admin/emojidelete.go b/internal/api/client/admin/emojidelete.go
index 14f3c70ff..b66116b6d 100644
--- a/internal/api/client/admin/emojidelete.go
+++ b/internal/api/client/admin/emojidelete.go
@@ -24,7 +24,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -78,31 +78,31 @@ import (
func (m *Module) EmojiDELETEHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
emojiID := c.Param(IDKey)
if emojiID == "" {
err := errors.New("no emoji id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
emoji, errWithCode := m.processor.AdminEmojiDelete(c.Request.Context(), authed, emojiID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/emojiget.go b/internal/api/client/admin/emojiget.go
index 60f7d5948..49d586756 100644
--- a/internal/api/client/admin/emojiget.go
+++ b/internal/api/client/admin/emojiget.go
@@ -24,7 +24,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -68,31 +68,31 @@ import (
func (m *Module) EmojiGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
emojiID := c.Param(IDKey)
if emojiID == "" {
err := errors.New("no emoji id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
emoji, errWithCode := m.processor.AdminEmojiGet(c.Request.Context(), authed, emojiID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/emojisget.go b/internal/api/client/admin/emojisget.go
index 0b7cfe059..e8b3c0e49 100644
--- a/internal/api/client/admin/emojisget.go
+++ b/internal/api/client/admin/emojisget.go
@@ -25,7 +25,7 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
@@ -125,18 +125,18 @@ import (
func (m *Module) EmojisGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -149,7 +149,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -177,7 +177,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
shortcode = strings.Trim(filter[10:], ":") // remove any errant ":"
default:
err := fmt.Errorf("filter %s not recognized; accepted values are 'domain:[domain]', 'disabled', 'enabled', 'shortcode:[shortcode]'", filter)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
}
@@ -200,7 +200,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.AdminEmojisGet(c.Request.Context(), authed, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/emojiupdate.go b/internal/api/client/admin/emojiupdate.go
index 695c6bcde..8402b30e9 100644
--- a/internal/api/client/admin/emojiupdate.go
+++ b/internal/api/client/admin/emojiupdate.go
@@ -25,8 +25,8 @@ import (
"strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -123,42 +123,42 @@ import (
func (m *Module) EmojiPATCHHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
emojiID := c.Param(IDKey)
if emojiID == "" {
err := errors.New("no emoji id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.EmojiUpdateRequest{}
+ form := &apimodel.EmojiUpdateRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if err := validateUpdateEmoji(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
emoji, errWithCode := m.processor.AdminEmojiUpdate(c.Request.Context(), emojiID, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -166,14 +166,14 @@ func (m *Module) EmojiPATCHHandler(c *gin.Context) {
}
// do a first pass on the form here
-func validateUpdateEmoji(form *model.EmojiUpdateRequest) error {
+func validateUpdateEmoji(form *apimodel.EmojiUpdateRequest) error {
// check + normalize update type so we don't need
// to do this trimming + lowercasing again later
switch strings.TrimSpace(strings.ToLower(string(form.Type))) {
- case string(model.EmojiUpdateDisable):
+ case string(apimodel.EmojiUpdateDisable):
// no params required for this one, so don't bother checking
- form.Type = model.EmojiUpdateDisable
- case string(model.EmojiUpdateCopy):
+ form.Type = apimodel.EmojiUpdateDisable
+ case string(apimodel.EmojiUpdateCopy):
// need at least a valid shortcode when doing a copy
if form.Shortcode == nil {
return errors.New("emoji action type was 'copy' but no shortcode was provided")
@@ -190,8 +190,8 @@ func validateUpdateEmoji(form *model.EmojiUpdateRequest) error {
}
}
- form.Type = model.EmojiUpdateCopy
- case string(model.EmojiUpdateModify):
+ form.Type = apimodel.EmojiUpdateCopy
+ case string(apimodel.EmojiUpdateModify):
// need either image or category name for modify
hasImage := form.Image != nil && form.Image.Size != 0
hasCategoryName := form.CategoryName != nil
@@ -212,7 +212,7 @@ func validateUpdateEmoji(form *model.EmojiUpdateRequest) error {
}
}
- form.Type = model.EmojiUpdateModify
+ form.Type = apimodel.EmojiUpdateModify
default:
return errors.New("emoji action type must be one of 'disable', 'copy', 'modify'")
}
diff --git a/internal/api/client/admin/mediacleanup.go b/internal/api/client/admin/mediacleanup.go
index 157f35ab0..7f3fc11d5 100644
--- a/internal/api/client/admin/mediacleanup.go
+++ b/internal/api/client/admin/mediacleanup.go
@@ -23,8 +23,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -71,19 +71,19 @@ import (
func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.MediaCleanupRequest{}
+ form := &apimodel.MediaCleanupRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -98,7 +98,7 @@ func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
}
if errWithCode := m.processor.AdminMediaPrune(c.Request.Context(), remoteCacheDays); errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/admin/mediarefetch.go b/internal/api/client/admin/mediarefetch.go
index 9c8a30c1b..5618843e5 100644
--- a/internal/api/client/admin/mediarefetch.go
+++ b/internal/api/client/admin/mediarefetch.go
@@ -23,7 +23,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -74,18 +74,18 @@ import (
func (m *Module) MediaRefetchPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := fmt.Errorf("user %s not an admin", authed.User.ID)
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
if errWithCode := m.processor.AdminMediaRefetch(c.Request.Context(), authed, c.Query(DomainQueryKey)); errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/app/app_test.go b/internal/api/client/app/app_test.go
deleted file mode 100644
index 5c1981ba1..000000000
--- a/internal/api/client/app/app_test.go
+++ /dev/null
@@ -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
diff --git a/internal/api/client/app/appcreate.go b/internal/api/client/apps/appcreate.go
index 6060c9480..f381e9954 100644
--- a/internal/api/client/app/appcreate.go
+++ b/internal/api/client/apps/appcreate.go
@@ -16,15 +16,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package app
+package apps
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -77,48 +77,48 @@ const (
func (m *Module) AppsPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, false, false, false, false)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.ApplicationCreateRequest{}
+ form := &apimodel.ApplicationCreateRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if len([]rune(form.ClientName)) > formFieldLen {
err := fmt.Errorf("client_name must be less than %d characters", formFieldLen)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if len([]rune(form.RedirectURIs)) > formRedirectLen {
err := fmt.Errorf("redirect_uris must be less than %d characters", formRedirectLen)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if len([]rune(form.Scopes)) > formFieldLen {
err := fmt.Errorf("scopes must be less than %d characters", formFieldLen)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if len([]rune(form.Website)) > formFieldLen {
err := fmt.Errorf("website must be less than %d characters", formFieldLen)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
apiApp, errWithCode := m.processor.AppCreate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/security/useragentblock.go b/internal/api/client/apps/apps.go
index b117e8608..264a76f6f 100644
--- a/internal/api/security/useragentblock.go
+++ b/internal/api/client/apps/apps.go
@@ -16,20 +16,28 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package security
+package apps
import (
- "errors"
"net/http"
"github.com/gin-gonic/gin"
+ "github.com/superseriousbusiness/gotosocial/internal/processing"
)
-// UserAgentBlock aborts requests with empty user agent strings.
-func (m *Module) UserAgentBlock(c *gin.Context) {
- if ua := c.Request.UserAgent(); ua == "" {
- code := http.StatusTeapot
- err := errors.New(http.StatusText(code) + ": no user-agent sent with request")
- c.AbortWithStatusJSON(code, gin.H{"error": err.Error()})
+// BasePath is the base path for this api module, excluding the api prefix
+const BasePath = "/v1/apps"
+
+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.MethodPost, BasePath, m.AppsPOSTHandler)
+}
diff --git a/internal/api/client/auth/auth.go b/internal/api/client/auth/auth.go
deleted file mode 100644
index 8a1d9d483..000000000
--- a/internal/api/client/auth/auth.go
+++ /dev/null
@@ -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
-}
diff --git a/internal/api/client/blocks/blocks.go b/internal/api/client/blocks/blocks.go
index 2211a8076..df2ee65bb 100644
--- a/internal/api/client/blocks/blocks.go
+++ b/internal/api/client/blocks/blocks.go
@@ -21,14 +21,13 @@ package blocks
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base URI path for serving favourites
- BasePath = "/api/v1/blocks"
+ // BasePath is the base URI path for serving blocks, minus the api prefix.
+ BasePath = "/v1/blocks"
// MaxIDKey is the url query for setting a max ID to return
MaxIDKey = "max_id"
@@ -38,20 +37,16 @@ const (
LimitKey = "limit"
)
-// Module implements the ClientAPIModule interface for everything relating to viewing blocks
type Module struct {
processor processing.Processor
}
-// New returns a new blocks module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.BlocksGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.BlocksGETHandler)
}
diff --git a/internal/api/client/blocks/blocksget.go b/internal/api/client/blocks/blocksget.go
index 98f5ce6ea..290ea6617 100644
--- a/internal/api/client/blocks/blocksget.go
+++ b/internal/api/client/blocks/blocksget.go
@@ -24,7 +24,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -96,12 +96,12 @@ import (
func (m *Module) BlocksGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -123,7 +123,7 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -131,7 +131,7 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.BlocksGet(c.Request.Context(), authed, maxID, sinceID, limit)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/bookmarks/bookmarks.go b/internal/api/client/bookmarks/bookmarks.go
index 492b7364c..d0273321c 100644
--- a/internal/api/client/bookmarks/bookmarks.go
+++ b/internal/api/client/bookmarks/bookmarks.go
@@ -21,9 +21,8 @@ package bookmarks
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
@@ -31,20 +30,16 @@ const (
BasePath = "/api/v1/bookmarks"
)
-// Module implements the ClientAPIModule interface for everything related to bookmarks
type Module struct {
processor processing.Processor
}
-// New returns a new emoji module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.BookmarksGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.BookmarksGETHandler)
}
diff --git a/internal/api/client/bookmarks/bookmarks_test.go b/internal/api/client/bookmarks/bookmarks_test.go
index b4a4bdfb1..3bd12aee1 100644
--- a/internal/api/client/bookmarks/bookmarks_test.go
+++ b/internal/api/client/bookmarks/bookmarks_test.go
@@ -29,7 +29,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -67,7 +67,7 @@ type BookmarkTestSuite struct {
testFollows map[string]*gtsmodel.Follow
// module being tested
- statusModule *status.Module
+ statusModule *statuses.Module
bookmarkModule *bookmarks.Module
}
@@ -99,8 +99,8 @@ func (suite *BookmarkTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.statusModule = status.New(suite.processor).(*status.Module)
- suite.bookmarkModule = bookmarks.New(suite.processor).(*bookmarks.Module)
+ suite.statusModule = statuses.New(suite.processor)
+ suite.bookmarkModule = bookmarks.New(suite.processor)
suite.NoError(suite.processor.Start())
}
@@ -123,7 +123,7 @@ func (suite *BookmarkTestSuite) TestGetBookmark() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.BookmarkPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.BookmarkPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
suite.bookmarkModule.BookmarksGETHandler(ctx)
diff --git a/internal/api/client/bookmarks/bookmarksget.go b/internal/api/client/bookmarks/bookmarksget.go
index dafc896ef..8f587f13d 100644
--- a/internal/api/client/bookmarks/bookmarksget.go
+++ b/internal/api/client/bookmarks/bookmarksget.go
@@ -6,7 +6,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -56,12 +56,12 @@ const (
func (m *Module) BookmarksGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -71,7 +71,7 @@ func (m *Module) BookmarksGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 64)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -91,12 +91,12 @@ func (m *Module) BookmarksGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.BookmarksGet(c.Request.Context(), authed, maxID, minID, limit)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/app/app.go b/internal/api/client/customemojis/customemojis.go
index 0bbeb6cc9..ab89415d0 100644
--- a/internal/api/client/app/app.go
+++ b/internal/api/client/customemojis/customemojis.go
@@ -16,33 +16,30 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package app
+package customemojis
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
-// BasePath is the base path for this api module
-const BasePath = "/api/v1/apps"
+const (
+ // BasePath is the base path for serving custom emojis, minus the 'api' prefix
+ BasePath = "/v1/custom_emojis"
+)
-// Module implements the ClientAPIModule interface for requests relating to registering/removing applications
type Module struct {
processor processing.Processor
}
-// New returns a new auth module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route satisfies the RESTAPIModule interface
-func (m *Module) Route(s router.Router) error {
- s.AttachHandler(http.MethodPost, BasePath, m.AppsPOSTHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.CustomEmojisGETHandler)
}
diff --git a/internal/api/client/customemojis/customemojisget.go b/internal/api/client/customemojis/customemojisget.go
new file mode 100644
index 000000000..3428071d0
--- /dev/null
+++ b/internal/api/client/customemojis/customemojisget.go
@@ -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)
+}
diff --git a/internal/api/client/emoji/emojisget.go b/internal/api/client/emoji/emojisget.go
deleted file mode 100644
index d41e5e7df..000000000
--- a/internal/api/client/emoji/emojisget.go
+++ /dev/null
@@ -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)
-}
diff --git a/internal/api/client/favourites/favourites.go b/internal/api/client/favourites/favourites.go
index f310d6873..5abc85a27 100644
--- a/internal/api/client/favourites/favourites.go
+++ b/internal/api/client/favourites/favourites.go
@@ -21,14 +21,13 @@ package favourites
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base URI path for serving favourites
- BasePath = "/api/v1/favourites"
+ // BasePath is the base URI path for serving favourites, minus the 'api' prefix
+ BasePath = "/v1/favourites"
// MaxIDKey is the url query for setting a max status ID to return
MaxIDKey = "max_id"
@@ -42,20 +41,16 @@ const (
LocalKey = "local"
)
-// Module implements the ClientAPIModule interface for everything relating to viewing favourites
type Module struct {
processor processing.Processor
}
-// New returns a new favourites module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.FavouritesGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.FavouritesGETHandler)
}
diff --git a/internal/api/client/favourites/favourites_test.go b/internal/api/client/favourites/favourites_test.go
index c84da6b32..050b72536 100644
--- a/internal/api/client/favourites/favourites_test.go
+++ b/internal/api/client/favourites/favourites_test.go
@@ -87,7 +87,7 @@ func (suite *FavouritesStandardTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.favModule = favourites.New(suite.processor).(*favourites.Module)
+ suite.favModule = favourites.New(suite.processor)
suite.NoError(suite.processor.Start())
}
diff --git a/internal/api/client/favourites/favouritesget.go b/internal/api/client/favourites/favouritesget.go
index 5ff032b9a..9b6bb715e 100644
--- a/internal/api/client/favourites/favouritesget.go
+++ b/internal/api/client/favourites/favouritesget.go
@@ -6,7 +6,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -78,12 +78,12 @@ import (
func (m *Module) FavouritesGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -105,7 +105,7 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -113,7 +113,7 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.FavedTimelineGet(c.Request.Context(), authed, maxID, minID, limit)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/filter/filter.go b/internal/api/client/filters/filter.go
index cf801e0a5..bdfd89ffe 100644
--- a/internal/api/client/filter/filter.go
+++ b/internal/api/client/filters/filter.go
@@ -21,30 +21,25 @@ package filter
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base path for serving the filter API
- BasePath = "/api/v1/filters"
+ // BasePath is the base path for serving the filters API, minus the 'api' prefix
+ BasePath = "/v1/filters"
)
-// Module implements the ClientAPIModule interface for every related to filters
type Module struct {
processor processing.Processor
}
-// New returns a new filter module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.FiltersGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.FiltersGETHandler)
}
diff --git a/internal/api/client/filter/filtersget.go b/internal/api/client/filters/filtersget.go
index 8e0a0bb34..71d6cac3e 100644
--- a/internal/api/client/filter/filtersget.go
+++ b/internal/api/client/filters/filtersget.go
@@ -4,7 +4,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -12,12 +12,12 @@ import (
// FiltersGETHandler returns a list of filters set by/for the authed account
func (m *Module) FiltersGETHandler(c *gin.Context) {
if _, err := oauth.Authed(c, true, true, true, true); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/followrequest/authorize.go b/internal/api/client/followrequests/authorize.go
index a5a392f76..d30bb979f 100644
--- a/internal/api/client/followrequest/authorize.go
+++ b/internal/api/client/followrequests/authorize.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest
+package followrequests
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,25 +72,25 @@ import (
func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
originAccountID := c.Param(IDKey)
if originAccountID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
relationship, errWithCode := m.processor.FollowRequestAccept(c.Request.Context(), authed, originAccountID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/followrequest/authorize_test.go b/internal/api/client/followrequests/authorize_test.go
index 693380d91..048c462c7 100644
--- a/internal/api/client/followrequest/authorize_test.go
+++ b/internal/api/client/followrequests/authorize_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest_test
+package followrequests_test
import (
"context"
@@ -30,7 +30,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
@@ -60,7 +60,7 @@ func (suite *AuthorizeTestSuite) TestAuthorize() {
ctx.Params = gin.Params{
gin.Param{
- Key: followrequest.IDKey,
+ Key: followrequests.IDKey,
Value: requestingAccount.ID,
},
}
@@ -90,7 +90,7 @@ func (suite *AuthorizeTestSuite) TestAuthorizeNoFR() {
ctx.Params = gin.Params{
gin.Param{
- Key: followrequest.IDKey,
+ Key: followrequests.IDKey,
Value: requestingAccount.ID,
},
}
diff --git a/internal/api/client/followrequest/followrequest.go b/internal/api/client/followrequests/followrequest.go
index a511d7226..d9d241e63 100644
--- a/internal/api/client/followrequest/followrequest.go
+++ b/internal/api/client/followrequests/followrequest.go
@@ -16,21 +16,20 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest
+package followrequests
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
// IDKey is for account IDs
IDKey = "id"
- // BasePath is the base path for serving the follow request API
- BasePath = "/api/v1/follow_requests"
+ // BasePath is the base path for serving the follow request API, minus the 'api' prefix
+ BasePath = "/v1/follow_requests"
// BasePathWithID is just the base path with the ID key in it.
// Use this anywhere you need to know the ID of the account that owns the follow request being queried.
BasePathWithID = BasePath + "/:" + IDKey
@@ -40,22 +39,18 @@ const (
RejectPath = BasePathWithID + "/reject"
)
-// Module implements the ClientAPIModule interface
type Module struct {
processor processing.Processor
}
-// New returns a new follow request module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.FollowRequestGETHandler)
- r.AttachHandler(http.MethodPost, AuthorizePath, m.FollowRequestAuthorizePOSTHandler)
- r.AttachHandler(http.MethodPost, RejectPath, m.FollowRequestRejectPOSTHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.FollowRequestGETHandler)
+ attachHandler(http.MethodPost, AuthorizePath, m.FollowRequestAuthorizePOSTHandler)
+ attachHandler(http.MethodPost, RejectPath, m.FollowRequestRejectPOSTHandler)
}
diff --git a/internal/api/client/followrequest/followrequest_test.go b/internal/api/client/followrequests/followrequest_test.go
index ca00ea054..c8036cd24 100644
--- a/internal/api/client/followrequest/followrequest_test.go
+++ b/internal/api/client/followrequests/followrequest_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest_test
+package followrequests_test
import (
"bytes"
@@ -25,7 +25,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -59,7 +59,7 @@ type FollowRequestStandardTestSuite struct {
testStatuses map[string]*gtsmodel.Status
// module being tested
- followRequestModule *followrequest.Module
+ followRequestModule *followrequests.Module
}
func (suite *FollowRequestStandardTestSuite) SetupSuite() {
@@ -85,7 +85,7 @@ func (suite *FollowRequestStandardTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.followRequestModule = followrequest.New(suite.processor).(*followrequest.Module)
+ suite.followRequestModule = followrequests.New(suite.processor)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/client/followrequest/get.go b/internal/api/client/followrequests/get.go
index 8a2be3686..1153f0f4b 100644
--- a/internal/api/client/followrequest/get.go
+++ b/internal/api/client/followrequests/get.go
@@ -16,13 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest
+package followrequests
import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -74,18 +74,18 @@ import (
func (m *Module) FollowRequestGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
accts, errWithCode := m.processor.FollowRequestsGet(c.Request.Context(), authed)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/followrequest/get_test.go b/internal/api/client/followrequests/get_test.go
index c9b72a35b..d4c9da0a1 100644
--- a/internal/api/client/followrequest/get_test.go
+++ b/internal/api/client/followrequests/get_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest_test
+package followrequests_test
import (
"context"
diff --git a/internal/api/client/followrequest/reject.go b/internal/api/client/followrequests/reject.go
index 717dbf4dd..782f932cd 100644
--- a/internal/api/client/followrequest/reject.go
+++ b/internal/api/client/followrequests/reject.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest
+package followrequests
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -70,25 +70,25 @@ import (
func (m *Module) FollowRequestRejectPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
originAccountID := c.Param(IDKey)
if originAccountID == "" {
err := errors.New("no account id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
relationship, errWithCode := m.processor.FollowRequestReject(c.Request.Context(), authed, originAccountID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/followrequest/reject_test.go b/internal/api/client/followrequests/reject_test.go
index 94c646ddc..cea42829d 100644
--- a/internal/api/client/followrequest/reject_test.go
+++ b/internal/api/client/followrequests/reject_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package followrequest_test
+package followrequests_test
import (
"context"
@@ -30,7 +30,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
@@ -60,7 +60,7 @@ func (suite *RejectTestSuite) TestReject() {
ctx.Params = gin.Params{
gin.Param{
- Key: followrequest.IDKey,
+ Key: followrequests.IDKey,
Value: requestingAccount.ID,
},
}
diff --git a/internal/api/client/instance/instance.go b/internal/api/client/instance/instance.go
index 16ff7c9f9..101e8cea4 100644
--- a/internal/api/client/instance/instance.go
+++ b/internal/api/client/instance/instance.go
@@ -21,36 +21,31 @@ package instance
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // InstanceInformationPath is for serving instance info requests
- InstanceInformationPath = "api/v1/instance"
+ // InstanceInformationPath is for serving instance info requests, minus the 'api' prefix.
+ InstanceInformationPath = "/v1/instance"
// InstancePeersPath is for serving instance peers requests.
InstancePeersPath = InstanceInformationPath + "/peers"
// PeersFilterKey is used to provide filters to /api/v1/instance/peers
PeersFilterKey = "filter"
)
-// Module implements the ClientModule interface
type Module struct {
processor processing.Processor
}
-// New returns a new instance information module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route satisfies the ClientModule interface
-func (m *Module) Route(s router.Router) error {
- s.AttachHandler(http.MethodGet, InstanceInformationPath, m.InstanceInformationGETHandler)
- s.AttachHandler(http.MethodPatch, InstanceInformationPath, m.InstanceUpdatePATCHHandler)
- s.AttachHandler(http.MethodGet, InstancePeersPath, m.InstancePeersGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, InstanceInformationPath, m.InstanceInformationGETHandler)
+ attachHandler(http.MethodPatch, InstanceInformationPath, m.InstanceUpdatePATCHHandler)
+ attachHandler(http.MethodGet, InstancePeersPath, m.InstancePeersGETHandler)
}
diff --git a/internal/api/client/instance/instance_test.go b/internal/api/client/instance/instance_test.go
index 26f29027d..33efbc847 100644
--- a/internal/api/client/instance/instance_test.go
+++ b/internal/api/client/instance/instance_test.go
@@ -88,7 +88,7 @@ func (suite *InstanceStandardTestSuite) SetupTest() {
suite.sentEmails = make(map[string]string)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.instanceModule = instance.New(suite.processor).(*instance.Module)
+ suite.instanceModule = instance.New(suite.processor)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}
diff --git a/internal/api/client/instance/instanceget.go b/internal/api/client/instance/instanceget.go
index bcedf398b..dfb8330ff 100644
--- a/internal/api/client/instance/instanceget.go
+++ b/internal/api/client/instance/instanceget.go
@@ -21,7 +21,7 @@ package instance
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
@@ -49,14 +49,14 @@ import (
// '500':
// description: internal error
func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/instance/instancepatch.go b/internal/api/client/instance/instancepatch.go
index d4fa8ca5d..891ce8e38 100644
--- a/internal/api/client/instance/instancepatch.go
+++ b/internal/api/client/instance/instancepatch.go
@@ -24,8 +24,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -130,42 +130,42 @@ import (
func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
if !*authed.User.Admin {
err := errors.New("user is not an admin so cannot update instance settings")
- api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.InstanceSettingsUpdateRequest{}
+ form := &apimodel.InstanceSettingsUpdateRequest{}
if err := c.ShouldBind(&form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if err := validateInstanceUpdate(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
}
i, errWithCode := m.processor.InstancePatch(c.Request.Context(), form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, i)
}
-func validateInstanceUpdate(form *model.InstanceSettingsUpdateRequest) error {
+func validateInstanceUpdate(form *apimodel.InstanceSettingsUpdateRequest) error {
if form.Title == nil &&
form.ContactUsername == nil &&
form.ContactEmail == nil &&
diff --git a/internal/api/client/instance/instancepeersget.go b/internal/api/client/instance/instancepeersget.go
index f7d05acdc..de6e40e7c 100644
--- a/internal/api/client/instance/instancepeersget.go
+++ b/internal/api/client/instance/instancepeersget.go
@@ -23,7 +23,7 @@ import (
"net/http"
"strings"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -101,12 +101,12 @@ import (
func (m *Module) InstancePeersGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, false, false, false, false)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -124,7 +124,7 @@ func (m *Module) InstancePeersGETHandler(c *gin.Context) {
includeOpen = true
default:
err := fmt.Errorf("filter %s not recognized; accepted values are 'open', 'suspended'", trimmed)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
}
@@ -138,7 +138,7 @@ func (m *Module) InstancePeersGETHandler(c *gin.Context) {
data, errWithCode := m.processor.InstancePeersGet(c.Request.Context(), authed, includeSuspended, includeOpen, flat)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/list/listsgets.go b/internal/api/client/list/listsgets.go
deleted file mode 100644
index 246a1216a..000000000
--- a/internal/api/client/list/listsgets.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package list
-
-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"
-)
-
-// ListsGETHandler returns a list of lists created by/for the authed account
-func (m *Module) ListsGETHandler(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
- }
-
- c.JSON(http.StatusOK, []string{})
-}
diff --git a/internal/api/client/list/list.go b/internal/api/client/lists/list.go
index c64ada43e..c14917b98 100644
--- a/internal/api/client/list/list.go
+++ b/internal/api/client/lists/list.go
@@ -16,35 +16,30 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package list
+package lists
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base path for serving the lists API
- BasePath = "/api/v1/lists"
+ // BasePath is the base path for serving the lists API, minus the 'api' prefix
+ BasePath = "/v1/lists"
)
-// Module implements the ClientAPIModule interface for everything related to lists
type Module struct {
processor processing.Processor
}
-// New returns a new list module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.ListsGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.ListsGETHandler)
}
diff --git a/internal/api/security/robots.go b/internal/api/client/lists/listsgets.go
index 5b8ba3c05..a4e5cbefa 100644
--- a/internal/api/security/robots.go
+++ b/internal/api/client/lists/listsgets.go
@@ -16,42 +16,29 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package security
+package lists
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"
)
-const robotsString = `User-agent: *
-Crawl-delay: 500
-# api stuff
-Disallow: /api/
-# auth/login stuff
-Disallow: /auth/
-Disallow: /oauth/
-Disallow: /check_your_email
-Disallow: /wait_for_approval
-Disallow: /account_disabled
-# well known stuff
-Disallow: /.well-known/
-# files
-Disallow: /fileserver/
-# s2s AP stuff
-Disallow: /users/
-Disallow: /emoji/
-# panels
-Disallow: /admin
-Disallow: /user
-Disallow: /settings/
-`
-
-// RobotsGETHandler returns a decent robots.txt that prevents crawling
-// the api, auth pages, settings pages, etc.
-//
-// More granular robots meta tags are then applied for web pages
-// depending on user preferences (see internal/web).
-func (m *Module) RobotsGETHandler(c *gin.Context) {
- c.String(http.StatusOK, robotsString)
+// ListsGETHandler returns a list of lists created by/for the authed account
+func (m *Module) ListsGETHandler(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
+ }
+
+ // todo: implement this; currently it's a no-op
+ c.JSON(http.StatusOK, []string{})
}
diff --git a/internal/api/client/media/media.go b/internal/api/client/media/media.go
index 87cc2f091..889a4f3df 100644
--- a/internal/api/client/media/media.go
+++ b/internal/api/client/media/media.go
@@ -21,34 +21,31 @@ package media
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- IDKey = "id" // IDKey is the key for media attachment IDs
- APIVersionKey = "api_version" // APIVersionKey is the key for which version of the API to use (v1 or v2)
- BasePathWithAPIVersion = "/api/:" + APIVersionKey + "/media" // BasePathWithAPIVersion is the base API path for making media requests through v1 or v2 of the api (for mastodon API compatibility)
- BasePathWithIDV1 = "/api/v1/media/:" + IDKey // BasePathWithID corresponds to a media attachment with the given ID
+ IDKey = "id" // IDKey is the key for media attachment IDs
+ APIVersionKey = "api_version" // APIVersionKey is the key for which version of the API to use (v1 or v2)
+ APIv1 = "v1" // APIV1 corresponds to version 1 of the api
+ APIv2 = "v2" // APIV2 corresponds to version 2 of the api
+ BasePath = "/:" + APIVersionKey + "/media" // BasePath is the base API path for making media requests through v1 or v2 of the api (for mastodon API compatibility)
+ AttachmentWithID = BasePath + "/:" + IDKey // BasePathWithID corresponds to a media attachment with the given ID
)
-// Module implements the ClientAPIModule interface for media
type Module struct {
processor processing.Processor
}
-// New returns a new auth module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route satisfies the RESTAPIModule interface
-func (m *Module) Route(s router.Router) error {
- s.AttachHandler(http.MethodPost, BasePathWithAPIVersion, m.MediaCreatePOSTHandler)
- s.AttachHandler(http.MethodGet, BasePathWithIDV1, m.MediaGETHandler)
- s.AttachHandler(http.MethodPut, BasePathWithIDV1, m.MediaPUTHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodPost, BasePath, m.MediaCreatePOSTHandler)
+ attachHandler(http.MethodGet, AttachmentWithID, m.MediaGETHandler)
+ attachHandler(http.MethodPut, AttachmentWithID, m.MediaPUTHandler)
}
diff --git a/internal/api/client/media/mediacreate.go b/internal/api/client/media/mediacreate.go
index db8b2ea56..7e29b2bb3 100644
--- a/internal/api/client/media/mediacreate.go
+++ b/internal/api/client/media/mediacreate.go
@@ -24,8 +24,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -94,42 +94,42 @@ import (
// '500':
// description: internal server error
func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
- authed, err := oauth.Authed(c, true, true, true, true)
- if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiVersion := c.Param(APIVersionKey)
+ if apiVersion != APIv1 && apiVersion != APIv2 {
+ err := errors.New("api version must be one of v1 or v2 for this path")
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(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)
+ authed, err := oauth.Authed(c, true, true, true, true)
+ if err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- apiVersion := c.Param(APIVersionKey)
- if apiVersion != "v1" && apiVersion != "v2" {
- err := errors.New("api version must be one of v1 or v2")
- api.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.AttachmentRequest{}
+ form := &apimodel.AttachmentRequest{}
if err := c.ShouldBind(&form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if err := validateCreateMedia(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
}
apiAttachment, errWithCode := m.processor.MediaCreate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
- if apiVersion == "v2" {
+ if apiVersion == APIv2 {
// the mastodon v2 media API specifies that the URL should be null
// and that the client should call /api/v1/media/:id to get the URL
//
@@ -141,7 +141,7 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
c.JSON(http.StatusOK, apiAttachment)
}
-func validateCreateMedia(form *model.AttachmentRequest) error {
+func validateCreateMedia(form *apimodel.AttachmentRequest) error {
// check there actually is a file attached and it's not size 0
if form.File == nil {
return errors.New("no attachment given")
diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go
index 2f6fb12a4..9e787b4b9 100644
--- a/internal/api/client/media/mediacreate_test.go
+++ b/internal/api/client/media/mediacreate_test.go
@@ -30,10 +30,9 @@ import (
"net/http/httptest"
"testing"
- "github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
mediamodule "github.com/superseriousbusiness/gotosocial/internal/api/client/media"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -96,7 +95,7 @@ func (suite *MediaCreateTestSuite) SetupSuite() {
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
// setup module being tested
- suite.mediaModule = mediamodule.New(suite.processor).(*mediamodule.Module)
+ suite.mediaModule = mediamodule.New(suite.processor)
}
func (suite *MediaCreateTestSuite) TearDownSuite() {
@@ -158,12 +157,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessful() {
ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v1/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting
ctx.Request.Header.Set("Content-Type", w.FormDataContentType())
ctx.Request.Header.Set("accept", "application/json")
- ctx.Params = gin.Params{
- gin.Param{
- Key: mediamodule.APIVersionKey,
- Value: "v1",
- },
- }
+ ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1)
// do the actual request
suite.mediaModule.MediaCreatePOSTHandler(ctx)
@@ -188,26 +182,26 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessful() {
suite.NoError(err)
fmt.Println(string(b))
- attachmentReply := &model.Attachment{}
+ attachmentReply := &apimodel.Attachment{}
err = json.Unmarshal(b, attachmentReply)
suite.NoError(err)
suite.Equal("this is a test image -- a cool background from somewhere", *attachmentReply.Description)
suite.Equal("image", attachmentReply.Type)
- suite.EqualValues(model.MediaMeta{
- Original: model.MediaDimensions{
+ suite.EqualValues(apimodel.MediaMeta{
+ Original: apimodel.MediaDimensions{
Width: 1920,
Height: 1080,
Size: "1920x1080",
Aspect: 1.7777778,
},
- Small: model.MediaDimensions{
+ Small: apimodel.MediaDimensions{
Width: 512,
Height: 288,
Size: "512x288",
Aspect: 1.7777778,
},
- Focus: model.MediaFocus{
+ Focus: apimodel.MediaFocus{
X: -0.5,
Y: 0.5,
},
@@ -252,12 +246,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessfulV2() {
ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v2/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting
ctx.Request.Header.Set("Content-Type", w.FormDataContentType())
ctx.Request.Header.Set("accept", "application/json")
- ctx.Params = gin.Params{
- gin.Param{
- Key: mediamodule.APIVersionKey,
- Value: "v2",
- },
- }
+ ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv2)
// do the actual request
suite.mediaModule.MediaCreatePOSTHandler(ctx)
@@ -282,26 +271,26 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessfulV2() {
suite.NoError(err)
fmt.Println(string(b))
- attachmentReply := &model.Attachment{}
+ attachmentReply := &apimodel.Attachment{}
err = json.Unmarshal(b, attachmentReply)
suite.NoError(err)
suite.Equal("this is a test image -- a cool background from somewhere", *attachmentReply.Description)
suite.Equal("image", attachmentReply.Type)
- suite.EqualValues(model.MediaMeta{
- Original: model.MediaDimensions{
+ suite.EqualValues(apimodel.MediaMeta{
+ Original: apimodel.MediaDimensions{
Width: 1920,
Height: 1080,
Size: "1920x1080",
Aspect: 1.7777778,
},
- Small: model.MediaDimensions{
+ Small: apimodel.MediaDimensions{
Width: 512,
Height: 288,
Size: "512x288",
Aspect: 1.7777778,
},
- Focus: model.MediaFocus{
+ Focus: apimodel.MediaFocus{
X: -0.5,
Y: 0.5,
},
@@ -342,12 +331,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateLongDescription() {
ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v1/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting
ctx.Request.Header.Set("Content-Type", w.FormDataContentType())
ctx.Request.Header.Set("accept", "application/json")
- ctx.Params = gin.Params{
- gin.Param{
- Key: mediamodule.APIVersionKey,
- Value: "v1",
- },
- }
+ ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1)
// do the actual request
suite.mediaModule.MediaCreatePOSTHandler(ctx)
@@ -388,12 +372,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateTooShortDescription() {
ctx.Request = httptest.NewRequest(http.MethodPost, "http://localhost:8080/api/v1/media", bytes.NewReader(buf.Bytes())) // the endpoint we're hitting
ctx.Request.Header.Set("Content-Type", w.FormDataContentType())
ctx.Request.Header.Set("accept", "application/json")
- ctx.Params = gin.Params{
- gin.Param{
- Key: mediamodule.APIVersionKey,
- Value: "v1",
- },
- }
+ ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1)
// do the actual request
suite.mediaModule.MediaCreatePOSTHandler(ctx)
diff --git a/internal/api/client/media/mediaget.go b/internal/api/client/media/mediaget.go
index fd232c4c7..b22c8e79c 100644
--- a/internal/api/client/media/mediaget.go
+++ b/internal/api/client/media/mediaget.go
@@ -23,7 +23,7 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -67,27 +67,33 @@ import (
// '500':
// description: internal server error
func (m *Module) MediaGETHandler(c *gin.Context) {
+ if apiVersion := c.Param(APIVersionKey); apiVersion != APIv1 {
+ err := errors.New("api version must be one v1 for this path")
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet)
+ return
+ }
+
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
attachmentID := c.Param(IDKey)
if attachmentID == "" {
err := errors.New("no attachment 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
}
attachment, errWithCode := m.processor.MediaGet(c.Request.Context(), authed, attachmentID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/media/mediaupdate.go b/internal/api/client/media/mediaupdate.go
index 438eaca23..9cfd8a5f1 100644
--- a/internal/api/client/media/mediaupdate.go
+++ b/internal/api/client/media/mediaupdate.go
@@ -24,8 +24,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -99,45 +99,51 @@ import (
// '500':
// description: internal server error
func (m *Module) MediaPUTHandler(c *gin.Context) {
+ if apiVersion := c.Param(APIVersionKey); apiVersion != APIv1 {
+ err := errors.New("api version must be one v1 for this path")
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet)
+ return
+ }
+
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
attachmentID := c.Param(IDKey)
if attachmentID == "" {
err := errors.New("no attachment id specified")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.AttachmentUpdateRequest{}
+ form := &apimodel.AttachmentUpdateRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if err := validateUpdateMedia(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
}
attachment, errWithCode := m.processor.MediaUpdate(c.Request.Context(), authed, attachmentID, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, attachment)
}
-func validateUpdateMedia(form *model.AttachmentUpdateRequest) error {
+func validateUpdateMedia(form *apimodel.AttachmentUpdateRequest) error {
minDescriptionChars := config.GetMediaDescriptionMinChars()
maxDescriptionChars := config.GetMediaDescriptionMaxChars()
diff --git a/internal/api/client/media/mediaupdate_test.go b/internal/api/client/media/mediaupdate_test.go
index e5abb0a91..bcf9a4dfe 100644
--- a/internal/api/client/media/mediaupdate_test.go
+++ b/internal/api/client/media/mediaupdate_test.go
@@ -28,10 +28,9 @@ import (
"net/http/httptest"
"testing"
- "github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
mediamodule "github.com/superseriousbusiness/gotosocial/internal/api/client/media"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -94,7 +93,7 @@ func (suite *MediaUpdateTestSuite) SetupSuite() {
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
// setup module being tested
- suite.mediaModule = mediamodule.New(suite.processor).(*mediamodule.Module)
+ suite.mediaModule = mediamodule.New(suite.processor)
}
func (suite *MediaUpdateTestSuite) TearDownSuite() {
@@ -148,12 +147,8 @@ func (suite *MediaUpdateTestSuite) TestUpdateImage() {
ctx.Request = httptest.NewRequest(http.MethodPut, fmt.Sprintf("http://localhost:8080/api/v1/media/%s", toUpdate.ID), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting
ctx.Request.Header.Set("Content-Type", w.FormDataContentType())
ctx.Request.Header.Set("accept", "application/json")
- ctx.Params = gin.Params{
- gin.Param{
- Key: mediamodule.IDKey,
- Value: toUpdate.ID,
- },
- }
+ ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1)
+ ctx.AddParam(mediamodule.IDKey, toUpdate.ID)
// do the actual request
suite.mediaModule.MediaPUTHandler(ctx)
@@ -167,17 +162,17 @@ func (suite *MediaUpdateTestSuite) TestUpdateImage() {
suite.NoError(err)
// reply should be an attachment
- attachmentReply := &model.Attachment{}
+ attachmentReply := &apimodel.Attachment{}
err = json.Unmarshal(b, attachmentReply)
suite.NoError(err)
// the reply should contain the updated fields
suite.Equal("new description!", *attachmentReply.Description)
suite.EqualValues("image", attachmentReply.Type)
- suite.EqualValues(model.MediaMeta{
- Original: model.MediaDimensions{Width: 800, Height: 450, FrameRate: "", Duration: 0, Bitrate: 0, Size: "800x450", Aspect: 1.7777778},
- Small: model.MediaDimensions{Width: 256, Height: 144, FrameRate: "", Duration: 0, Bitrate: 0, Size: "256x144", Aspect: 1.7777778},
- Focus: model.MediaFocus{X: -0.1, Y: 0.3},
+ suite.EqualValues(apimodel.MediaMeta{
+ Original: apimodel.MediaDimensions{Width: 800, Height: 450, FrameRate: "", Duration: 0, Bitrate: 0, Size: "800x450", Aspect: 1.7777778},
+ Small: apimodel.MediaDimensions{Width: 256, Height: 144, FrameRate: "", Duration: 0, Bitrate: 0, Size: "256x144", Aspect: 1.7777778},
+ Focus: apimodel.MediaFocus{X: -0.1, Y: 0.3},
}, attachmentReply.Meta)
suite.Equal(toUpdate.Blurhash, attachmentReply.Blurhash)
suite.Equal(toUpdate.ID, attachmentReply.ID)
@@ -213,12 +208,8 @@ func (suite *MediaUpdateTestSuite) TestUpdateImageShortDescription() {
ctx.Request = httptest.NewRequest(http.MethodPut, fmt.Sprintf("http://localhost:8080/api/v1/media/%s", toUpdate.ID), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting
ctx.Request.Header.Set("Content-Type", w.FormDataContentType())
ctx.Request.Header.Set("accept", "application/json")
- ctx.Params = gin.Params{
- gin.Param{
- Key: mediamodule.IDKey,
- Value: toUpdate.ID,
- },
- }
+ ctx.AddParam(mediamodule.APIVersionKey, mediamodule.APIv1)
+ ctx.AddParam(mediamodule.IDKey, toUpdate.ID)
// do the actual request
suite.mediaModule.MediaPUTHandler(ctx)
diff --git a/internal/api/client/notification/notification.go b/internal/api/client/notifications/notifications.go
index 6ade0b02f..235f0a678 100644
--- a/internal/api/client/notification/notification.go
+++ b/internal/api/client/notifications/notifications.go
@@ -16,21 +16,20 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package notification
+package notifications
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
// IDKey is for notification UUIDs
IDKey = "id"
- // BasePath is the base path for serving the notification API
- BasePath = "/api/v1/notifications"
+ // BasePath is the base path for serving the notification API, minus the 'api' prefix.
+ BasePath = "/v1/notifications"
// BasePathWithID is just the base path with the ID key in it.
// Use this anywhere you need to know the ID of the notification being queried.
BasePathWithID = BasePath + "/:" + IDKey
@@ -46,21 +45,17 @@ const (
SinceIDKey = "since_id"
)
-// Module implements the ClientAPIModule interface for every related to posting/deleting/interacting with notifications
type Module struct {
processor processing.Processor
}
-// New returns a new notification module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.NotificationsGETHandler)
- r.AttachHandler(http.MethodPost, BasePathWithClear, m.NotificationsClearPOSTHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.NotificationsGETHandler)
+ attachHandler(http.MethodPost, BasePathWithClear, m.NotificationsClearPOSTHandler)
}
diff --git a/internal/api/client/notification/notificationsclear.go b/internal/api/client/notifications/notificationsclear.go
index b97371638..48c074504 100644
--- a/internal/api/client/notification/notificationsclear.go
+++ b/internal/api/client/notifications/notificationsclear.go
@@ -16,13 +16,13 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package notification
+package notifications
import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -61,18 +61,18 @@ import (
func (m *Module) NotificationsClearPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
errWithCode := m.processor.NotificationsClear(c.Request.Context(), authed)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/notification/notificationsget.go b/internal/api/client/notifications/notificationsget.go
index d6b3f5162..09000d02a 100644
--- a/internal/api/client/notification/notificationsget.go
+++ b/internal/api/client/notifications/notificationsget.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package notification
+package notifications
import (
"fmt"
@@ -24,7 +24,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -111,12 +111,12 @@ import (
func (m *Module) NotificationsGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -126,7 +126,7 @@ func (m *Module) NotificationsGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -148,7 +148,7 @@ func (m *Module) NotificationsGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.NotificationsGet(c.Request.Context(), authed, excludeTypes, limit, maxID, sinceID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/search/search.go b/internal/api/client/search/search.go
index 71370a6d5..bebe0bd61 100644
--- a/internal/api/client/search/search.go
+++ b/internal/api/client/search/search.go
@@ -21,17 +21,16 @@ package search
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePathV1 is the base path for serving v1 of the search API
- BasePathV1 = "/api/v1/search"
+ // BasePathV1 is the base path for serving v1 of the search API, minus the 'api' prefix
+ BasePathV1 = "/v1/search"
- // BasePathV2 is the base path for serving v2 of the search API
- BasePathV2 = "/api/v2/search"
+ // BasePathV2 is the base path for serving v2 of the search API, minus the 'api' prefix
+ BasePathV2 = "/v2/search"
// AccountIDKey -- If provided, statuses returned will be authored only by this account
AccountIDKey = "account_id"
@@ -62,21 +61,17 @@ const (
TypeStatuses = "statuses"
)
-// Module implements the ClientAPIModule interface for everything related to searching
type Module struct {
processor processing.Processor
}
-// New returns a new search module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePathV1, m.SearchGETHandler)
- r.AttachHandler(http.MethodGet, BasePathV2, m.SearchGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePathV1, m.SearchGETHandler)
+ attachHandler(http.MethodGet, BasePathV2, m.SearchGETHandler)
}
diff --git a/internal/api/client/search/search_test.go b/internal/api/client/search/search_test.go
index 11b5b80b2..3cb5e8377 100644
--- a/internal/api/client/search/search_test.go
+++ b/internal/api/client/search/search_test.go
@@ -84,7 +84,7 @@ func (suite *SearchStandardTestSuite) SetupTest() {
suite.sentEmails = make(map[string]string)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.searchModule = search.New(suite.processor).(*search.Module)
+ suite.searchModule = search.New(suite.processor)
testrig.StandardDBSetup(suite.db, nil)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/client/search/searchget.go b/internal/api/client/search/searchget.go
index 7026213ac..15786e6e3 100644
--- a/internal/api/client/search/searchget.go
+++ b/internal/api/client/search/searchget.go
@@ -25,8 +25,8 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -66,12 +66,12 @@ import (
func (m *Module) SearchGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -82,7 +82,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
excludeUnreviewed, err = strconv.ParseBool(excludeUnreviewedString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", ExcludeUnreviewedKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
}
@@ -90,7 +90,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
query := c.Query(QueryKey)
if query == "" {
err := errors.New("query parameter q was empty")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -101,7 +101,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
resolve, err = strconv.ParseBool(resolveString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", ResolveKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
}
@@ -112,7 +112,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -130,7 +130,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(offsetString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", OffsetKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
offset = int(i)
@@ -143,12 +143,12 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
following, err = strconv.ParseBool(followingString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", FollowingKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
}
- searchQuery := &model.SearchQuery{
+ searchQuery := &apimodel.SearchQuery{
AccountID: c.Query(AccountIDKey),
MaxID: c.Query(MaxIDKey),
MinID: c.Query(MinIDKey),
@@ -163,7 +163,7 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
results, errWithCode := m.processor.SearchGet(c.Request.Context(), authed, searchQuery)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/status.go b/internal/api/client/statuses/status.go
index dc32ae9b5..7f58e8c9d 100644
--- a/internal/api/client/status/status.go
+++ b/internal/api/client/statuses/status.go
@@ -16,31 +16,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"net/http"
- "strings"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
// IDKey is for status UUIDs
IDKey = "id"
- // BasePath is the base path for serving the status API
- BasePath = "/api/v1/statuses"
+ // BasePath is the base path for serving the statuses API, minus the 'api' prefix
+ BasePath = "/v1/statuses"
// BasePathWithID is just the base path with the ID key in it.
// Use this anywhere you need to know the ID of the status being queried.
BasePathWithID = BasePath + "/:" + IDKey
- // ContextPath is used for fetching context of posts
- ContextPath = BasePathWithID + "/context"
-
// FavouritedPath is for seeing who's faved a given status
FavouritedPath = BasePathWithID + "/favourited_by"
// FavouritePath is for posting a fave on a status
@@ -69,55 +62,39 @@ const (
PinPath = BasePathWithID + "/pin"
// UnpinPath is for undoing a pin and returning a status to the ever-swirling drain of time and entropy
UnpinPath = BasePathWithID + "/unpin"
+
+ // ContextPath is used for fetching context of posts
+ ContextPath = BasePathWithID + "/context"
)
-// Module implements the ClientAPIModule interface for every related to posting/deleting/interacting with statuses
type Module struct {
processor processing.Processor
}
-// New returns a new account module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodPost, BasePath, m.StatusCreatePOSTHandler)
- r.AttachHandler(http.MethodDelete, BasePathWithID, m.StatusDELETEHandler)
-
- r.AttachHandler(http.MethodPost, FavouritePath, m.StatusFavePOSTHandler)
- r.AttachHandler(http.MethodPost, UnfavouritePath, m.StatusUnfavePOSTHandler)
- r.AttachHandler(http.MethodGet, FavouritedPath, m.StatusFavedByGETHandler)
-
- r.AttachHandler(http.MethodPost, ReblogPath, m.StatusBoostPOSTHandler)
- r.AttachHandler(http.MethodPost, UnreblogPath, m.StatusUnboostPOSTHandler)
- r.AttachHandler(http.MethodGet, RebloggedPath, m.StatusBoostedByGETHandler)
-
- r.AttachHandler(http.MethodPost, BookmarkPath, m.StatusBookmarkPOSTHandler)
- r.AttachHandler(http.MethodPost, UnbookmarkPath, m.StatusUnbookmarkPOSTHandler)
-
- r.AttachHandler(http.MethodGet, ContextPath, m.StatusContextGETHandler)
-
- r.AttachHandler(http.MethodGet, BasePathWithID, m.muxHandler)
- return nil
-}
-
-// muxHandler is a little workaround to overcome the limitations of Gin
-func (m *Module) muxHandler(c *gin.Context) {
- log.Debug("entering mux handler")
- ru := c.Request.RequestURI
-
- if c.Request.Method == http.MethodGet {
- switch {
- case strings.HasPrefix(ru, ContextPath):
- // TODO
- case strings.HasPrefix(ru, FavouritedPath):
- m.StatusFavedByGETHandler(c)
- default:
- m.StatusGETHandler(c)
- }
- }
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ // create / get / delete status
+ attachHandler(http.MethodPost, BasePath, m.StatusCreatePOSTHandler)
+ attachHandler(http.MethodGet, BasePathWithID, m.StatusGETHandler)
+ attachHandler(http.MethodDelete, BasePathWithID, m.StatusDELETEHandler)
+
+ // fave stuff
+ attachHandler(http.MethodPost, FavouritePath, m.StatusFavePOSTHandler)
+ attachHandler(http.MethodPost, UnfavouritePath, m.StatusUnfavePOSTHandler)
+ attachHandler(http.MethodGet, FavouritedPath, m.StatusFavedByGETHandler)
+
+ // reblog stuff
+ attachHandler(http.MethodPost, ReblogPath, m.StatusBoostPOSTHandler)
+ attachHandler(http.MethodPost, UnreblogPath, m.StatusUnboostPOSTHandler)
+ attachHandler(http.MethodGet, RebloggedPath, m.StatusBoostedByGETHandler)
+ attachHandler(http.MethodPost, BookmarkPath, m.StatusBookmarkPOSTHandler)
+ attachHandler(http.MethodPost, UnbookmarkPath, m.StatusUnbookmarkPOSTHandler)
+
+ // context / status thread
+ attachHandler(http.MethodGet, ContextPath, m.StatusContextGETHandler)
}
diff --git a/internal/api/client/status/status_test.go b/internal/api/client/statuses/status_test.go
index 7c3f094f2..0bf824fdb 100644
--- a/internal/api/client/status/status_test.go
+++ b/internal/api/client/statuses/status_test.go
@@ -16,11 +16,11 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
@@ -56,7 +56,7 @@ type StatusStandardTestSuite struct {
testFollows map[string]*gtsmodel.Follow
// module being tested
- statusModule *status.Module
+ statusModule *statuses.Module
}
func (suite *StatusStandardTestSuite) SetupSuite() {
@@ -87,7 +87,7 @@ func (suite *StatusStandardTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.statusModule = status.New(suite.processor).(*status.Module)
+ suite.statusModule = statuses.New(suite.processor)
suite.NoError(suite.processor.Start())
}
diff --git a/internal/api/client/status/statusbookmark.go b/internal/api/client/statuses/statusbookmark.go
index 983becd72..4efa53528 100644
--- a/internal/api/client/status/statusbookmark.go
+++ b/internal/api/client/statuses/statusbookmark.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,25 +72,25 @@ import (
func (m *Module) StatusBookmarkPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusBookmark(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusbookmark_test.go b/internal/api/client/statuses/statusbookmark_test.go
index d3da4f297..ba2de78e1 100644
--- a/internal/api/client/status/statusbookmark_test.go
+++ b/internal/api/client/statuses/statusbookmark_test.go
@@ -13,7 +13,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"encoding/json"
@@ -26,7 +26,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "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/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -49,14 +49,14 @@ func (suite *StatusBookmarkTestSuite) TestPostBookmark() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.BookmarkPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.BookmarkPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
diff --git a/internal/api/client/status/statusboost.go b/internal/api/client/statuses/statusboost.go
index d43bedd6c..c8921b1b6 100644
--- a/internal/api/client/status/statusboost.go
+++ b/internal/api/client/statuses/statusboost.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -75,25 +75,25 @@ import (
func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusBoost(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusboost_test.go b/internal/api/client/statuses/statusboost_test.go
index 5b4b1b3cd..13ca2acf2 100644
--- a/internal/api/client/status/statusboost_test.go
+++ b/internal/api/client/statuses/statusboost_test.go
@@ -13,7 +13,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"context"
@@ -27,8 +27,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -51,14 +51,14 @@ func (suite *StatusBoostTestSuite) TestPostBoost() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
@@ -73,12 +73,12 @@ func (suite *StatusBoostTestSuite) TestPostBoost() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
suite.False(statusReply.Sensitive)
- suite.Equal(model.VisibilityPublic, statusReply.Visibility)
+ suite.Equal(apimodel.VisibilityPublic, statusReply.Visibility)
suite.Equal(targetStatus.ContentWarning, statusReply.SpoilerText)
suite.Equal(targetStatus.Content, statusReply.Content)
@@ -117,12 +117,12 @@ func (suite *StatusBoostTestSuite) TestPostBoostOwnFollowersOnly() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, testUser)
ctx.Set(oauth.SessionAuthorizedAccount, testAccount)
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", testStatus.ID, 1)), nil)
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.ReblogPath, ":id", testStatus.ID, 1)), nil)
ctx.Request.Header.Set("accept", "application/json")
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: testStatus.ID,
},
}
@@ -137,7 +137,7 @@ func (suite *StatusBoostTestSuite) TestPostBoostOwnFollowersOnly() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- responseStatus := &model.Status{}
+ responseStatus := &apimodel.Status{}
err = json.Unmarshal(b, responseStatus)
suite.NoError(err)
@@ -182,14 +182,14 @@ func (suite *StatusBoostTestSuite) TestPostUnboostable() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
@@ -224,14 +224,14 @@ func (suite *StatusBoostTestSuite) TestPostNotVisible() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
diff --git a/internal/api/client/status/statusboostedby.go b/internal/api/client/statuses/statusboostedby.go
index 4a175f6e9..dc1567dba 100644
--- a/internal/api/client/status/statusboostedby.go
+++ b/internal/api/client/statuses/statusboostedby.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -68,20 +68,20 @@ import (
func (m *Module) StatusBoostedByGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiAccounts, errWithCode := m.processor.StatusBoostedBy(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusboostedby_test.go b/internal/api/client/statuses/statusboostedby_test.go
index 0d7c9f7ab..576dee369 100644
--- a/internal/api/client/status/statusboostedby_test.go
+++ b/internal/api/client/statuses/statusboostedby_test.go
@@ -13,7 +13,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"encoding/json"
@@ -25,7 +25,7 @@ import (
"testing"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -46,7 +46,7 @@ func (suite *StatusBoostedByTestSuite) TestRebloggedByOK() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.RebloggedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.RebloggedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.AddParam("id", targetStatus.ID)
@@ -82,7 +82,7 @@ func (suite *StatusBoostedByTestSuite) TestRebloggedByUseBoostWrapperID() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.RebloggedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.RebloggedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.AddParam("id", targetStatus.ID)
diff --git a/internal/api/client/status/statuscontext.go b/internal/api/client/statuses/statuscontext.go
index 632a151d5..9a6ac9f7f 100644
--- a/internal/api/client/status/statuscontext.go
+++ b/internal/api/client/statuses/statuscontext.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -74,25 +74,25 @@ import (
func (m *Module) StatusContextGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
statusContext, errWithCode := m.processor.StatusGetContext(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statuscreate.go b/internal/api/client/statuses/statuscreate.go
index c1427411d..d36c93e77 100644
--- a/internal/api/client/status/statuscreate.go
+++ b/internal/api/client/statuses/statuscreate.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
@@ -24,8 +24,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -75,18 +75,18 @@ import (
func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.AdvancedStatusCreateForm{}
+ form := &apimodel.AdvancedStatusCreateForm{}
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
}
@@ -100,20 +100,20 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
// form.Status += "\n\nsent from " + user + "'s iphone\n"
if err := validateCreateStatus(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
}
apiStatus, errWithCode := m.processor.StatusCreate(c.Request.Context(), authed, form)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, apiStatus)
}
-func validateCreateStatus(form *model.AdvancedStatusCreateForm) error {
+func validateCreateStatus(form *apimodel.AdvancedStatusCreateForm) error {
hasStatus := form.Status != ""
hasMedia := len(form.MediaIDs) != 0
hasPoll := form.Poll != nil
diff --git a/internal/api/client/status/statuscreate_test.go b/internal/api/client/statuses/statuscreate_test.go
index c143489f3..3648d7520 100644
--- a/internal/api/client/status/statuscreate_test.go
+++ b/internal/api/client/statuses/statuscreate_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"context"
@@ -29,8 +29,8 @@ import (
"testing"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -59,13 +59,13 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {"this is a brand new status! #helloworld"},
"spoiler_text": {"hello hello"},
"sensitive": {"true"},
- "visibility": {string(model.VisibilityMutualsOnly)},
+ "visibility": {string(apimodel.VisibilityMutualsOnly)},
"likeable": {"false"},
"replyable": {"false"},
"federated": {"false"},
@@ -82,16 +82,16 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
suite.Equal("hello hello", statusReply.SpoilerText)
suite.Equal("<p>this is a brand new status! <a href=\"http://localhost:8080/tags/helloworld\" class=\"mention hashtag\" rel=\"tag nofollow noreferrer noopener\" target=\"_blank\">#<span>helloworld</span></a></p>", statusReply.Content)
suite.True(statusReply.Sensitive)
- suite.Equal(model.VisibilityPrivate, statusReply.Visibility) // even though we set this status to mutuals only, it should serialize to private, because the mastodon api has no idea about mutuals_only
+ suite.Equal(apimodel.VisibilityPrivate, statusReply.Visibility) // even though we set this status to mutuals only, it should serialize to private, because the mastodon api has no idea about mutuals_only
suite.Len(statusReply.Tags, 1)
- suite.Equal(model.Tag{
+ suite.Equal(apimodel.Tag{
Name: "helloworld",
URL: "http://localhost:8080/tags/helloworld",
}, statusReply.Tags[0])
@@ -124,11 +124,11 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusMarkdown() {
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, a)
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil)
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil)
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {statusMarkdown},
- "visibility": {string(model.VisibilityPublic)},
+ "visibility": {string(apimodel.VisibilityPublic)},
}
suite.statusModule.StatusCreatePOSTHandler(ctx)
@@ -139,7 +139,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusMarkdown() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
@@ -163,11 +163,11 @@ func (suite *StatusCreateTestSuite) TestMentionUnknownAccount() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {"hello @brand_new_person@unknown-instance.com"},
- "visibility": {string(model.VisibilityPublic)},
+ "visibility": {string(apimodel.VisibilityPublic)},
}
suite.statusModule.StatusCreatePOSTHandler(ctx)
@@ -178,13 +178,13 @@ func (suite *StatusCreateTestSuite) TestMentionUnknownAccount() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
// if the status is properly formatted, that means the account has been put in the db
suite.Equal(`<p>hello <span class="h-card"><a href="https://unknown-instance.com/@brand_new_person" class="u-url mention" rel="nofollow noreferrer noopener" target="_blank">@<span>brand_new_person</span></a></span></p>`, statusReply.Content)
- suite.Equal(model.VisibilityPublic, statusReply.Visibility)
+ suite.Equal(apimodel.VisibilityPublic, statusReply.Visibility)
}
func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
@@ -198,7 +198,7 @@ func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {statusWithLinksAndTags},
@@ -215,7 +215,7 @@ func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
@@ -233,7 +233,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {"here is a rainbow emoji a few times! :rainbow: :rainbow: :rainbow: \n here's an emoji that isn't in the db: :test_emoji: "},
@@ -247,7 +247,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
@@ -275,7 +275,7 @@ func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {"this is a reply to a status that doesn't exist"},
@@ -307,7 +307,7 @@ func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {fmt.Sprintf("hello @%s this reply should work!", testrig.NewTestAccounts()["local_account_2"].Username)},
@@ -323,14 +323,14 @@ func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
suite.Equal("", statusReply.SpoilerText)
suite.Equal(fmt.Sprintf("<p>hello <span class=\"h-card\"><a href=\"http://localhost:8080/@%s\" class=\"u-url mention\" rel=\"nofollow noreferrer noopener\" target=\"_blank\">@<span>%s</span></a></span> this reply should work!</p>", testrig.NewTestAccounts()["local_account_2"].Username, testrig.NewTestAccounts()["local_account_2"].Username), statusReply.Content)
suite.False(statusReply.Sensitive)
- suite.Equal(model.VisibilityPublic, statusReply.Visibility)
+ suite.Equal(apimodel.VisibilityPublic, statusReply.Visibility)
suite.Equal(testrig.NewTestStatuses()["local_account_2_status_1"].ID, *statusReply.InReplyToID)
suite.Equal(testrig.NewTestAccounts()["local_account_2"].ID, *statusReply.InReplyToAccountID)
suite.Len(statusReply.Mentions, 1)
@@ -350,7 +350,7 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Request.Form = url.Values{
"status": {"here's an image attachment"},
@@ -366,14 +366,14 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusResponse := &model.Status{}
+ statusResponse := &apimodel.Status{}
err = json.Unmarshal(b, statusResponse)
suite.NoError(err)
suite.Equal("", statusResponse.SpoilerText)
suite.Equal("<p>here&#39;s an image attachment</p>", statusResponse.Content)
suite.False(statusResponse.Sensitive)
- suite.Equal(model.VisibilityPublic, statusResponse.Visibility)
+ suite.Equal(apimodel.VisibilityPublic, statusResponse.Visibility)
// there should be one media attachment
suite.Len(statusResponse.MediaAttachments, 1)
diff --git a/internal/api/client/status/statusdelete.go b/internal/api/client/statuses/statusdelete.go
index b37dd5f14..3db7397db 100644
--- a/internal/api/client/status/statusdelete.go
+++ b/internal/api/client/statuses/statusdelete.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -74,25 +74,25 @@ import (
func (m *Module) StatusDELETEHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusDelete(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusdelete_test.go b/internal/api/client/statuses/statusdelete_test.go
index f97a13eec..9a9ceef8f 100644
--- a/internal/api/client/status/statusdelete_test.go
+++ b/internal/api/client/statuses/statusdelete_test.go
@@ -13,7 +13,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"encoding/json"
@@ -27,8 +27,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -50,14 +50,14 @@ func (suite *StatusDeleteTestSuite) TestPostDelete() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.BasePathWithID, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.BasePathWithID, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
@@ -72,7 +72,7 @@ func (suite *StatusDeleteTestSuite) TestPostDelete() {
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
suite.NoError(err)
suite.NotNil(statusReply)
diff --git a/internal/api/client/status/statusfave.go b/internal/api/client/statuses/statusfave.go
index 3117e7ef2..bd9ded147 100644
--- a/internal/api/client/status/statusfave.go
+++ b/internal/api/client/statuses/statusfave.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -71,25 +71,25 @@ import (
func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusFave(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusfave_test.go b/internal/api/client/statuses/statusfave_test.go
index da5d2a48a..20805d87c 100644
--- a/internal/api/client/status/statusfave_test.go
+++ b/internal/api/client/statuses/statusfave_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"encoding/json"
@@ -30,8 +30,9 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
+
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -54,14 +55,14 @@ func (suite *StatusFaveTestSuite) TestPostFave() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
@@ -76,14 +77,14 @@ func (suite *StatusFaveTestSuite) TestPostFave() {
b, err := ioutil.ReadAll(result.Body)
assert.NoError(suite.T(), err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.SpoilerText)
assert.Equal(suite.T(), targetStatus.Content, statusReply.Content)
assert.True(suite.T(), statusReply.Sensitive)
- assert.Equal(suite.T(), model.VisibilityPublic, statusReply.Visibility)
+ assert.Equal(suite.T(), apimodel.VisibilityPublic, statusReply.Visibility)
assert.True(suite.T(), statusReply.Favourited)
assert.Equal(suite.T(), 1, statusReply.FavouritesCount)
}
@@ -102,14 +103,14 @@ func (suite *StatusFaveTestSuite) TestPostUnfaveable() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
diff --git a/internal/api/client/status/statusfavedby.go b/internal/api/client/statuses/statusfavedby.go
index 20ef86ded..aa0f1f8d6 100644
--- a/internal/api/client/status/statusfavedby.go
+++ b/internal/api/client/statuses/statusfavedby.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,25 +72,25 @@ import (
func (m *Module) StatusFavedByGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiAccounts, errWithCode := m.processor.StatusFavedBy(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusfavedby_test.go b/internal/api/client/statuses/statusfavedby_test.go
index e704fa724..fc04c490e 100644
--- a/internal/api/client/status/statusfavedby_test.go
+++ b/internal/api/client/statuses/statusfavedby_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"encoding/json"
@@ -30,8 +30,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -53,14 +53,14 @@ func (suite *StatusFavedByTestSuite) TestGetFavedBy() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.FavouritedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
@@ -75,7 +75,7 @@ func (suite *StatusFavedByTestSuite) TestGetFavedBy() {
b, err := ioutil.ReadAll(result.Body)
assert.NoError(suite.T(), err)
- accts := []model.Account{}
+ accts := []apimodel.Account{}
err = json.Unmarshal(b, &accts)
assert.NoError(suite.T(), err)
diff --git a/internal/api/client/status/statusget.go b/internal/api/client/statuses/statusget.go
index a0d0e913c..5e7a59027 100644
--- a/internal/api/client/status/statusget.go
+++ b/internal/api/client/statuses/statusget.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -71,25 +71,25 @@ import (
func (m *Module) StatusGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusGet(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusget_test.go b/internal/api/client/statuses/statusget_test.go
index d11c9b587..e8e1fd8f4 100644
--- a/internal/api/client/status/statusget_test.go
+++ b/internal/api/client/statuses/statusget_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"testing"
diff --git a/internal/api/client/status/statusunbookmark.go b/internal/api/client/statuses/statusunbookmark.go
index aa090f8c9..117ef833b 100644
--- a/internal/api/client/status/statusunbookmark.go
+++ b/internal/api/client/statuses/statusunbookmark.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,25 +72,25 @@ import (
func (m *Module) StatusUnbookmarkPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusUnbookmark(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusunbookmark_test.go b/internal/api/client/statuses/statusunbookmark_test.go
index 09a18ab9b..9c4667ad8 100644
--- a/internal/api/client/status/statusunbookmark_test.go
+++ b/internal/api/client/statuses/statusunbookmark_test.go
@@ -13,7 +13,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"encoding/json"
@@ -26,7 +26,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/suite"
- "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/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
@@ -49,12 +49,12 @@ func (suite *StatusUnbookmarkTestSuite) TestPostUnbookmark() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnbookmarkPath, ":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.UnbookmarkPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
diff --git a/internal/api/client/status/statusunboost.go b/internal/api/client/statuses/statusunboost.go
index 45a8e0ece..e91081195 100644
--- a/internal/api/client/status/statusunboost.go
+++ b/internal/api/client/statuses/statusunboost.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -72,25 +72,25 @@ import (
func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusUnboost(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusunfave.go b/internal/api/client/statuses/statusunfave.go
index 19d3da3bd..57ae88e1e 100644
--- a/internal/api/client/status/statusunfave.go
+++ b/internal/api/client/statuses/statusunfave.go
@@ -16,14 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status
+package statuses
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -71,25 +71,25 @@ import (
func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
targetStatusID := c.Param(IDKey)
if targetStatusID == "" {
err := errors.New("no status 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
}
apiStatus, errWithCode := m.processor.StatusUnfave(c.Request.Context(), authed, targetStatusID)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/status/statusunfave_test.go b/internal/api/client/statuses/statusunfave_test.go
index b8448d657..2ca3450a4 100644
--- a/internal/api/client/status/statusunfave_test.go
+++ b/internal/api/client/statuses/statusunfave_test.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package status_test
+package statuses_test
import (
"encoding/json"
@@ -30,8 +30,8 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/status"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ "github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -55,14 +55,14 @@ func (suite *StatusUnfaveTestSuite) TestPostUnfave() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
@@ -77,14 +77,14 @@ func (suite *StatusUnfaveTestSuite) TestPostUnfave() {
b, err := ioutil.ReadAll(result.Body)
assert.NoError(suite.T(), err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.SpoilerText)
assert.Equal(suite.T(), targetStatus.Content, statusReply.Content)
assert.False(suite.T(), statusReply.Sensitive)
- assert.Equal(suite.T(), model.VisibilityPublic, statusReply.Visibility)
+ assert.Equal(suite.T(), apimodel.VisibilityPublic, statusReply.Visibility)
assert.False(suite.T(), statusReply.Favourited)
assert.Equal(suite.T(), 0, statusReply.FavouritesCount)
}
@@ -104,14 +104,14 @@ func (suite *StatusUnfaveTestSuite) TestPostAlreadyNotFaved() {
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
- ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/json")
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
- Key: status.IDKey,
+ Key: statuses.IDKey,
Value: targetStatus.ID,
},
}
@@ -126,14 +126,14 @@ func (suite *StatusUnfaveTestSuite) TestPostAlreadyNotFaved() {
b, err := ioutil.ReadAll(result.Body)
assert.NoError(suite.T(), err)
- statusReply := &model.Status{}
+ statusReply := &apimodel.Status{}
err = json.Unmarshal(b, statusReply)
assert.NoError(suite.T(), err)
assert.Equal(suite.T(), targetStatus.ContentWarning, statusReply.SpoilerText)
assert.Equal(suite.T(), targetStatus.Content, statusReply.Content)
assert.True(suite.T(), statusReply.Sensitive)
- assert.Equal(suite.T(), model.VisibilityPublic, statusReply.Visibility)
+ assert.Equal(suite.T(), apimodel.VisibilityPublic, statusReply.Visibility)
assert.False(suite.T(), statusReply.Favourited)
assert.Equal(suite.T(), 0, statusReply.FavouritesCount)
}
diff --git a/internal/api/client/streaming/stream.go b/internal/api/client/streaming/stream.go
index a9cb62732..de98719c2 100644
--- a/internal/api/client/streaming/stream.go
+++ b/internal/api/client/streaming/stream.go
@@ -1,3 +1,21 @@
+/*
+ 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 streaming
import (
@@ -6,7 +24,7 @@ import (
"time"
"codeberg.org/gruf/go-kv"
- "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/log"
@@ -14,12 +32,15 @@ import (
"github.com/gorilla/websocket"
)
-var wsUpgrader = websocket.Upgrader{
- ReadBufferSize: 1024,
- WriteBufferSize: 1024,
- // we expect cors requests (via eg., pinafore.social) so be lenient
- CheckOrigin: func(r *http.Request) bool { return true },
-}
+var (
+ wsUpgrader = websocket.Upgrader{
+ ReadBufferSize: 1024,
+ WriteBufferSize: 1024,
+ // we expect cors requests (via eg., pinafore.social) so be lenient
+ CheckOrigin: func(r *http.Request) bool { return true },
+ }
+ errNoToken = fmt.Errorf("no access token provided under query key %s or under header %s", AccessTokenQueryKey, AccessTokenHeader)
+)
// StreamGETHandler swagger:operation GET /api/v1/streaming streamGet
//
@@ -125,29 +146,33 @@ func (m *Module) StreamGETHandler(c *gin.Context) {
streamType := c.Query(StreamQueryKey)
if streamType == "" {
err := fmt.Errorf("no stream type provided under query key %s", StreamQueryKey)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- accessToken := c.Query(AccessTokenQueryKey)
- if accessToken == "" {
- accessToken = c.GetHeader(AccessTokenHeader)
- }
- if accessToken == "" {
- err := fmt.Errorf("no access token provided under query key %s or under header %s", AccessTokenQueryKey, AccessTokenHeader)
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ var accessToken string
+ if t := c.Query(AccessTokenQueryKey); t != "" {
+ // try query param first
+ accessToken = t
+ } else if t := c.GetHeader(AccessTokenHeader); t != "" {
+ // fall back to Sec-Websocket-Protocol
+ accessToken = t
+ } else {
+ // no token
+ err := errNoToken
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
account, errWithCode := m.processor.AuthorizeStreamingRequest(c.Request.Context(), accessToken)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
stream, errWithCode := m.processor.OpenStreamForAccount(c.Request.Context(), account, streamType)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -175,6 +200,7 @@ func (m *Module) StreamGETHandler(c *gin.Context) {
}()
streamTicker := time.NewTicker(m.tickDuration)
+ defer streamTicker.Stop()
// We want to stay in the loop as long as possible while the client is connected.
// The only thing that should break the loop is if the client leaves or the connection becomes unhealthy.
diff --git a/internal/api/client/streaming/streaming.go b/internal/api/client/streaming/streaming.go
index b15dfbdbd..f9d9fdf36 100644
--- a/internal/api/client/streaming/streaming.go
+++ b/internal/api/client/streaming/streaming.go
@@ -22,14 +22,13 @@ import (
"net/http"
"time"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the path for the streaming api
- BasePath = "/api/v1/streaming"
+ // BasePath is the path for the streaming api, minus the 'api' prefix
+ BasePath = "/v1/streaming"
// StreamQueryKey is the query key for the type of stream being requested
StreamQueryKey = "stream"
@@ -41,29 +40,25 @@ const (
AccessTokenHeader = "Sec-Websocket-Protocol"
)
-// Module implements the api.ClientModule interface for everything related to streaming
type Module struct {
processor processing.Processor
tickDuration time.Duration
}
-// New returns a new streaming module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
tickDuration: 30 * time.Second,
}
}
-func NewWithTickDuration(processor processing.Processor, tickDuration time.Duration) api.ClientModule {
+func NewWithTickDuration(processor processing.Processor, tickDuration time.Duration) *Module {
return &Module{
processor: processor,
tickDuration: tickDuration,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, BasePath, m.StreamGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, BasePath, m.StreamGETHandler)
}
diff --git a/internal/api/client/streaming/streaming_test.go b/internal/api/client/streaming/streaming_test.go
index 49c983fff..2f2d850c1 100644
--- a/internal/api/client/streaming/streaming_test.go
+++ b/internal/api/client/streaming/streaming_test.go
@@ -99,7 +99,7 @@ func (suite *StreamingTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.streamingModule = streaming.NewWithTickDuration(suite.processor, 1).(*streaming.Module)
+ suite.streamingModule = streaming.NewWithTickDuration(suite.processor, 1)
suite.NoError(suite.processor.Start())
}
diff --git a/internal/api/client/timeline/home.go b/internal/api/client/timelines/home.go
index e6135dd63..33af8fe5e 100644
--- a/internal/api/client/timeline/home.go
+++ b/internal/api/client/timelines/home.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package timeline
+package timelines
import (
"fmt"
@@ -24,7 +24,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -112,12 +112,12 @@ import (
func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -145,7 +145,7 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -157,7 +157,7 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(localString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LocalKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
local = i
@@ -165,7 +165,7 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.HomeTimelineGet(c.Request.Context(), authed, maxID, sinceID, minID, limit, local)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/timeline/public.go b/internal/api/client/timelines/public.go
index fda23438b..efe351a37 100644
--- a/internal/api/client/timeline/public.go
+++ b/internal/api/client/timelines/public.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package timeline
+package timelines
import (
"fmt"
@@ -24,7 +24,7 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -123,12 +123,12 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
}
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
@@ -156,7 +156,7 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
i, err := strconv.ParseInt(limitString, 10, 32)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
limit = int(i)
@@ -168,7 +168,7 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
i, err := strconv.ParseBool(localString)
if err != nil {
err := fmt.Errorf("error parsing %s: %s", LocalKey, err)
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
local = i
@@ -176,7 +176,7 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
resp, errWithCode := m.processor.PublicTimelineGet(c.Request.Context(), authed, maxID, sinceID, minID, limit, local)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/timeline/timeline.go b/internal/api/client/timelines/timeline.go
index 3604a1fc2..609e1855e 100644
--- a/internal/api/client/timeline/timeline.go
+++ b/internal/api/client/timelines/timeline.go
@@ -16,19 +16,18 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package timeline
+package timelines
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base URI path for serving timelines
- BasePath = "/api/v1/timelines"
+ // BasePath is the base URI path for serving timelines, minus the 'api' prefix.
+ BasePath = "/v1/timelines"
// HomeTimeline is the path for the home timeline
HomeTimeline = BasePath + "/home"
// PublicTimeline is the path for the public (and public local) timeline
@@ -45,21 +44,17 @@ const (
LocalKey = "local"
)
-// Module implements the ClientAPIModule interface for everything relating to viewing timelines
type Module struct {
processor processing.Processor
}
-// New returns a new timeline module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodGet, HomeTimeline, m.HomeTimelineGETHandler)
- r.AttachHandler(http.MethodGet, PublicTimeline, m.PublicTimelineGETHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, HomeTimeline, m.HomeTimelineGETHandler)
+ attachHandler(http.MethodGet, PublicTimeline, m.PublicTimelineGETHandler)
}
diff --git a/internal/api/client/user/passwordchange.go b/internal/api/client/user/passwordchange.go
index a900af897..c766d915c 100644
--- a/internal/api/client/user/passwordchange.go
+++ b/internal/api/client/user/passwordchange.go
@@ -23,8 +23,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
@@ -68,35 +68,35 @@ import (
func (m *Module) PasswordChangePOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- form := &model.PasswordChangeRequest{}
+ form := &apimodel.PasswordChangeRequest{}
if err := c.ShouldBind(form); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if form.OldPassword == "" {
err := errors.New("password change request missing field old_password")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if form.NewPassword == "" {
err := errors.New("password change request missing field new_password")
- api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
if errWithCode := m.processor.UserChangePassword(c.Request.Context(), authed, form); errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/user/user.go b/internal/api/client/user/user.go
index 86a0096e0..5e6002b40 100644
--- a/internal/api/client/user/user.go
+++ b/internal/api/client/user/user.go
@@ -21,32 +21,27 @@ package user
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // BasePath is the base URI path for this module
- BasePath = "/api/v1/user"
+ // BasePath is the base URI path for this module, minus the 'api' prefix
+ BasePath = "/v1/user"
// PasswordChangePath is the path for POSTing a password change request.
PasswordChangePath = BasePath + "/password_change"
)
-// Module implements the ClientAPIModule interface
type Module struct {
processor processing.Processor
}
-// New returns a new user module
-func New(processor processing.Processor) api.ClientModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route attaches all routes from this module to the given router
-func (m *Module) Route(r router.Router) error {
- r.AttachHandler(http.MethodPost, PasswordChangePath, m.PasswordChangePOSTHandler)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodPost, PasswordChangePath, m.PasswordChangePOSTHandler)
}
diff --git a/internal/api/client/user/user_test.go b/internal/api/client/user/user_test.go
index cc4fafca9..055b1f7a4 100644
--- a/internal/api/client/user/user_test.go
+++ b/internal/api/client/user/user_test.go
@@ -73,7 +73,7 @@ func (suite *UserStandardTestSuite) SetupTest() {
suite.sentEmails = make(map[string]string)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.userModule = user.New(suite.processor).(*user.Module)
+ suite.userModule = user.New(suite.processor)
testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/fileserver.go b/internal/api/fileserver.go
new file mode 100644
index 000000000..8784a8663
--- /dev/null
+++ b/internal/api/fileserver.go
@@ -0,0 +1,55 @@
+/*
+ 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/fileserver"
+ "github.com/superseriousbusiness/gotosocial/internal/middleware"
+ "github.com/superseriousbusiness/gotosocial/internal/processing"
+ "github.com/superseriousbusiness/gotosocial/internal/router"
+)
+
+type Fileserver struct {
+ fileserver *fileserver.Module
+}
+
+func (f *Fileserver) Route(r router.Router) {
+ fileserverGroup := r.AttachGroup("fileserver")
+
+ // attach middlewares appropriate for this group
+ fileserverGroup.Use(
+ middleware.RateLimit(),
+ // Since we'll never host different files at the same
+ // URL (bc the ULIDs are generated per piece of media),
+ // it's sensible and safe to use a long cache here, so
+ // that clients don't keep fetching files over + over again.
+ //
+ // Nevertheless, we should use 'private' to indicate
+ // that there might be media in there which are gated by ACLs.
+ middleware.CacheControl("private", "max-age=604800"),
+ )
+
+ f.fileserver.Route(fileserverGroup.Handle)
+}
+
+func NewFileserver(p processing.Processor) *Fileserver {
+ return &Fileserver{
+ fileserver: fileserver.New(p),
+ }
+}
diff --git a/internal/api/client/fileserver/fileserver.go b/internal/api/fileserver/fileserver.go
index dcb54f986..bbfd7d200 100644
--- a/internal/api/client/fileserver/fileserver.go
+++ b/internal/api/fileserver/fileserver.go
@@ -19,18 +19,13 @@
package fileserver
import (
- "fmt"
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
- "github.com/superseriousbusiness/gotosocial/internal/uris"
)
const (
- // FileServeBasePath forms the first part of the fileserver path.
- FileServeBasePath = "/" + uris.FileserverPath
// AccountIDKey is the url key for account id (an account ulid)
AccountIDKey = "account_id"
// MediaTypeKey is the url key for media type (usually something like attachment or header etc)
@@ -39,26 +34,21 @@ const (
MediaSizeKey = "media_size"
// FileNameKey is the actual filename being sought. Will usually be a UUID then something like .jpeg
FileNameKey = "file_name"
+ // FileServePath is the fileserve path minus the 'fileserver' prefix.
+ FileServePath = "/:" + AccountIDKey + "/:" + MediaTypeKey + "/:" + MediaSizeKey + "/:" + FileNameKey
)
-// FileServer implements the RESTAPIModule interface.
-// The goal here is to serve requested media files if the gotosocial server is configured to use local storage.
-type FileServer struct {
+type Module struct {
processor processing.Processor
}
-// New returns a new fileServer module
-func New(processor processing.Processor) api.ClientModule {
- return &FileServer{
+func New(processor processing.Processor) *Module {
+ return &Module{
processor: processor,
}
}
-// Route satisfies the RESTAPIModule interface
-func (m *FileServer) Route(s router.Router) error {
- // something like "/fileserver/:account_id/:media_type/:media_size/:file_name"
- fileServePath := fmt.Sprintf("%s/:%s/:%s/:%s/:%s", FileServeBasePath, AccountIDKey, MediaTypeKey, MediaSizeKey, FileNameKey)
- s.AttachHandler(http.MethodGet, fileServePath, m.ServeFile)
- s.AttachHandler(http.MethodHead, fileServePath, m.ServeFile)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, FileServePath, m.ServeFile)
+ attachHandler(http.MethodHead, FileServePath, m.ServeFile)
}
diff --git a/internal/api/client/fileserver/fileserver_test.go b/internal/api/fileserver/fileserver_test.go
index f1fab5672..f6de72a27 100644
--- a/internal/api/client/fileserver/fileserver_test.go
+++ b/internal/api/fileserver/fileserver_test.go
@@ -22,7 +22,7 @@ import (
"context"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver"
+ "github.com/superseriousbusiness/gotosocial/internal/api/fileserver"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
@@ -59,7 +59,7 @@ type FileserverTestSuite struct {
testAttachments map[string]*gtsmodel.MediaAttachment
// item being tested
- fileServer *fileserver.FileServer
+ fileServer *fileserver.Module
}
/*
@@ -75,20 +75,20 @@ func (suite *FileserverTestSuite) SetupSuite() {
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewInMemoryStorage()
- suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
- suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
+ suite.emailSender = testrig.NewEmailSender("../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, testrig.NewTestMediaManager(suite.db, suite.storage), clientWorker, fedWorker)
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.mediaManager = testrig.NewTestMediaManager(suite.db, suite.storage)
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
- suite.fileServer = fileserver.New(suite.processor).(*fileserver.FileServer)
+ suite.fileServer = fileserver.New(suite.processor)
}
func (suite *FileserverTestSuite) SetupTest() {
testrig.StandardDBSetup(suite.db, nil)
- testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
+ testrig.StandardStorageSetup(suite.storage, "../../../testrig/media")
suite.testTokens = testrig.NewTestTokens()
suite.testClients = testrig.NewTestClients()
suite.testApplications = testrig.NewTestApplications()
diff --git a/internal/api/client/fileserver/servefile.go b/internal/api/fileserver/servefile.go
index d2328a5fc..08ab3110b 100644
--- a/internal/api/client/fileserver/servefile.go
+++ b/internal/api/fileserver/servefile.go
@@ -26,8 +26,8 @@ import (
"strconv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -37,10 +37,10 @@ import (
//
// Note: to mitigate scraping attempts, no information should be given out on a bad request except "404 page not found".
// Don't give away account ids or media ids or anything like that; callers shouldn't be able to infer anything.
-func (m *FileServer) ServeFile(c *gin.Context) {
+func (m *Module) ServeFile(c *gin.Context) {
authed, err := oauth.Authed(c, false, false, false, false)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
return
}
@@ -50,39 +50,39 @@ func (m *FileServer) ServeFile(c *gin.Context) {
accountID := c.Param(AccountIDKey)
if accountID == "" {
err := fmt.Errorf("missing %s from request", AccountIDKey)
- api.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
return
}
mediaType := c.Param(MediaTypeKey)
if mediaType == "" {
err := fmt.Errorf("missing %s from request", MediaTypeKey)
- api.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
return
}
mediaSize := c.Param(MediaSizeKey)
if mediaSize == "" {
err := fmt.Errorf("missing %s from request", MediaSizeKey)
- api.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
return
}
fileName := c.Param(FileNameKey)
if fileName == "" {
err := fmt.Errorf("missing %s from request", FileNameKey)
- api.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err), m.processor.InstanceGet)
return
}
- content, errWithCode := m.processor.FileGet(c.Request.Context(), authed, &model.GetContentRequestForm{
+ content, errWithCode := m.processor.FileGet(c.Request.Context(), authed, &apimodel.GetContentRequestForm{
AccountID: accountID,
MediaType: mediaType,
MediaSize: mediaSize,
FileName: fileName,
})
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
@@ -103,18 +103,13 @@ func (m *FileServer) ServeFile(c *gin.Context) {
// TODO: if the requester only accepts text/html we should try to serve them *something*.
// This is mostly needed because when sharing a link to a gts-hosted file on something like mastodon, the masto servers will
// attempt to look up the content to provide a preview of the link, and they ask for text/html.
- format, err := api.NegotiateAccept(c, api.MIME(content.ContentType))
+ format, err := apiutil.NegotiateAccept(c, apiutil.MIME(content.ContentType))
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
}
- // since we'll never host different files at the same
- // URL (bc the ULIDs are generated per piece of media),
- // it's sensible and safe to use a long cache here, so
- // that clients don't keep fetching files over + over again
- c.Header("Cache-Control", "max-age=604800")
-
+ // if this is a head request, just return info + throw the reader away
if c.Request.Method == http.MethodHead {
c.Header("Content-Type", format)
c.Header("Content-Length", strconv.FormatInt(content.ContentLength, 10))
@@ -126,7 +121,7 @@ func (m *FileServer) ServeFile(c *gin.Context) {
b := bytes.NewBuffer(make([]byte, 0, 64))
if _, err := io.CopyN(b, content.Content, 64); err != nil {
err = fmt.Errorf("ServeFile: error reading from content: %w", err)
- api.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet)
return
}
diff --git a/internal/api/client/fileserver/servefile_test.go b/internal/api/fileserver/servefile_test.go
index 1ca0c60d6..efaf3896d 100644
--- a/internal/api/client/fileserver/servefile_test.go
+++ b/internal/api/fileserver/servefile_test.go
@@ -26,7 +26,7 @@ import (
"testing"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver"
+ "github.com/superseriousbusiness/gotosocial/internal/api/fileserver"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/testrig"
diff --git a/internal/api/nodeinfo.go b/internal/api/nodeinfo.go
new file mode 100644
index 000000000..906703434
--- /dev/null
+++ b/internal/api/nodeinfo.go
@@ -0,0 +1,50 @@
+/*
+ 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/nodeinfo"
+ "github.com/superseriousbusiness/gotosocial/internal/middleware"
+ "github.com/superseriousbusiness/gotosocial/internal/processing"
+ "github.com/superseriousbusiness/gotosocial/internal/router"
+)
+
+type NodeInfo struct {
+ nodeInfo *nodeinfo.Module
+}
+
+func (w *NodeInfo) Route(r router.Router) {
+ // group nodeinfo endpoints together
+ nodeInfoGroup := r.AttachGroup("nodeinfo")
+
+ // attach middlewares appropriate for this group
+ nodeInfoGroup.Use(
+ middleware.Gzip(),
+ middleware.RateLimit(),
+ middleware.CacheControl("public", "max-age=120"), // allow cache for 2 minutes
+ )
+
+ w.nodeInfo.Route(nodeInfoGroup.Handle)
+}
+
+func NewNodeInfo(p processing.Processor) *NodeInfo {
+ return &NodeInfo{
+ nodeInfo: nodeinfo.New(p),
+ }
+}
diff --git a/internal/api/nodeinfo/nodeinfo.go b/internal/api/nodeinfo/nodeinfo.go
new file mode 100644
index 000000000..aa6aec882
--- /dev/null
+++ b/internal/api/nodeinfo/nodeinfo.go
@@ -0,0 +1,46 @@
+/*
+ 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 nodeinfo
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+ "github.com/superseriousbusiness/gotosocial/internal/processing"
+)
+
+const (
+ NodeInfo2Version = "2.0"
+ NodeInfo2Path = "/" + NodeInfo2Version
+ NodeInfo2ContentType = "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/" + NodeInfo2Version + "#\""
+)
+
+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, NodeInfo2Path, m.NodeInfo2GETHandler)
+}
diff --git a/internal/api/s2s/nodeinfo/nodeinfoget.go b/internal/api/nodeinfo/nodeinfoget.go
index 6cb5e1ebf..f2a3604eb 100644
--- a/internal/api/s2s/nodeinfo/nodeinfoget.go
+++ b/internal/api/nodeinfo/nodeinfoget.go
@@ -23,11 +23,11 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
-// NodeInfoGETHandler swagger:operation GET /nodeinfo/2.0 nodeInfoGet
+// NodeInfo2GETHandler swagger:operation GET /nodeinfo/2.0 nodeInfoGet
//
// Returns a compliant nodeinfo response to node info queries.
//
@@ -44,23 +44,23 @@ import (
// '200':
// schema:
// "$ref": "#/definitions/nodeinfo"
-func (m *Module) NodeInfoGETHandler(c *gin.Context) {
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+func (m *Module) NodeInfo2GETHandler(c *gin.Context) {
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- ni, errWithCode := m.processor.GetNodeInfo(c.Request.Context(), c.Request)
+ nodeInfo, errWithCode := m.processor.GetNodeInfo(c.Request.Context())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
- b, err := json.Marshal(ni)
+ b, err := json.Marshal(nodeInfo)
if err != nil {
- api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
return
}
- c.Data(http.StatusOK, `application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"`, b)
+ c.Data(http.StatusOK, NodeInfo2ContentType, b)
}
diff --git a/internal/api/s2s/emoji/emoji.go b/internal/api/s2s/emoji/emoji.go
deleted file mode 100644
index d448d2105..000000000
--- a/internal/api/s2s/emoji/emoji.go
+++ /dev/null
@@ -1,53 +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 emoji
-
-import (
- "net/http"
-
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
- "github.com/superseriousbusiness/gotosocial/internal/uris"
-)
-
-const (
- // EmojiIDKey is for emoji IDs
- EmojiIDKey = "id"
- // EmojiBasePath is the base path for serving information about Emojis eg https://example.org/emoji
- EmojiWithIDPath = "/" + uris.EmojiPath + "/:" + EmojiIDKey
-)
-
-// Module implements the FederationModule interface
-type Module struct {
- processor processing.Processor
-}
-
-// New returns a emoji module
-func New(processor processing.Processor) api.FederationModule {
- return &Module{
- processor: processor,
- }
-}
-
-// Route satisfies the RESTAPIModule interface
-func (m *Module) Route(s router.Router) error {
- s.AttachHandler(http.MethodGet, EmojiWithIDPath, m.EmojiGetHandler)
- return nil
-}
diff --git a/internal/api/s2s/nodeinfo/nodeinfo.go b/internal/api/s2s/nodeinfo/nodeinfo.go
deleted file mode 100644
index 539dcc2d1..000000000
--- a/internal/api/s2s/nodeinfo/nodeinfo.go
+++ /dev/null
@@ -1,53 +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 nodeinfo
-
-import (
- "net/http"
-
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
-)
-
-const (
- // NodeInfoWellKnownPath is the base path for serving responses to nodeinfo lookup requests.
- NodeInfoWellKnownPath = ".well-known/nodeinfo"
- // NodeInfoBasePath is the path for serving nodeinfo responses.
- NodeInfoBasePath = "/nodeinfo/2.0"
-)
-
-// Module implements the FederationModule interface
-type Module struct {
- processor processing.Processor
-}
-
-// New returns a new nodeinfo module
-func New(processor processing.Processor) api.FederationModule {
- return &Module{
- processor: processor,
- }
-}
-
-// Route satisfies the FederationModule interface
-func (m *Module) Route(s router.Router) error {
- s.AttachHandler(http.MethodGet, NodeInfoWellKnownPath, m.NodeInfoWellKnownGETHandler)
- s.AttachHandler(http.MethodGet, NodeInfoBasePath, m.NodeInfoGETHandler)
- return nil
-}
diff --git a/internal/api/s2s/user/user.go b/internal/api/s2s/user/user.go
deleted file mode 100644
index 1daa53ad8..000000000
--- a/internal/api/s2s/user/user.go
+++ /dev/null
@@ -1,89 +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 user
-
-import (
- "net/http"
-
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
- "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"
-
- // UsersBasePath is the base path for serving information about Users eg https://example.org/users
- UsersBasePath = "/" + uris.UsersPath
- // UsersBasePathWithUsername is just the users base path with the Username key in it.
- // Use this anywhere you need to know the username of the user being queried.
- // Eg https://example.org/users/:username
- UsersBasePathWithUsername = UsersBasePath + "/:" + UsernameKey
- // UsersPublicKeyPath is a path to a user's public key, for serving bare minimum AP representations.
- UsersPublicKeyPath = UsersBasePathWithUsername + "/" + uris.PublicKeyPath
- // UsersInboxPath is for serving POST requests to a user's inbox with the given username key.
- UsersInboxPath = UsersBasePathWithUsername + "/" + uris.InboxPath
- // UsersOutboxPath is for serving GET requests to a user's outbox with the given username key.
- UsersOutboxPath = UsersBasePathWithUsername + "/" + uris.OutboxPath
- // UsersFollowersPath is for serving GET request's to a user's followers list, with the given username key.
- UsersFollowersPath = UsersBasePathWithUsername + "/" + uris.FollowersPath
- // UsersFollowingPath is for serving GET request's to a user's following list, with the given username key.
- UsersFollowingPath = UsersBasePathWithUsername + "/" + uris.FollowingPath
- // UsersStatusPath is for serving GET requests to a particular status by a user, with the given username key and status ID
- UsersStatusPath = UsersBasePathWithUsername + "/" + uris.StatusesPath + "/:" + StatusIDKey
- // UsersStatusRepliesPath is for serving the replies collection of a status.
- UsersStatusRepliesPath = UsersStatusPath + "/replies"
-)
-
-// Module implements the FederationAPIModule interface
-type Module struct {
- processor processing.Processor
-}
-
-// New returns a new auth module
-func New(processor processing.Processor) api.FederationModule {
- return &Module{
- processor: processor,
- }
-}
-
-// Route satisfies the RESTAPIModule interface
-func (m *Module) Route(s router.Router) error {
- s.AttachHandler(http.MethodGet, UsersBasePathWithUsername, m.UsersGETHandler)
- s.AttachHandler(http.MethodPost, UsersInboxPath, m.InboxPOSTHandler)
- s.AttachHandler(http.MethodGet, UsersFollowersPath, m.FollowersGETHandler)
- s.AttachHandler(http.MethodGet, UsersFollowingPath, m.FollowingGETHandler)
- s.AttachHandler(http.MethodGet, UsersStatusPath, m.StatusGETHandler)
- s.AttachHandler(http.MethodGet, UsersPublicKeyPath, m.PublicKeyGETHandler)
- s.AttachHandler(http.MethodGet, UsersStatusRepliesPath, m.StatusRepliesGETHandler)
- s.AttachHandler(http.MethodGet, UsersOutboxPath, m.OutboxGETHandler)
- return nil
-}
diff --git a/internal/api/security/extraheaders.go b/internal/api/security/extraheaders.go
deleted file mode 100644
index f66ac43b4..000000000
--- a/internal/api/security/extraheaders.go
+++ /dev/null
@@ -1,26 +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 security
-
-import "github.com/gin-gonic/gin"
-
-// ExtraHeaders adds any additional required headers to the response
-func (m *Module) ExtraHeaders(c *gin.Context) {
- c.Header("Server", "gotosocial")
-}
diff --git a/internal/api/security/flocblock.go b/internal/api/security/flocblock.go
deleted file mode 100644
index 0b61f4ef5..000000000
--- a/internal/api/security/flocblock.go
+++ /dev/null
@@ -1,31 +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 security
-
-import "github.com/gin-gonic/gin"
-
-// FlocBlock is a middleware that prevents google chrome cohort tracking by
-// writing the Permissions-Policy header after all other parts of the request
-// have been completed. Floc was replaced by Topics in 2022 and the spec says
-// that interest-cohort will also block Topics (as of 2022-Nov).
-// See: https://smartframe.io/blog/google-topics-api-everything-you-need-to-know
-// See: https://github.com/patcg-individual-drafts/topics
-func (m *Module) FlocBlock(c *gin.Context) {
- c.Header("Permissions-Policy", "browsing-topics=()")
-}
diff --git a/internal/api/security/ratelimit.go b/internal/api/security/ratelimit.go
deleted file mode 100644
index 3c0d078c7..000000000
--- a/internal/api/security/ratelimit.go
+++ /dev/null
@@ -1,70 +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 security
-
-import (
- "net"
- "net/http"
- "time"
-
- "github.com/gin-gonic/gin"
- limiter "github.com/ulule/limiter/v3"
- mgin "github.com/ulule/limiter/v3/drivers/middleware/gin"
- memory "github.com/ulule/limiter/v3/drivers/store/memory"
-)
-
-type RateLimitOptions struct {
- Period time.Duration
- Limit int64
-}
-
-func (m *Module) LimitReachedHandler(c *gin.Context) {
- code := http.StatusTooManyRequests
- c.AbortWithStatusJSON(code, gin.H{"error": "rate limit reached"})
-}
-
-// returns a gin middleware that will automatically rate limit caller (by IP address)
-// and enrich the response header with the following headers:
-// - `x-ratelimit-limit` maximum number of requests allowed per time period (fixed)
-// - `x-ratelimit-remaining` number of remaining requests that can still be performed
-// - `x-ratelimit-reset` unix timestamp when the rate limit will reset
-// if `x-ratelimit-limit` is exceeded an HTTP 429 error is returned
-func (m *Module) RateLimit(rateOptions RateLimitOptions) func(c *gin.Context) {
- rate := limiter.Rate{
- Period: rateOptions.Period,
- Limit: rateOptions.Limit,
- }
-
- store := memory.NewStore()
-
- limiterInstance := limiter.New(
- store,
- rate,
- // apply /64 mask to IPv6 addresses
- limiter.WithIPv6Mask(net.CIDRMask(64, 128)),
- )
-
- middleware := mgin.NewMiddleware(
- limiterInstance,
- // use custom rate limit reached error
- mgin.WithLimitReachedHandler(m.LimitReachedHandler),
- )
-
- return middleware
-}
diff --git a/internal/api/security/security.go b/internal/api/security/security.go
deleted file mode 100644
index 1dce111d3..000000000
--- a/internal/api/security/security.go
+++ /dev/null
@@ -1,65 +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 security
-
-import (
- "net/http"
- "time"
-
- "github.com/superseriousbusiness/gotosocial/internal/api"
- "github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
- "github.com/superseriousbusiness/gotosocial/internal/router"
-)
-
-const robotsPath = "/robots.txt"
-
-// Module implements the ClientAPIModule interface for security middleware
-type Module struct {
- db db.DB
- server oauth.Server
-}
-
-// New returns a new security module
-func New(db db.DB, server oauth.Server) api.ClientModule {
- return &Module{
- db: db,
- server: server,
- }
-}
-
-// Route attaches security middleware to the given router
-func (m *Module) Route(s router.Router) error {
- // only enable rate limit middleware if configured
- // advanced-rate-limit-requests is greater than 0
- if rateLimitRequests := config.GetAdvancedRateLimitRequests(); rateLimitRequests > 0 {
- s.AttachMiddleware(m.RateLimit(RateLimitOptions{
- Period: 5 * time.Minute,
- Limit: int64(rateLimitRequests),
- }))
- }
- s.AttachMiddleware(m.SignatureCheck)
- s.AttachMiddleware(m.FlocBlock)
- s.AttachMiddleware(m.ExtraHeaders)
- s.AttachMiddleware(m.UserAgentBlock)
- s.AttachMiddleware(m.TokenCheck)
- s.AttachHandler(http.MethodGet, robotsPath, m.RobotsGETHandler)
- return nil
-}
diff --git a/internal/api/security/signaturecheck.go b/internal/api/security/signaturecheck.go
deleted file mode 100644
index 1c117cd1b..000000000
--- a/internal/api/security/signaturecheck.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package security
-
-import (
- "net/http"
- "net/url"
-
- "github.com/superseriousbusiness/gotosocial/internal/ap"
- "github.com/superseriousbusiness/gotosocial/internal/log"
-
- "github.com/gin-gonic/gin"
- "github.com/go-fed/httpsig"
-)
-
-// SignatureCheck checks whether an incoming http request has been signed. If so, it will check if the domain
-// that signed the request is permitted to access the server. If it is permitted, the handler will set the key
-// verifier and the signature in the gin context for use down the line.
-func (m *Module) SignatureCheck(c *gin.Context) {
- // create the verifier from the request
- // if the request is signed, it will have a signature header
- verifier, err := httpsig.NewVerifier(c.Request)
- if err == nil {
- // the request was signed!
-
- // The key ID should be given in the signature so that we know where to fetch it from the remote server.
- // This will be something like https://example.org/users/whatever_requesting_user#main-key
- requestingPublicKeyID, err := url.Parse(verifier.KeyId())
- if err == nil && requestingPublicKeyID != nil {
- // we managed to parse the url!
-
- // if the domain is blocked we want to bail as early as possible
- blocked, err := m.db.IsURIBlocked(c.Request.Context(), requestingPublicKeyID)
- if err != nil {
- log.Errorf("could not tell if domain %s was blocked or not: %s", requestingPublicKeyID.Host, err)
- c.AbortWithStatus(http.StatusInternalServerError)
- return
- }
- if blocked {
- log.Infof("domain %s is blocked", requestingPublicKeyID.Host)
- c.AbortWithStatus(http.StatusForbidden)
- return
- }
-
- // set the verifier and signature on the context here to save some work further down the line
- c.Set(string(ap.ContextRequestingPublicKeyVerifier), verifier)
- signature := c.GetHeader("Signature")
- if signature != "" {
- c.Set(string(ap.ContextRequestingPublicKeySignature), signature)
- }
- }
- }
-}
diff --git a/internal/api/security/tokencheck.go b/internal/api/security/tokencheck.go
deleted file mode 100644
index 9f2b7f36e..000000000
--- a/internal/api/security/tokencheck.go
+++ /dev/null
@@ -1,120 +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 security
-
-import (
- "github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/log"
- "github.com/superseriousbusiness/gotosocial/internal/oauth"
-)
-
-// TokenCheck checks if the client has presented a valid oauth Bearer token.
-// If so, it will check the User that the token belongs to, and set that in the context of
-// the request. Then, it will look up the account for that user, and set that in the request too.
-// If user or account can't be found, then the handler won't *fail*, in case the server wants to allow
-// public requests that don't have a Bearer token set (eg., for public instance information and so on).
-func (m *Module) TokenCheck(c *gin.Context) {
- ctx := c.Request.Context()
- defer c.Next()
-
- if c.Request.Header.Get("Authorization") == "" {
- // no token set in the header, we can just bail
- return
- }
-
- ti, err := m.server.ValidationBearerToken(c.Copy().Request)
- if err != nil {
- log.Infof("token was passed in Authorization header but we could not validate it: %s", err)
- return
- }
- c.Set(oauth.SessionAuthorizedToken, ti)
-
- // check for user-level token
- if userID := ti.GetUserID(); userID != "" {
- log.Tracef("authenticated user %s with bearer token, scope is %s", userID, ti.GetScope())
-
- // fetch user for this token
- user, err := m.db.GetUserByID(ctx, userID)
- if err != nil {
- if err != db.ErrNoEntries {
- log.Errorf("database error looking for user with id %s: %s", userID, err)
- return
- }
- log.Warnf("no user found for userID %s", userID)
- return
- }
-
- if user.ConfirmedAt.IsZero() {
- log.Warnf("authenticated user %s has never confirmed thier email address", userID)
- return
- }
-
- if !*user.Approved {
- log.Warnf("authenticated user %s's account was never approved by an admin", userID)
- return
- }
-
- if *user.Disabled {
- log.Warnf("authenticated user %s's account was disabled'", userID)
- return
- }
-
- c.Set(oauth.SessionAuthorizedUser, user)
-
- // fetch account for this token
- if user.Account == nil {
- acct, err := m.db.GetAccountByID(ctx, user.AccountID)
- if err != nil {
- if err != db.ErrNoEntries {
- log.Errorf("database error looking for account with id %s: %s", user.AccountID, err)
- return
- }
- log.Warnf("no account found for userID %s", userID)
- return
- }
- user.Account = acct
- }
-
- if !user.Account.SuspendedAt.IsZero() {
- log.Warnf("authenticated user %s's account (accountId=%s) has been suspended", userID, user.AccountID)
- return
- }
-
- c.Set(oauth.SessionAuthorizedAccount, user.Account)
- }
-
- // check for application token
- if clientID := ti.GetClientID(); clientID != "" {
- log.Tracef("authenticated client %s with bearer token, scope is %s", clientID, ti.GetScope())
-
- // fetch app for this token
- app := &gtsmodel.Application{}
- if err := m.db.GetWhere(ctx, []db.Where{{Key: "client_id", Value: clientID}}, app); err != nil {
- if err != db.ErrNoEntries {
- log.Errorf("database error looking for application with clientID %s: %s", clientID, err)
- return
- }
- log.Warnf("no app found for client %s", clientID)
- return
- }
- c.Set(oauth.SessionAuthorizedApplication, app)
- }
-}
diff --git a/internal/api/errorhandling.go b/internal/api/util/errorhandling.go
index f6fec4168..97894dc36 100644
--- a/internal/api/errorhandling.go
+++ b/internal/api/util/errorhandling.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package api
+package util
import (
"context"
diff --git a/internal/api/mime.go b/internal/api/util/mime.go
index d02b64ab0..396a6f9b3 100644
--- a/internal/api/mime.go
+++ b/internal/api/util/mime.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package api
+package util
// MIME represents a mime-type.
type MIME string
diff --git a/internal/api/negotiate.go b/internal/api/util/negotiate.go
index cf44f4328..2854ea09c 100644
--- a/internal/api/negotiate.go
+++ b/internal/api/util/negotiate.go
@@ -16,7 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package api
+package util
import (
"errors"
diff --git a/internal/api/client/auth/util.go b/internal/api/util/signaturectx.go
index d59983c55..ec7d2c816 100644
--- a/internal/api/client/auth/util.go
+++ b/internal/api/util/signaturectx.go
@@ -16,16 +16,26 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package auth
+package util
import (
- "github.com/gin-contrib/sessions"
+ "context"
+
+ "github.com/gin-gonic/gin"
+ "github.com/superseriousbusiness/gotosocial/internal/ap"
)
-func (m *Module) clearSession(s sessions.Session) {
- s.Clear()
+// TransferSignatureContext transfers a signature verifier and signature from a gin context to a go context.
+func TransferSignatureContext(c *gin.Context) context.Context {
+ ctx := c.Request.Context()
+
+ if verifier, signed := c.Get(string(ap.ContextRequestingPublicKeyVerifier)); signed {
+ ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeyVerifier, verifier)
+ }
- if err := s.Save(); err != nil {
- panic(err)
+ if signature, signed := c.Get(string(ap.ContextRequestingPublicKeySignature)); signed {
+ ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeySignature, signature)
}
+
+ return ctx
}
diff --git a/internal/api/wellknown.go b/internal/api/wellknown.go
new file mode 100644
index 000000000..e4fe15f94
--- /dev/null
+++ b/internal/api/wellknown.go
@@ -0,0 +1,55 @@
+/*
+ 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/wellknown/nodeinfo"
+ "github.com/superseriousbusiness/gotosocial/internal/api/wellknown/webfinger"
+ "github.com/superseriousbusiness/gotosocial/internal/middleware"
+ "github.com/superseriousbusiness/gotosocial/internal/processing"
+ "github.com/superseriousbusiness/gotosocial/internal/router"
+)
+
+type WellKnown struct {
+ nodeInfo *nodeinfo.Module
+ webfinger *webfinger.Module
+}
+
+func (w *WellKnown) Route(r router.Router) {
+ // group .well-known endpoints together
+ wellKnownGroup := r.AttachGroup(".well-known")
+
+ // attach middlewares appropriate for this group
+ wellKnownGroup.Use(
+ middleware.Gzip(),
+ middleware.RateLimit(),
+ // allow .well-known responses to be cached for 2 minutes
+ middleware.CacheControl("public", "max-age=120"),
+ )
+
+ w.nodeInfo.Route(wellKnownGroup.Handle)
+ w.webfinger.Route(wellKnownGroup.Handle)
+}
+
+func NewWellKnown(p processing.Processor) *WellKnown {
+ return &WellKnown{
+ nodeInfo: nodeinfo.New(p),
+ webfinger: webfinger.New(p),
+ }
+}
diff --git a/internal/api/wellknown/nodeinfo/nodeinfo.go b/internal/api/wellknown/nodeinfo/nodeinfo.go
new file mode 100644
index 000000000..fd57ee08b
--- /dev/null
+++ b/internal/api/wellknown/nodeinfo/nodeinfo.go
@@ -0,0 +1,47 @@
+/*
+ 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 nodeinfo
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+ "github.com/superseriousbusiness/gotosocial/internal/processing"
+)
+
+const (
+ // NodeInfoWellKnownPath is the base path for serving responses
+ // to nodeinfo lookup requests, minus the '.well-known' prefix.
+ NodeInfoWellKnownPath = "/nodeinfo"
+)
+
+type Module struct {
+ processor processing.Processor
+}
+
+// New returns a new nodeinfo module
+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, NodeInfoWellKnownPath, m.NodeInfoWellKnownGETHandler)
+}
diff --git a/internal/api/s2s/nodeinfo/wellknownget.go b/internal/api/wellknown/nodeinfo/nodeinfoget.go
index dc14e43a3..21b29ebd9 100644
--- a/internal/api/s2s/nodeinfo/wellknownget.go
+++ b/internal/api/wellknown/nodeinfo/nodeinfoget.go
@@ -22,20 +22,20 @@ import (
"net/http"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
)
// NodeInfoWellKnownGETHandler swagger:operation GET /.well-known/nodeinfo nodeInfoWellKnownGet
//
-// Directs callers to /nodeinfo/2.0.
+// Returns a well-known response which redirects callers to `/nodeinfo/2.0`.
//
// eg. `{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"http://example.org/nodeinfo/2.0"}]}`
// See: https://nodeinfo.diaspora.software/protocol.html
//
// ---
// tags:
-// - nodeinfo
+// - .well-known
//
// produces:
// - application/json
@@ -45,16 +45,16 @@ import (
// schema:
// "$ref": "#/definitions/wellKnownResponse"
func (m *Module) NodeInfoWellKnownGETHandler(c *gin.Context) {
- if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
- api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
- niRel, errWithCode := m.processor.GetNodeInfoRel(c.Request.Context(), c.Request)
+ resp, errWithCode := m.processor.GetNodeInfoRel(c.Request.Context())
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
- c.JSON(http.StatusOK, niRel)
+ c.JSON(http.StatusOK, resp)
}
diff --git a/internal/api/s2s/webfinger/webfinger.go b/internal/api/wellknown/webfinger/webfinger.go
index c46ca7260..8d509b409 100644
--- a/internal/api/s2s/webfinger/webfinger.go
+++ b/internal/api/wellknown/webfinger/webfinger.go
@@ -21,30 +21,26 @@ package webfinger
import (
"net/http"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/processing"
- "github.com/superseriousbusiness/gotosocial/internal/router"
)
const (
- // WebfingerBasePath is the base path for serving webfinger lookup requests
- WebfingerBasePath = ".well-known/webfinger"
+ // WebfingerBasePath is the base path for serving webfinger
+ // lookup requests, minus the .well-known prefix
+ WebfingerBasePath = "/webfinger"
)
-// Module implements the FederationModule interface
type Module struct {
processor processing.Processor
}
-// New returns a new webfinger module
-func New(processor processing.Processor) api.FederationModule {
+func New(processor processing.Processor) *Module {
return &Module{
processor: processor,
}
}
-// Route satisfies the FederationModule interface
-func (m *Module) Route(s router.Router) error {
- s.AttachHandler(http.MethodGet, WebfingerBasePath, m.WebfingerGETRequest)
- return nil
+func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
+ attachHandler(http.MethodGet, WebfingerBasePath, m.WebfingerGETRequest)
}
diff --git a/internal/api/s2s/webfinger/webfinger_test.go b/internal/api/wellknown/webfinger/webfinger_test.go
index e5d026d06..bb9f82ccb 100644
--- a/internal/api/s2s/webfinger/webfinger_test.go
+++ b/internal/api/wellknown/webfinger/webfinger_test.go
@@ -25,8 +25,7 @@ import (
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/ap"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
- "github.com/superseriousbusiness/gotosocial/internal/api/security"
+ "github.com/superseriousbusiness/gotosocial/internal/api/wellknown/webfinger"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/email"
@@ -44,15 +43,14 @@ import (
type WebfingerStandardTestSuite struct {
// standard suite interfaces
suite.Suite
- db db.DB
- tc typeutils.TypeConverter
- mediaManager media.Manager
- federator federation.Federator
- emailSender email.Sender
- processor processing.Processor
- storage *storage.Driver
- oauthServer oauth.Server
- securityModule *security.Module
+ db db.DB
+ tc typeutils.TypeConverter
+ mediaManager media.Manager
+ federator federation.Federator
+ emailSender email.Sender
+ processor processing.Processor
+ storage *storage.Driver
+ oauthServer oauth.Server
// standard suite models
testTokens map[string]*gtsmodel.Token
@@ -91,9 +89,8 @@ func (suite *WebfingerStandardTestSuite) SetupTest() {
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
- suite.webfingerModule = webfinger.New(suite.processor).(*webfinger.Module)
+ suite.webfingerModule = webfinger.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.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/s2s/webfinger/webfingerget.go b/internal/api/wellknown/webfinger/webfingerget.go
index 9949140c1..72d011734 100644
--- a/internal/api/s2s/webfinger/webfingerget.go
+++ b/internal/api/wellknown/webfinger/webfingerget.go
@@ -19,16 +19,14 @@
package webfinger
import (
- "context"
+ "errors"
"fmt"
"net/http"
- "codeberg.org/gruf/go-kv"
"github.com/gin-gonic/gin"
- "github.com/superseriousbusiness/gotosocial/internal/ap"
- "github.com/superseriousbusiness/gotosocial/internal/api"
+ apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
"github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/log"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
@@ -48,7 +46,7 @@ import (
//
// ---
// tags:
-// - webfinger
+// - .well-known
//
// produces:
// - application/json
@@ -58,43 +56,34 @@ import (
// schema:
// "$ref": "#/definitions/wellKnownResponse"
func (m *Module) WebfingerGETRequest(c *gin.Context) {
- l := log.WithFields(kv.Fields{
- {K: "user-agent", V: c.Request.UserAgent()},
- }...)
+ if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
+ apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
+ return
+ }
resourceQuery, set := c.GetQuery("resource")
if !set || resourceQuery == "" {
- l.Debug("aborting request because no resource was set in query")
- c.JSON(http.StatusBadRequest, gin.H{"error": "no 'resource' in request query"})
+ err := errors.New("no 'resource' in request query")
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
requestedUsername, requestedHost, err := util.ExtractWebfingerParts(resourceQuery)
if err != nil {
- l.Debugf("bad webfinger request with resource query %s: %s", resourceQuery, err)
- c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("bad webfinger request with resource query %s", resourceQuery)})
+ err := fmt.Errorf("bad webfinger request with resource query %s: %w", resourceQuery, err)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- accountDomain := config.GetAccountDomain()
- host := config.GetHost()
-
- if requestedHost != host && requestedHost != accountDomain {
- l.Debugf("aborting request because requestedHost %s does not belong to this instance", requestedHost)
- c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("requested host %s does not belong to this instance", requestedHost)})
+ if requestedHost != config.GetHost() && requestedHost != config.GetAccountDomain() {
+ err := fmt.Errorf("requested host %s does not belong to this instance", requestedHost)
+ apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
return
}
- // transfer the signature verifier from the gin context to the request context
- ctx := c.Request.Context()
- verifier, signed := c.Get(string(ap.ContextRequestingPublicKeyVerifier))
- if signed {
- ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeyVerifier, verifier)
- }
-
- resp, errWithCode := m.processor.GetWebfingerAccount(ctx, requestedUsername)
+ resp, errWithCode := m.processor.GetWebfingerAccount(c.Request.Context(), requestedUsername)
if errWithCode != nil {
- api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
+ apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
diff --git a/internal/api/s2s/webfinger/webfingerget_test.go b/internal/api/wellknown/webfinger/webfingerget_test.go
index 3e91b8f6a..70099b930 100644
--- a/internal/api/s2s/webfinger/webfingerget_test.go
+++ b/internal/api/wellknown/webfinger/webfingerget_test.go
@@ -28,7 +28,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
- "github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
+ "github.com/superseriousbusiness/gotosocial/internal/api/wellknown/webfinger"
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/messages"
@@ -73,7 +73,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByHo
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
suite.processor = processing.NewProcessor(suite.tc, suite.federator, testrig.NewTestOauthServer(suite.db), testrig.NewTestMediaManager(suite.db, suite.storage), suite.storage, suite.db, suite.emailSender, clientWorker, fedWorker)
- suite.webfingerModule = webfinger.New(suite.processor).(*webfinger.Module)
+ suite.webfingerModule = webfinger.New(suite.processor)
targetAccount := accountDomainAccount()
if err := suite.db.Put(context.Background(), targetAccount); err != nil {
@@ -110,7 +110,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByAc
clientWorker := concurrency.NewWorkerPool[messages.FromClientAPI](-1, -1)
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
suite.processor = processing.NewProcessor(suite.tc, suite.federator, testrig.NewTestOauthServer(suite.db), testrig.NewTestMediaManager(suite.db, suite.storage), suite.storage, suite.db, suite.emailSender, clientWorker, fedWorker)
- suite.webfingerModule = webfinger.New(suite.processor).(*webfinger.Module)
+ suite.webfingerModule = webfinger.New(suite.processor)
targetAccount := accountDomainAccount()
if err := suite.db.Put(context.Background(), targetAccount); err != nil {