Skip to content

Commit

Permalink
Block/unblock (#96)
Browse files Browse the repository at this point in the history
* remote + local block logic, incl. federation

* improve blocking stuff

* fiddle with display of blocked profiles

* go fmt
  • Loading branch information
tsmethurst committed Jul 11, 2021
1 parent c7da649 commit 846057f
Show file tree
Hide file tree
Showing 45 changed files with 1,405 additions and 63 deletions.
8 changes: 4 additions & 4 deletions PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ Things are moving on the project! As of July 2021 you can now:
* [ ] /api/v1/accounts/:id/identity_proofs GET (Get identity proofs for this account)
* [x] /api/v1/accounts/:id/follow POST (Follow this account)
* [x] /api/v1/accounts/:id/unfollow POST (Unfollow this account)
* [ ] /api/v1/accounts/:id/block POST (Block this account)
* [ ] /api/v1/accounts/:id/unblock POST (Unblock this account)
* [x] /api/v1/accounts/:id/block POST (Block this account)
* [x] /api/v1/accounts/:id/unblock POST (Unblock this account)
* [ ] /api/v1/accounts/:id/mute POST (Mute this account)
* [ ] /api/v1/accounts/:id/unmute POST (Unmute this account)
* [ ] /api/v1/accounts/:id/pin POST (Feature this account on profile)
Expand All @@ -71,8 +71,8 @@ Things are moving on the project! As of July 2021 you can now:
* [x] /api/v1/favourites GET (See faved statuses)
* [ ] Mutes
* [ ] /api/v1/mutes GET (See list of muted accounts)
* [ ] Blocks
* [ ] /api/v1/blocks GET (See list of blocked accounts)
* [x] Blocks
* [x] /api/v1/blocks GET (See list of blocked accounts)
* [ ] Domain Blocks
* [x] /api/v1/domain_blocks GET (See list of domain blocks)
* [x] /api/v1/domain_blocks POST (Create a domain block)
Expand Down
34 changes: 28 additions & 6 deletions internal/api/client/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,14 @@ const (
GetFollowingPath = BasePathWithID + "/following"
// GetRelationshipsPath is for showing an account's relationship with other accounts
GetRelationshipsPath = BasePath + "/relationships"
// PostFollowPath is for POSTing new follows to, and updating existing follows
PostFollowPath = BasePathWithID + "/follow"
// PostUnfollowPath is for POSTing an unfollow
PostUnfollowPath = BasePathWithID + "/unfollow"
// FollowPath is for POSTing new follows to, and updating existing follows
FollowPath = BasePathWithID + "/follow"
// UnfollowPath is for POSTing an unfollow
UnfollowPath = BasePathWithID + "/unfollow"
// BlockPath is for creating a block of an account
BlockPath = BasePathWithID + "/block"
// UnblockPath is for removing a block of an account
UnblockPath = BasePathWithID + "/unblock"
)

// Module implements the ClientAPIModule interface for account-related actions
Expand All @@ -85,15 +89,33 @@ func New(config *config.Config, processor processing.Processor, log *logrus.Logg

// Route attaches all routes from this module to the given router
func (m *Module) Route(r router.Router) error {
// create account
r.AttachHandler(http.MethodPost, BasePath, m.AccountCreatePOSTHandler)

// get account
r.AttachHandler(http.MethodGet, BasePathWithID, m.muxHandler)

// modify account
r.AttachHandler(http.MethodPatch, BasePathWithID, m.muxHandler)

// get account's statuses
r.AttachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler)

// get following or followers
r.AttachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler)
r.AttachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler)

// get relationship with account
r.AttachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler)
r.AttachHandler(http.MethodPost, PostFollowPath, m.AccountFollowPOSTHandler)
r.AttachHandler(http.MethodPost, PostUnfollowPath, m.AccountUnfollowPOSTHandler)

// follow or unfollow account
r.AttachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler)
r.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
}

Expand Down
49 changes: 49 additions & 0 deletions internal/api/client/account/block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package account

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)

// AccountBlockPOSTHandler handles the creation of a block from the authed account targeting the given account ID.
func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}

targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
return
}

relationship, errWithCode := m.processor.AccountBlockCreate(authed, targetAcctID)
if errWithCode != nil {
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
return
}

c.JSON(http.StatusOK, relationship)
}
49 changes: 49 additions & 0 deletions internal/api/client/account/unblock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package account

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)

// AccountUnblockPOSTHandler handles the removal of a block from the authed account targeting the given account ID.
func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}

targetAcctID := c.Param(IDKey)
if targetAcctID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
return
}

relationship, errWithCode := m.processor.AccountBlockRemove(authed, targetAcctID)
if errWithCode != nil {
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
return
}

