Enable stricter linting with golangci-lint (#316)

* update golangci-lint

* add golangci config file w/ more linters

* correct issues flagged by stricter linters

* add more generous timeout for golangci-lint

* add some style + formatting guidelines

* move timeout to config file

* go fmt
This commit is contained in:
tobi 2021-11-22 08:46:19 +01:00 committed by GitHub
parent 38d73f0316
commit f8630348b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 227 additions and 163 deletions

View file

@ -13,7 +13,7 @@ steps:
# We use golangci-lint for linting. # We use golangci-lint for linting.
# See: https://golangci-lint.run/ # See: https://golangci-lint.run/
- name: lint - name: lint
image: golangci/golangci-lint:v1.42.1 image: golangci/golangci-lint:v1.43.0
volumes: volumes:
- name: go-build-cache - name: go-build-cache
path: /root/.cache/go-build path: /root/.cache/go-build
@ -22,7 +22,7 @@ steps:
- name: go-src - name: go-src
path: /go path: /go
commands: commands:
- golangci-lint run --timeout 5m0s --tests=false --verbose - golangci-lint run
when: when:
event: event:
include: include:
@ -115,6 +115,6 @@ trigger:
--- ---
kind: signature kind: signature
hmac: aa44c4655421fb0ed3141b8d7255a9a240dd4312244081d9e95929c4a196fd92 hmac: 07d6ed18510f9591c6b347d6768cbe8e613561b3759f1a8dda8721d1d231a522
... ...

26
.golangci.yml Normal file
View file

@ -0,0 +1,26 @@
# Configuration file for golangci-lint linter.
# This will be automatically picked up when golangci-lint is invoked.
# For all config options, see https://golangci-lint.run/usage/configuration/#config-file
#
# For GoToSocial we mostly take the default linters, but we add a few to catch style issues as well.
# options for analysis running
run:
# include test files or not, default is true
tests: false
# timeout for analysis, e.g. 30s, 5m, default is 1m
timeout: 5m
linters:
# enable some extra linters, see here for the list: https://golangci-lint.run/usage/linters/
enable:
- contextcheck
- forcetypeassert
- goconst
- gocritic
- gofmt
- gosec
- ifshort
- nilerr
- revive
- wastedassign

View file

@ -21,7 +21,9 @@ Check the [issues](https://github.com/superseriousbusiness/gotosocial/issues) to
- [SQLite](#sqlite) - [SQLite](#sqlite)
- [Postgres](#postgres) - [Postgres](#postgres)
- [Both](#both) - [Both](#both)
- [Linting](#linting) - [Project Structure](#project-structure)
- [Style](#style)
- [Linting and Formatting](#linting-and-formatting)
- [Updating Swagger docs](#updating-swagger-docs) - [Updating Swagger docs](#updating-swagger-docs)
- [CI/CD configuration](#cicd-configuration) - [CI/CD configuration](#cicd-configuration)
- [Building releases and Docker containers](#building-releases-and-docker-containers) - [Building releases and Docker containers](#building-releases-and-docker-containers)
@ -179,33 +181,48 @@ Finally, to run tests against both database types one after the other, use:
./scripts/test.sh ./scripts/test.sh
``` ```
## Linting ## Project Structure
We use [golangci-lint](https://golangci-lint.run/) for linting. To run this locally, first install the linter following the instructions [here](https://golangci-lint.run/usage/install/#local-installation). For project structure, GoToSocial follows a standard and widely-accepted project layout [defined here](https://github.com/golang-standards/project-layout). As the author writes:
> This is a basic layout for Go application projects. It's not an official standard defined by the core Go dev team; however, it is a set of common historical and emerging project layout patterns in the Go ecosystem.
Most of the crucial business logic of the application is found inside the various packages and subpackages of the `internal` directory.
Where possible, we prefer more files and packages of shorter length that very clearly pertain to definable chunks of application logic, rather than fewer but longer files: if one `.go` file is pushing 1,000 lines of code, it's probably too long.
## Style
It is a good idea to read the short official [Effective Go](https://golang.org/doc/effective_go) page before submitting code: this document is the foundation of many a style guide, for good reason, and GoToSocial more or less follows its advice.
Another useful style guide that we try to follow: [this one](https://github.com/bahlo/go-styleguide).
In addition, here are some specific highlights from Uber's Go style guide which agree with what we try to do in GtS:
- [Group Similar Declarations](https://github.com/uber-go/guide/blob/master/style.md#group-similar-declarations).
- [Reduce Nesting](https://github.com/uber-go/guide/blob/master/style.md#reduce-nesting).
- [Unnecessary Else](https://github.com/uber-go/guide/blob/master/style.md#unnecessary-else).
- [Local Variable Declarations](https://github.com/uber-go/guide/blob/master/style.md#local-variable-declarations).
- [Reduce Scope of Variables](https://github.com/uber-go/guide/blob/master/style.md#reduce-scope-of-variables).
- [Initializing Structs](https://github.com/uber-go/guide/blob/master/style.md#initializing-structs).
## Linting and Formatting
Before you submit any code, make sure to run `go fmt ./...` to update whitespace and other opinionated formatting.
We use [golangci-lint](https://golangci-lint.run/) for linting, which allows us to catch style inconsistencies and potential bugs or security issues, using static code analysis.
If you make a PR that doesn't pass the linter, it will be rejected. As such, it's good practice to run the linter locally before pushing or opening a PR.
To do this, first install the linter following the instructions [here](https://golangci-lint.run/usage/install/#local-installation).
Then, you can run the linter with: Then, you can run the linter with:
```bash ```bash
golangci-lint run --tests=false golangci-lint run
``` ```
Note that this linter also runs as a job on the Github repo, so if you make a PR that doesn't pass the linter, it will be rejected. As such, it's good practice to run the linter locally before pushing or opening a PR. If there's no output, great! It passed :)
Another useful linter is [golint](https://pkg.go.dev/github.com/360EntSecGroup-Skylar/goreporter/linters/golint), which catches some style issues that golangci-lint does not.
To install golint, run:
```bash
go get -u github.com/golang/lint/golint
```
To run the linter, use:
```bash
golint ./internal/...
```
Then make sure to run `go fmt ./...` to update whitespace and other opinionated formatting.
## Updating Swagger docs ## Updating Swagger docs

View file

@ -49,8 +49,7 @@ func main() {
Commands: getCommands(), Commands: getCommands(),
} }
err := app.Run(os.Args) if err := app.Run(os.Args); err != nil {
if err != nil {
logrus.Fatal(err) logrus.Fatal(err)
} }
} }

View file

@ -20,14 +20,22 @@
import ( import (
"fmt" "fmt"
"github.com/sirupsen/logrus"
"net/http" "net/http"
"github.com/sirupsen/logrus"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
const (
// permitted length for most fields
formFieldLen = 64
// redirect can be a bit bigger because we probably need to encode data in the redirect uri
formRedirectLen = 512
)
// AppsPOSTHandler swagger:operation POST /api/v1/apps appCreate // AppsPOSTHandler swagger:operation POST /api/v1/apps appCreate
// //
// Register a new application on this instance. // Register a new application on this instance.
@ -79,11 +87,6 @@ func (m *Module) AppsPOSTHandler(c *gin.Context) {
return return
} }
// permitted length for most fields
formFieldLen := 64
// redirect can be a bit bigger because we probably need to encode data in the redirect uri
formRedirectLen := 512
// check lengths of fields before proceeding so the user can't spam huge entries into the database // check lengths of fields before proceeding so the user can't spam huge entries into the database
if len(form.ClientName) > formFieldLen { if len(form.ClientName) > formFieldLen {
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("client_name must be less than %d bytes", formFieldLen)}) c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf("client_name must be less than %d bytes", formFieldLen)})

View file

@ -29,6 +29,7 @@
"github.com/superseriousbusiness/gotosocial/internal/router" "github.com/superseriousbusiness/gotosocial/internal/router"
) )
/* #nosec G101 */
const ( const (
// AuthSignInPath is the API path for users to sign in through // AuthSignInPath is the API path for users to sign in through
AuthSignInPath = "/auth/sign_in" AuthSignInPath = "/auth/sign_in"

View file

@ -182,7 +182,7 @@ func (m *Module) parseUserFromClaims(ctx context.Context, claims *oidc.Claims, i
// //
// note that for the first iteration, iString is still "" when the check is made, so our first choice // note that for the first iteration, iString is still "" when the check is made, so our first choice
// is still the raw username with no integer stuck on the end // is still the raw username with no integer stuck on the end
for i := 1; !found; i = i + 1 { for i := 1; !found; i++ {
usernameAvailable, err := m.db.IsUsernameAvailable(ctx, username+iString) usernameAvailable, err := m.db.IsUsernameAvailable(ctx, username+iString)
if err != nil { if err != nil {
return nil, err return nil, err
@ -190,7 +190,7 @@ func (m *Module) parseUserFromClaims(ctx context.Context, claims *oidc.Claims, i
if usernameAvailable { if usernameAvailable {
// no error so we've found a username that works // no error so we've found a username that works
found = true found = true
username = username + iString username += iString
continue continue
} }
iString = strconv.Itoa(i) iString = strconv.Itoa(i)

View file

@ -19,10 +19,11 @@
package status package status
import ( import (
"github.com/sirupsen/logrus"
"net/http" "net/http"
"strings" "strings"
"github.com/sirupsen/logrus"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api" "github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
@ -110,13 +111,13 @@ func (m *Module) muxHandler(c *gin.Context) {
logrus.Debug("entering mux handler") logrus.Debug("entering mux handler")
ru := c.Request.RequestURI ru := c.Request.RequestURI
switch c.Request.Method { if c.Request.Method == http.MethodGet {
case http.MethodGet: switch {
if strings.HasPrefix(ru, ContextPath) { case strings.HasPrefix(ru, ContextPath):
// TODO // TODO
} else if strings.HasPrefix(ru, FavouritedPath) { case strings.HasPrefix(ru, FavouritedPath):
m.StatusFavedByGETHandler(c) m.StatusFavedByGETHandler(c)
} else { default:
m.StatusGETHandler(c) m.StatusGETHandler(c)
} }
} }

View file

@ -90,9 +90,8 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
return return
} }
page := false var page bool
pageString := c.Query(PageKey) if pageString := c.Query(PageKey); pageString != "" {
if pageString != "" {
i, err := strconv.ParseBool(pageString) i, err := strconv.ParseBool(pageString)
if err != nil { if err != nil {
l.Debugf("error parsing page string: %s", err) l.Debugf("error parsing page string: %s", err)

View file

@ -102,9 +102,8 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
return return
} }
page := false var page bool
pageString := c.Query(PageKey) if pageString := c.Query(PageKey); pageString != "" {
if pageString != "" {
i, err := strconv.ParseBool(pageString) i, err := strconv.ParseBool(pageString)
if err != nil { if err != nil {
l.Debugf("error parsing page string: %s", err) l.Debugf("error parsing page string: %s", err)

View file

@ -31,8 +31,7 @@ func (m *Module) UserAgentBlock(c *gin.Context) {
"func": "UserAgentBlock", "func": "UserAgentBlock",
}) })
ua := c.Request.UserAgent() if ua := c.Request.UserAgent(); ua == "" {
if ua == "" {
l.Debug("aborting request because there's no user-agent set") l.Debug("aborting request because there's no user-agent set")
c.AbortWithStatus(http.StatusTeapot) c.AbortWithStatus(http.StatusTeapot)
return return

View file

@ -4,6 +4,7 @@
"sync" "sync"
"github.com/ReneKroon/ttlcache" "github.com/ReneKroon/ttlcache"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
@ -26,7 +27,10 @@ func NewAccountCache() *AccountCache {
// Set callback to purge lookup maps on expiration // Set callback to purge lookup maps on expiration
c.cache.SetExpirationCallback(func(key string, value interface{}) { c.cache.SetExpirationCallback(func(key string, value interface{}) {
account := value.(*gtsmodel.Account) account, ok := value.(*gtsmodel.Account)
if !ok {
logrus.Panicf("AccountCache could not assert entry with key %s to *gtsmodel.Account", key)
}
c.mutex.Lock() c.mutex.Lock()
delete(c.urls, account.URL) delete(c.urls, account.URL)

View file

@ -4,6 +4,7 @@
"sync" "sync"
"github.com/ReneKroon/ttlcache" "github.com/ReneKroon/ttlcache"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
@ -26,7 +27,10 @@ func NewStatusCache() *StatusCache {
// Set callback to purge lookup maps on expiration // Set callback to purge lookup maps on expiration
c.cache.SetExpirationCallback(func(key string, value interface{}) { c.cache.SetExpirationCallback(func(key string, value interface{}) {
status := value.(*gtsmodel.Status) status, ok := value.(*gtsmodel.Status)
if !ok {
logrus.Panicf("StatusCache could not assert entry with key %s to *gtsmodel.Status", key)
}
c.mutex.Lock() c.mutex.Lock()
delete(c.urls, status.URL) delete(c.urls, status.URL)

View file

@ -46,4 +46,4 @@ type DBConfig struct {
var DBTLSModeRequire DBTLSMode = "require" var DBTLSModeRequire DBTLSMode = "require"
// DBTLSModeUnset means that the TLS mode has not been set. // DBTLSModeUnset means that the TLS mode has not been set.
var DBTLSModeUnset DBTLSMode = "" var DBTLSModeUnset DBTLSMode

View file

@ -137,8 +137,7 @@ func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gts
WhereGroup(" AND ", whereEmptyOrNull("domain")) WhereGroup(" AND ", whereEmptyOrNull("domain"))
} }
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, a.conn.ProcessError(err) return nil, a.conn.ProcessError(err)
} }
return account, nil return account, nil
@ -155,8 +154,7 @@ func (a *accountDB) GetAccountLastPosted(ctx context.Context, accountID string)
Where("account_id = ?", accountID). Where("account_id = ?", accountID).
Column("created_at") Column("created_at")
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return time.Time{}, a.conn.ProcessError(err) return time.Time{}, a.conn.ProcessError(err)
} }
return status.CreatedAt, nil return status.CreatedAt, nil
@ -168,11 +166,12 @@ func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachmen
} }
var headerOrAVI string var headerOrAVI string
if mediaAttachment.Avatar { switch {
case mediaAttachment.Avatar:
headerOrAVI = "avatar" headerOrAVI = "avatar"
} else if mediaAttachment.Header { case mediaAttachment.Header:
headerOrAVI = "header" headerOrAVI = "header"
} else { default:
return errors.New("given media attachment was neither a header nor an avatar") return errors.New("given media attachment was neither a header nor an avatar")
} }
@ -202,8 +201,7 @@ func (a *accountDB) GetLocalAccountByUsername(ctx context.Context, username stri
Where("username = ?", username). Where("username = ?", username).
WhereGroup(" AND ", whereEmptyOrNull("domain")) WhereGroup(" AND ", whereEmptyOrNull("domain"))
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, a.conn.ProcessError(err) return nil, a.conn.ProcessError(err)
} }
return account, nil return account, nil
@ -308,8 +306,7 @@ func (a *accountDB) GetAccountBlocks(ctx context.Context, accountID string, maxI
fq = fq.Limit(limit) fq = fq.Limit(limit)
} }
err := fq.Scan(ctx) if err := fq.Scan(ctx); err != nil {
if err != nil {
return nil, "", "", a.conn.ProcessError(err) return nil, "", "", a.conn.ProcessError(err)
} }

View file

@ -54,7 +54,7 @@
dbTypeSqlite = "sqlite" dbTypeSqlite = "sqlite"
) )
var registerTables []interface{} = []interface{}{ var registerTables = []interface{}{
&gtsmodel.StatusToEmoji{}, &gtsmodel.StatusToEmoji{},
&gtsmodel.StatusToTag{}, &gtsmodel.StatusToTag{},
} }
@ -305,6 +305,7 @@ func deriveBunDBPGOptions(c *config.Config) (*pgx.ConnConfig, error) {
case config.DBTLSModeDisable, config.DBTLSModeUnset: case config.DBTLSModeDisable, config.DBTLSModeUnset:
break // nothing to do break // nothing to do
case config.DBTLSModeEnable: case config.DBTLSModeEnable:
/* #nosec G402 */
tlsConfig = &tls.Config{ tlsConfig = &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
@ -312,6 +313,7 @@ func deriveBunDBPGOptions(c *config.Config) (*pgx.ConnConfig, error) {
tlsConfig = &tls.Config{ tlsConfig = &tls.Config{
InsecureSkipVerify: false, InsecureSkipVerify: false,
ServerName: c.DBConfig.Address, ServerName: c.DBConfig.Address,
MinVersion: tls.VersionTLS12,
} }
} }

View file

@ -116,8 +116,7 @@ func (i *instanceDB) GetInstanceAccounts(ctx context.Context, domain string, max
q = q.Limit(limit) q = q.Limit(limit)
} }
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, i.conn.ProcessError(err) return nil, i.conn.ProcessError(err)
} }
return accounts, nil return accounts, nil

View file

@ -45,8 +45,7 @@ func (m *mediaDB) GetAttachmentByID(ctx context.Context, id string) (*gtsmodel.M
q := m.newMediaQ(attachment). q := m.newMediaQ(attachment).
Where("media_attachment.id = ?", id) Where("media_attachment.id = ?", id)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, m.conn.ProcessError(err) return nil, m.conn.ProcessError(err)
} }
return attachment, nil return attachment, nil

View file

@ -61,8 +61,7 @@ func (m *mentionDB) getMentionDB(ctx context.Context, id string) (*gtsmodel.Ment
q := m.newMentionQ(mention). q := m.newMentionQ(mention).
Where("mention.id = ?", id) Where("mention.id = ?", id)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, m.conn.ProcessError(err) return nil, m.conn.ProcessError(err)
} }

View file

@ -125,8 +125,7 @@ func (n *notificationDB) getNotificationDB(ctx context.Context, id string, dst *
q := n.newNotificationQ(dst). q := n.newNotificationQ(dst).
Where("notification.id = ?", id) Where("notification.id = ?", id)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return n.conn.ProcessError(err) return n.conn.ProcessError(err)
} }

View file

@ -74,8 +74,7 @@ func (r *relationshipDB) GetBlock(ctx context.Context, account1 string, account2
Where("block.account_id = ?", account1). Where("block.account_id = ?", account1).
Where("block.target_account_id = ?", account2) Where("block.target_account_id = ?", account2)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, r.conn.ProcessError(err) return nil, r.conn.ProcessError(err)
} }
return block, nil return block, nil
@ -286,8 +285,7 @@ func (r *relationshipDB) GetAccountFollowRequests(ctx context.Context, accountID
q := r.newFollowQ(&followRequests). q := r.newFollowQ(&followRequests).
Where("target_account_id = ?", accountID) Where("target_account_id = ?", accountID)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, r.conn.ProcessError(err) return nil, r.conn.ProcessError(err)
} }
return followRequests, nil return followRequests, nil
@ -299,8 +297,7 @@ func (r *relationshipDB) GetAccountFollows(ctx context.Context, accountID string
q := r.newFollowQ(&follows). q := r.newFollowQ(&follows).
Where("account_id = ?", accountID) Where("account_id = ?", accountID)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, r.conn.ProcessError(err) return nil, r.conn.ProcessError(err)
} }
return follows, nil return follows, nil

View file

@ -47,7 +47,7 @@ func (s *sessionDB) GetSession(ctx context.Context) (*gtsmodel.RouterSession, db
return nil, s.conn.ProcessError(err) return nil, s.conn.ProcessError(err)
} }
if len(rss) <= 0 { if len(rss) == 0 {
// no session created yet, so make one // no session created yet, so make one
return s.createSession(ctx) return s.createSession(ctx)
} }

View file

@ -23,6 +23,7 @@
"context" "context"
"time" "time"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/cache" "github.com/superseriousbusiness/gotosocial/internal/cache"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
@ -206,7 +207,11 @@ func (s *statusDB) GetStatusChildren(ctx context.Context, status *gtsmodel.Statu
children := []*gtsmodel.Status{} children := []*gtsmodel.Status{}
for e := foundStatuses.Front(); e != nil; e = e.Next() { for e := foundStatuses.Front(); e != nil; e = e.Next() {
// only append children, not the overall parent status // only append children, not the overall parent status
entry := e.Value.(*gtsmodel.Status) entry, ok := e.Value.(*gtsmodel.Status)
if !ok {
logrus.Panic("GetStatusChildren: found status could not be asserted to *gtsmodel.Status")
}
if entry.ID != status.ID { if entry.ID != status.ID {
children = append(children, entry) children = append(children, entry)
} }
@ -233,7 +238,11 @@ func (s *statusDB) statusChildren(ctx context.Context, status *gtsmodel.Status,
for _, child := range immediateChildren { for _, child := range immediateChildren {
insertLoop: insertLoop:
for e := foundStatuses.Front(); e != nil; e = e.Next() { for e := foundStatuses.Front(); e != nil; e = e.Next() {
entry := e.Value.(*gtsmodel.Status) entry, ok := e.Value.(*gtsmodel.Status)
if !ok {
logrus.Panic("statusChildren: found status could not be asserted to *gtsmodel.Status")
}
if child.InReplyToAccountID != "" && entry.ID == child.InReplyToID { if child.InReplyToAccountID != "" && entry.ID == child.InReplyToID {
foundStatuses.InsertAfter(child, e) foundStatuses.InsertAfter(child, e)
break insertLoop break insertLoop
@ -306,8 +315,7 @@ func (s *statusDB) GetStatusFaves(ctx context.Context, status *gtsmodel.Status)
q := s.newFaveQ(&faves). q := s.newFaveQ(&faves).
Where("status_id = ?", status.ID) Where("status_id = ?", status.ID)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, s.conn.ProcessError(err) return nil, s.conn.ProcessError(err)
} }
return faves, nil return faves, nil
@ -319,8 +327,7 @@ func (s *statusDB) GetStatusReblogs(ctx context.Context, status *gtsmodel.Status
q := s.newStatusQ(&reblogs). q := s.newStatusQ(&reblogs).
Where("boost_of_id = ?", status.ID) Where("boost_of_id = ?", status.ID)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, s.conn.ProcessError(err) return nil, s.conn.ProcessError(err)
} }
return reblogs, nil return reblogs, nil

View file

@ -91,8 +91,7 @@ func (t *timelineDB) GetHomeTimeline(ctx context.Context, accountID string, maxI
q = q.WhereGroup(" AND ", whereGroup) q = q.WhereGroup(" AND ", whereGroup)
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, t.conn.ProcessError(err) return nil, t.conn.ProcessError(err)
} }
return statuses, nil return statuses, nil
@ -136,8 +135,7 @@ func (t *timelineDB) GetPublicTimeline(ctx context.Context, accountID string, ma
q = q.Limit(limit) q = q.Limit(limit)
} }
err := q.Scan(ctx) if err := q.Scan(ctx); err != nil {
if err != nil {
return nil, t.conn.ProcessError(err) return nil, t.conn.ProcessError(err)
} }
return statuses, nil return statuses, nil

View file

@ -149,7 +149,7 @@ func (f *federator) AuthenticateFederatedRequest(ctx context.Context, requestedU
requestingPublicKeyID, err := url.Parse(verifier.KeyId()) requestingPublicKeyID, err := url.Parse(verifier.KeyId())
if err != nil { if err != nil {
l.Debug("couldn't parse public key URL") l.Debug("couldn't parse public key URL")
return nil, false, nil // couldn't parse the public key ID url return nil, false, err // couldn't parse the public key ID url
} }
requestingRemoteAccount := &gtsmodel.Account{} requestingRemoteAccount := &gtsmodel.Account{}

View file

@ -172,7 +172,7 @@ func (d *deref) iterateDescendants(ctx context.Context, username string, statusI
l.Debugf("dereferencing page %s", currentPageIRI) l.Debugf("dereferencing page %s", currentPageIRI)
nextPage, err := d.DereferenceCollectionPage(ctx, username, currentPageIRI) nextPage, err := d.DereferenceCollectionPage(ctx, username, currentPageIRI)
if err != nil { if err != nil {
return nil return err
} }
// next items could be either a list of URLs or a list of statuses // next items could be either a list of URLs or a list of statuses
@ -188,19 +188,19 @@ func (d *deref) iterateDescendants(ctx context.Context, username string, statusI
// We're looking for a url to feed to GetRemoteStatus. // We're looking for a url to feed to GetRemoteStatus.
// Items can be either an IRI, or a Note. // Items can be either an IRI, or a Note.
// If a note, we grab the ID from it and call it, rather than parsing the note. // If a note, we grab the ID from it and call it, rather than parsing the note.
var itemURI *url.URL var itemURI *url.URL
if iter.IsIRI() { switch {
case iter.IsIRI():
// iri, easy // iri, easy
itemURI = iter.GetIRI() itemURI = iter.GetIRI()
} else if iter.IsActivityStreamsNote() { case iter.IsActivityStreamsNote():
// note, get the id from it to use as iri // note, get the id from it to use as iri
n := iter.GetActivityStreamsNote() n := iter.GetActivityStreamsNote()
id := n.GetJSONLDId() id := n.GetJSONLDId()
if id != nil && id.IsIRI() { if id != nil && id.IsIRI() {
itemURI = id.GetIRI() itemURI = id.GetIRI()
} }
} else { default:
// if it's not an iri or a note, we don't know how to process it // if it's not an iri or a note, we don't know how to process it
continue continue
} }
@ -211,7 +211,7 @@ func (d *deref) iterateDescendants(ctx context.Context, username string, statusI
} }
// we can confidently say now that we found something // we can confidently say now that we found something
foundReplies = foundReplies + 1 foundReplies++
// get the remote statusable and put it in the db // get the remote statusable and put it in the db
_, statusable, new, err := d.GetRemoteStatus(ctx, username, itemURI, false, false) _, statusable, new, err := d.GetRemoteStatus(ctx, username, itemURI, false, false)

View file

@ -97,9 +97,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
if iter.GetType() == nil { if iter.GetType() == nil {
continue continue
} }
switch iter.GetType().GetTypeName() { if iter.GetType().GetTypeName() == ap.ActivityFollow {
// we have the whole object so we can figure out what we're accepting
case ap.ActivityFollow:
// ACCEPT FOLLOW // ACCEPT FOLLOW
asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow) asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
if !ok { if !ok {

View file

@ -24,6 +24,8 @@
"net/url" "net/url"
"sync" "sync"
"sync/atomic" "sync/atomic"
"github.com/sirupsen/logrus"
) )
// Lock takes a lock for the object at the specified id. If an error // Lock takes a lock for the object at the specified id. If an error
@ -54,7 +56,10 @@ func (f *federatingDB) Lock(c context.Context, id *url.URL) error {
// Get mutex, or create new // Get mutex, or create new
mu, ok := f.locks[idStr] mu, ok := f.locks[idStr]
if !ok { if !ok {
mu = f.pool.Get().(*mutex) mu, ok = f.pool.Get().(*mutex)
if !ok {
logrus.Panic("Lock: pool entry was not a *mutex")
}
f.locks[idStr] = mu f.locks[idStr] = mu
} }

View file

@ -90,9 +90,8 @@ func (f *federatingDB) Reject(ctx context.Context, reject vocab.ActivityStreamsR
continue continue
} }
switch iter.GetType().GetTypeName() { if iter.GetType().GetTypeName() == ap.ActivityFollow {
// we have the whole object so we can figure out what we're rejecting // we have the whole object so we can figure out what we're rejecting
case ap.ActivityFollow:
// REJECT FOLLOW // REJECT FOLLOW
asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow) asFollow, ok := iter.GetType().(vocab.ActivityStreamsFollow)
if !ok { if !ok {

View file

@ -308,17 +308,29 @@ func (f *federatingDB) collectIRIs(ctx context.Context, iris []*url.URL) (vocab.
func extractFromCtx(ctx context.Context) (receivingAccount, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) { func extractFromCtx(ctx context.Context) (receivingAccount, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) {
receivingAccountI := ctx.Value(util.APReceivingAccount) receivingAccountI := ctx.Value(util.APReceivingAccount)
if receivingAccountI != nil { if receivingAccountI != nil {
receivingAccount = receivingAccountI.(*gtsmodel.Account) var ok bool
receivingAccount, ok = receivingAccountI.(*gtsmodel.Account)
if !ok {
logrus.Panicf("extractFromCtx: context entry with key %s could not be asserted to *gtsmodel.Account", util.APReceivingAccount)
}
} }
requestingAcctI := ctx.Value(util.APRequestingAccount) requestingAcctI := ctx.Value(util.APRequestingAccount)
if requestingAcctI != nil { if requestingAcctI != nil {
requestingAccount = requestingAcctI.(*gtsmodel.Account) var ok bool
requestingAccount, ok = requestingAcctI.(*gtsmodel.Account)
if !ok {
logrus.Panicf("extractFromCtx: context entry with key %s could not be asserted to *gtsmodel.Account", util.APRequestingAccount)
}
} }
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey) fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI != nil { if fromFederatorChanI != nil {
fromFederatorChan = fromFederatorChanI.(chan messages.FromFederator) var ok bool
fromFederatorChan, ok = fromFederatorChanI.(chan messages.FromFederator)
if !ok {
logrus.Panicf("extractFromCtx: context entry with key %s could not be asserted to chan messages.FromFederator", util.APFromFederatorChanKey)
}
} }
return return

View file

@ -54,17 +54,18 @@ func (f *federator) NewTransport(ctx context.Context, actorBoxIRI *url.URL, gofe
var username string var username string
var err error var err error
if util.IsInboxPath(actorBoxIRI) { switch {
case util.IsInboxPath(actorBoxIRI):
username, err = util.ParseInboxPath(actorBoxIRI) username, err = util.ParseInboxPath(actorBoxIRI)
if err != nil { if err != nil {
return nil, fmt.Errorf("couldn't parse path %s as an inbox: %s", actorBoxIRI.String(), err) return nil, fmt.Errorf("couldn't parse path %s as an inbox: %s", actorBoxIRI.String(), err)
} }
} else if util.IsOutboxPath(actorBoxIRI) { case util.IsOutboxPath(actorBoxIRI):
username, err = util.ParseOutboxPath(actorBoxIRI) username, err = util.ParseOutboxPath(actorBoxIRI)
if err != nil { if err != nil {
return nil, fmt.Errorf("couldn't parse path %s as an outbox: %s", actorBoxIRI.String(), err) return nil, fmt.Errorf("couldn't parse path %s as an outbox: %s", actorBoxIRI.String(), err)
} }
} else { default:
return nil, fmt.Errorf("id %s was neither an inbox path nor an outbox path", actorBoxIRI.String()) return nil, fmt.Errorf("id %s was neither an inbox path nor an outbox path", actorBoxIRI.String())
} }

View file

@ -82,10 +82,9 @@ func (p *processor) GetStatusReplies(ctx context.Context, requestedUsername stri
// 1. we're asked for the whole collection and not a page -- we can just return the collection, with no items, but a link to 'first' page. // 1. we're asked for the whole collection and not a page -- we can just return the collection, with no items, but a link to 'first' page.
// 2. we're asked for a page but only_other_accounts has not been set in the query -- so we should just return the first page of the collection, with no items. // 2. we're asked for a page but only_other_accounts has not been set in the query -- so we should just return the first page of the collection, with no items.
// 3. we're asked for a page, and only_other_accounts has been set, and min_id has optionally been set -- so we need to return some actual items! // 3. we're asked for a page, and only_other_accounts has been set, and min_id has optionally been set -- so we need to return some actual items!
switch {
if !page { case !page:
// scenario 1 // scenario 1
// get the collection // get the collection
collection, err := p.tc.StatusToASRepliesCollection(ctx, s, onlyOtherAccounts) collection, err := p.tc.StatusToASRepliesCollection(ctx, s, onlyOtherAccounts)
if err != nil { if err != nil {
@ -96,9 +95,8 @@ func (p *processor) GetStatusReplies(ctx context.Context, requestedUsername stri
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
} else if page && requestURL.Query().Get("only_other_accounts") == "" { case page && requestURL.Query().Get("only_other_accounts") == "":
// scenario 2 // scenario 2
// get the collection // get the collection
collection, err := p.tc.StatusToASRepliesCollection(ctx, s, onlyOtherAccounts) collection, err := p.tc.StatusToASRepliesCollection(ctx, s, onlyOtherAccounts)
if err != nil { if err != nil {
@ -109,7 +107,7 @@ func (p *processor) GetStatusReplies(ctx context.Context, requestedUsername stri
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
} else { default:
// scenario 3 // scenario 3
// get immediate children // get immediate children
replies, err := p.db.GetStatusChildren(ctx, s, true, minID) replies, err := p.db.GetStatusChildren(ctx, s, true, minID)

View file

@ -38,13 +38,14 @@ func (p *processor) GetUser(ctx context.Context, requestedUsername string, reque
} }
var requestedPerson vocab.ActivityStreamsPerson var requestedPerson vocab.ActivityStreamsPerson
if util.IsPublicKeyPath(requestURL) { switch {
case util.IsPublicKeyPath(requestURL):
// if it's a public key path, we don't need to authenticate but we'll only serve the bare minimum user profile needed for the public key // if it's a public key path, we don't need to authenticate but we'll only serve the bare minimum user profile needed for the public key
requestedPerson, err = p.tc.AccountToASMinimal(ctx, requestedAccount) requestedPerson, err = p.tc.AccountToASMinimal(ctx, requestedAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
} else if util.IsUserPath(requestURL) { case util.IsUserPath(requestURL):
// if it's a user path, we want to fully authenticate the request before we serve any data, and then we can serve a more complete profile // if it's a user path, we want to fully authenticate the request before we serve any data, and then we can serve a more complete profile
requestingAccountURI, authenticated, err := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername) requestingAccountURI, authenticated, err := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername)
if err != nil || !authenticated { if err != nil || !authenticated {
@ -72,7 +73,7 @@ func (p *processor) GetUser(ctx context.Context, requestedUsername string, reque
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
} else { default:
return nil, gtserror.NewErrorBadRequest(fmt.Errorf("path was not public key path or user path")) return nil, gtserror.NewErrorBadRequest(fmt.Errorf("path was not public key path or user path"))
} }

View file

@ -64,15 +64,13 @@ func (p *processor) ProcessFromClientAPI(ctx context.Context, clientMsg messages
} }
case ap.ActivityAccept: case ap.ActivityAccept:
// ACCEPT // ACCEPT
switch clientMsg.APObjectType { if clientMsg.APObjectType == ap.ActivityFollow {
case ap.ActivityFollow:
// ACCEPT FOLLOW // ACCEPT FOLLOW
return p.processAcceptFollowFromClientAPI(ctx, clientMsg) return p.processAcceptFollowFromClientAPI(ctx, clientMsg)
} }
case ap.ActivityReject: case ap.ActivityReject:
// REJECT // REJECT
switch clientMsg.APObjectType { if clientMsg.APObjectType == ap.ActivityFollow {
case ap.ActivityFollow:
// REJECT FOLLOW (request) // REJECT FOLLOW (request)
return p.processRejectFollowFromClientAPI(ctx, clientMsg) return p.processRejectFollowFromClientAPI(ctx, clientMsg)
} }

View file

@ -64,8 +64,7 @@ func (p *processor) ProcessFromFederator(ctx context.Context, federatorMsg messa
} }
case ap.ActivityUpdate: case ap.ActivityUpdate:
// UPDATE SOMETHING // UPDATE SOMETHING
switch federatorMsg.APObjectType { if federatorMsg.APObjectType == ap.ObjectProfile {
case ap.ObjectProfile:
// UPDATE AN ACCOUNT // UPDATE AN ACCOUNT
return p.processUpdateAccountFromFederator(ctx, federatorMsg) return p.processUpdateAccountFromFederator(ctx, federatorMsg)
} }

View file

@ -38,14 +38,15 @@ func (p *processor) ProcessVisibility(ctx context.Context, form *apimodel.Advanc
replyable := true replyable := true
likeable := true likeable := true
var vis gtsmodel.Visibility
// If visibility isn't set on the form, then just take the account default. // If visibility isn't set on the form, then just take the account default.
// If that's also not set, take the default for the whole instance. // If that's also not set, take the default for the whole instance.
if form.Visibility != "" { var vis gtsmodel.Visibility
switch {
case form.Visibility != "":
vis = p.tc.APIVisToVis(form.Visibility) vis = p.tc.APIVisToVis(form.Visibility)
} else if accountDefaultVis != "" { case accountDefaultVis != "":
vis = accountDefaultVis vis = accountDefaultVis
} else { default:
vis = gtsmodel.VisibilityDefault vis = gtsmodel.VisibilityDefault
} }

View file

@ -51,6 +51,7 @@ func oddOrEven(n int) string {
} }
func noescape(str string) template.HTML { func noescape(str string) template.HTML {
/* #nosec G203 */
return template.HTML(str) return template.HTML(str)
} }
@ -67,19 +68,21 @@ type iconWithLabel struct {
func visibilityIcon(visibility model.Visibility) template.HTML { func visibilityIcon(visibility model.Visibility) template.HTML {
var icon iconWithLabel var icon iconWithLabel
if visibility == model.VisibilityPublic { switch visibility {
case model.VisibilityPublic:
icon = iconWithLabel{"globe", "public"} icon = iconWithLabel{"globe", "public"}
} else if visibility == model.VisibilityUnlisted { case model.VisibilityUnlisted:
icon = iconWithLabel{"unlock", "unlisted"} icon = iconWithLabel{"unlock", "unlisted"}
} else if visibility == model.VisibilityPrivate { case model.VisibilityPrivate:
icon = iconWithLabel{"lock", "private"} icon = iconWithLabel{"lock", "private"}
} else if visibility == model.VisibilityMutualsOnly { case model.VisibilityMutualsOnly:
icon = iconWithLabel{"handshake-o", "mutuals only"} icon = iconWithLabel{"handshake-o", "mutuals only"}
} else if visibility == model.VisibilityDirect { case model.VisibilityDirect:
icon = iconWithLabel{"envelope", "direct"} icon = iconWithLabel{"envelope", "direct"}
} }
return template.HTML(fmt.Sprintf(`<i aria-label="Visiblity: %v" class="fa fa-%v"></i>`, icon.label, icon.faIcon)) /* #nosec G203 */
return template.HTML(fmt.Sprintf(`<i aria-label="Visibility: %v" class="fa fa-%v"></i>`, icon.label, icon.faIcon))
} }
func loadTemplateFunctions(engine *gin.Engine) { func loadTemplateFunctions(engine *gin.Engine) {

View file

@ -98,7 +98,7 @@ func (f *formatter) ReplaceLinks(ctx context.Context, in string) string {
shortString := thisURL.Hostname() shortString := thisURL.Hostname()
if thisURL.Path != "" { if thisURL.Path != "" {
shortString = shortString + thisURL.Path shortString += thisURL.Path
} }
if thisURL.Fragment != "" { if thisURL.Fragment != "" {

View file

@ -127,7 +127,7 @@ func (t *timeline) GetXFromTop(ctx context.Context, amount int) ([]*apimodel.Sta
return nil, errors.New("GetXFromTop: could not parse e as a preparedPostsEntry") return nil, errors.New("GetXFromTop: could not parse e as a preparedPostsEntry")
} }
statuses = append(statuses, entry.prepared) statuses = append(statuses, entry.prepared)
served = served + 1 served++
if served >= amount { if served >= amount {
break break
} }
@ -145,7 +145,7 @@ func (t *timeline) GetXBehindID(ctx context.Context, amount int, behindID string
}) })
newAttempts := *attempts newAttempts := *attempts
newAttempts = newAttempts + 1 newAttempts++
attempts = &newAttempts attempts = &newAttempts
// make a slice of statuses with the length we need to return // make a slice of statuses with the length we need to return
@ -161,7 +161,7 @@ func (t *timeline) GetXBehindID(ctx context.Context, amount int, behindID string
findMarkLoop: findMarkLoop:
for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() { for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() {
position = position + 1 position++
entry, ok := e.Value.(*preparedPostsEntry) entry, ok := e.Value.(*preparedPostsEntry)
if !ok { if !ok {
return nil, errors.New("GetXBehindID: could not parse e as a preparedPostsEntry") return nil, errors.New("GetXBehindID: could not parse e as a preparedPostsEntry")
@ -218,7 +218,7 @@ func (t *timeline) GetXBehindID(ctx context.Context, amount int, behindID string
// serve up to the amount requested // serve up to the amount requested
statuses = append(statuses, entry.prepared) statuses = append(statuses, entry.prepared)
served = served + 1 served++
if served >= amount { if served >= amount {
break serveloop break serveloop
} }
@ -272,7 +272,7 @@ func (t *timeline) GetXBeforeID(ctx context.Context, amount int, beforeID string
// serve up to the amount requested // serve up to the amount requested
statuses = append(statuses, entry.prepared) statuses = append(statuses, entry.prepared)
served = served + 1 served++
if served >= amount { if served >= amount {
break serveloopFromTop break serveloopFromTop
} }
@ -288,7 +288,7 @@ func (t *timeline) GetXBeforeID(ctx context.Context, amount int, beforeID string
// serve up to the amount requested // serve up to the amount requested
statuses = append(statuses, entry.prepared) statuses = append(statuses, entry.prepared)
served = served + 1 served++
if served >= amount { if served >= amount {
break serveloopFromBottom break serveloopFromBottom
} }
@ -311,7 +311,7 @@ func (t *timeline) GetXBetweenID(ctx context.Context, amount int, behindID strin
var behindIDMark *list.Element var behindIDMark *list.Element
findMarkLoop: findMarkLoop:
for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() { for e := t.preparedPosts.data.Front(); e != nil; e = e.Next() {
position = position + 1 position++
entry, ok := e.Value.(*preparedPostsEntry) entry, ok := e.Value.(*preparedPostsEntry)
if !ok { if !ok {
return nil, errors.New("GetXBetweenID: could not parse e as a preparedPostsEntry") return nil, errors.New("GetXBetweenID: could not parse e as a preparedPostsEntry")
@ -350,7 +350,7 @@ func (t *timeline) GetXBetweenID(ctx context.Context, amount int, behindID strin
// serve up to the amount requested // serve up to the amount requested
statuses = append(statuses, entry.prepared) statuses = append(statuses, entry.prepared)
served = served + 1 served++
if served >= amount { if served >= amount {
break serveloop break serveloop
} }

View file

@ -50,7 +50,7 @@ func (t *timeline) IndexBefore(ctx context.Context, statusID string, include boo
i := 0 i := 0
grabloop: grabloop:
for ; len(filtered) < amount && i < 5; i = i + 1 { // try the grabloop 5 times only for ; len(filtered) < amount && i < 5; i++ { // try the grabloop 5 times only
statuses, err := t.db.GetHomeTimeline(ctx, t.accountID, "", "", offsetStatus, amount, false) statuses, err := t.db.GetHomeTimeline(ctx, t.accountID, "", "", offsetStatus, amount, false)
if err != nil { if err != nil {
if err == db.ErrNoEntries { if err == db.ErrNoEntries {
@ -129,7 +129,7 @@ func (t *timeline) IndexBehind(ctx context.Context, statusID string, include boo
i := 0 i := 0
grabloop: grabloop:
for ; len(filtered) < amount && i < 5; i = i + 1 { // try the grabloop 5 times only for ; len(filtered) < amount && i < 5; i++ { // try the grabloop 5 times only
l.Tracef("entering grabloop; i is %d; len(filtered) is %d", i, len(filtered)) l.Tracef("entering grabloop; i is %d; len(filtered) is %d", i, len(filtered))
statuses, err := t.db.GetHomeTimeline(ctx, t.accountID, offsetStatus, "", "", amount, false) statuses, err := t.db.GetHomeTimeline(ctx, t.accountID, offsetStatus, "", "", amount, false)
if err != nil { if err != nil {

View file

@ -50,7 +50,7 @@ func (p *postIndex) insertIndexed(i *postIndexEntry) (bool, error) {
// We need to iterate through the index to make sure we put this post in the appropriate place according to when it was created. // We need to iterate through the index to make sure we put this post in the appropriate place according to when it was created.
// We also need to make sure we're not inserting a duplicate post -- this can happen sometimes and it's not nice UX (*shudder*). // We also need to make sure we're not inserting a duplicate post -- this can happen sometimes and it's not nice UX (*shudder*).
for e := p.data.Front(); e != nil; e = e.Next() { for e := p.data.Front(); e != nil; e = e.Next() {
position = position + 1 position++
entry, ok := e.Value.(*postIndexEntry) entry, ok := e.Value.(*postIndexEntry)
if !ok { if !ok {

View file

@ -107,7 +107,7 @@ func (t *timeline) PrepareBehind(ctx context.Context, statusID string, amount in
// we're done // we're done
break prepareloop break prepareloop
} }
prepared = prepared + 1 prepared++
} }
} }
@ -162,7 +162,7 @@ func (t *timeline) PrepareBefore(ctx context.Context, statusID string, include b
// we're done // we're done
break prepareloop break prepareloop
} }
prepared = prepared + 1 prepared++
} }
} }
@ -214,7 +214,7 @@ func (t *timeline) PrepareFromTop(ctx context.Context, amount int) error {
continue continue
} }
prepared = prepared + 1 prepared++
if prepared == amount { if prepared == amount {
// we're done // we're done
l.Trace("leaving prepareloop") l.Trace("leaving prepareloop")

View file

@ -53,7 +53,7 @@ func (p *preparedPosts) insertPrepared(i *preparedPostsEntry) error {
// We need to iterate through the index to make sure we put this post in the appropriate place according to when it was created. // We need to iterate through the index to make sure we put this post in the appropriate place according to when it was created.
// We also need to make sure we're not inserting a duplicate post -- this can happen sometimes and it's not nice UX (*shudder*). // We also need to make sure we're not inserting a duplicate post -- this can happen sometimes and it's not nice UX (*shudder*).
for e := p.data.Front(); e != nil; e = e.Next() { for e := p.data.Front(); e != nil; e = e.Next() {
position = position + 1 position++
entry, ok := e.Value.(*preparedPostsEntry) entry, ok := e.Value.(*preparedPostsEntry)
if !ok { if !ok {

View file

@ -52,7 +52,7 @@ func (t *timeline) Remove(ctx context.Context, statusID string) (int, error) {
} }
for _, e := range removeIndexes { for _, e := range removeIndexes {
t.postIndex.data.Remove(e) t.postIndex.data.Remove(e)
removed = removed + 1 removed++
} }
// remove entr(ies) from prepared posts // remove entr(ies) from prepared posts
@ -71,7 +71,7 @@ func (t *timeline) Remove(ctx context.Context, statusID string) (int, error) {
} }
for _, e := range removePrepared { for _, e := range removePrepared {
t.preparedPosts.data.Remove(e) t.preparedPosts.data.Remove(e)
removed = removed + 1 removed++
} }
l.Debugf("removed %d entries", removed) l.Debugf("removed %d entries", removed)
@ -104,7 +104,7 @@ func (t *timeline) RemoveAllBy(ctx context.Context, accountID string) (int, erro
} }
for _, e := range removeIndexes { for _, e := range removeIndexes {
t.postIndex.data.Remove(e) t.postIndex.data.Remove(e)
removed = removed + 1 removed++
} }
// remove entr(ies) from prepared posts // remove entr(ies) from prepared posts
@ -123,7 +123,7 @@ func (t *timeline) RemoveAllBy(ctx context.Context, accountID string) (int, erro
} }
for _, e := range removePrepared { for _, e := range removePrepared {
t.preparedPosts.data.Remove(e) t.preparedPosts.data.Remove(e)
removed = removed + 1 removed++
} }
l.Debugf("removed %d entries", removed) l.Debugf("removed %d entries", removed)

View file

@ -29,7 +29,7 @@
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
var testModels []interface{} = []interface{}{ var testModels = []interface{}{
&gtsmodel.Account{}, &gtsmodel.Account{},
&gtsmodel.Application{}, &gtsmodel.Application{},
&gtsmodel.Block{}, &gtsmodel.Block{},