c.JSON(http.StatusOK, relationship)
}
63 changes: 63 additions & 0 deletions internal/api/client/blocks/blocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package blocks

import (
"net/http"

"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
"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"

// MaxIDKey is the url query for setting a max ID to return
MaxIDKey = "max_id"
// SinceIDKey is the url query for returning results newer than the given ID
SinceIDKey = "since_id"
// LimitKey is for specifying maximum number of results to return.
LimitKey = "limit"
)

// Module implements the ClientAPIModule interface for everything relating to viewing blocks
type Module struct {
config *config.Config
processor processing.Processor
log *logrus.Logger
}

// New returns a new blocks module
func New(config *config.Config, processor processing.Processor, log *logrus.Logger) api.ClientModule {
return &Module{
config: config,
processor: processor,
log: log,
}
}

// 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
}
75 changes: 75 additions & 0 deletions internal/api/client/blocks/blocksget.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package blocks

import (
"net/http"
"strconv"

"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)

// BlocksGETHandler handles GETting blocks.
func (m *Module) BlocksGETHandler(c *gin.Context) {
l := m.log.WithField("func", "PublicTimelineGETHandler")

authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
l.Debugf("error authing: %s", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}

maxID := ""
maxIDString := c.Query(MaxIDKey)
if maxIDString != "" {
maxID = maxIDString
}

sinceID := ""
sinceIDString := c.Query(SinceIDKey)
if sinceIDString != "" {
sinceID = sinceIDString
}

limit := 20
limitString := c.Query(LimitKey)
if limitString != "" {
i, err := strconv.ParseInt(limitString, 10, 64)
if err != nil {
l.Debugf("error parsing limit string: %s", err)
c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse limit query param"})
return
}
limit = int(i)
}

resp, errWithCode := m.processor.BlocksGet(authed, maxID, sinceID, limit)
if errWithCode != nil {
l.Debugf("error from processor BlocksGet: %s", errWithCode)
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
return
}

if resp.LinkHeader != "" {
c.Header("Link", resp.LinkHeader)
}
c.JSON(http.StatusOK, resp.Accounts)
}
14 changes: 7 additions & 7 deletions internal/api/client/streaming/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,23 @@ sendLoop:
select {
case m := <-stream.Messages:
// we've got a streaming message!!
l.Debug("received message from stream")
l.Trace("received message from stream")
if err := conn.WriteJSON(m); err != nil {
l.Infof("error writing json to websocket connection: %s", err)
l.Debugf("error writing json to websocket connection: %s", err)
// if something is wrong we want to bail and drop the connection -- the client will create a new one
break sendLoop
}
l.Debug("wrote message into websocket connection")
l.Trace("wrote message into websocket connection")
case <-t.C:
l.Debug("received TICK from ticker")
l.Trace("received TICK from ticker")
if err := conn.WriteMessage(websocket.PingMessage, []byte(": ping")); err != nil {
l.Infof("error writing ping to websocket connection: %s", err)
l.Debugf("error writing ping to websocket connection: %s", err)
// if something is wrong we want to bail and drop the connection -- the client will create a new one
break sendLoop
}
l.Debug("wrote ping message into websocket connection")
l.Trace("wrote ping message into websocket connection")
}
}

l.Debug("leaving StreamGETHandler")
l.Trace("leaving StreamGETHandler")
}
26 changes: 26 additions & 0 deletions internal/api/model/block.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package model

// BlocksResponse wraps a slice of accounts, ready to be serialized, along with the Link
// header for the previous and next queries, to be returned to the client.
type BlocksResponse struct {
Accounts []*Account
LinkHeader string
}
3 changes: 3 additions & 0 deletions internal/cliactions/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
"github.com/superseriousbusiness/gotosocial/internal/api/client/app"
"github.com/superseriousbusiness/gotosocial/internal/api/client/auth"
"github.com/superseriousbusiness/gotosocial/internal/api/client/blocks"
"github.com/superseriousbusiness/gotosocial/internal/api/client/emoji"
"github.com/superseriousbusiness/gotosocial/internal/api/client/favourites"
"github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver"
Expand Down Expand Up @@ -143,6 +144,7 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log
securityModule := security.New(c, dbService, log)
streamingModule := streaming.New(c, processor, log)
favouritesModule := favourites.New(c, processor, log)
blocksModule := blocks.New(c, processor, log)

apis := []api.ClientModule{
// modules with middleware go first
Expand Down Expand Up @@ -170,6 +172,7 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log
listsModule,
streamingModule,
favouritesModule,
blocksModule,
}

for _, m := range apis {
Expand Down

0 comments on commit 846057f

Please sign in to comment.