replace errors package with fmt.Errorf

https://gist.github.com/Peltoche/60b8b81dfbf70164d0e2b88988003229
was used for it, thanks to @Peltoche for publishing it.
This commit is contained in:
Dmitry Verkhoturov
2022-04-26 00:25:09 -05:00
committed by Umputun
parent 21d0339d3d
commit ba86db1263
48 changed files with 426 additions and 399 deletions
@@ -7,8 +7,9 @@
package accessor
import (
"fmt"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store/admin"
)
@@ -43,7 +44,7 @@ func (m *MemAdmin) Key(_ string) (key string, err error) {
func (m *MemAdmin) Admins(siteID string) (ids []string, err error) {
resp, ok := m.data[siteID]
if !ok {
return nil, errors.Errorf("site %s not found", siteID)
return nil, fmt.Errorf("site %s not found", siteID)
}
log.Printf("[DEBUG] admins for %s, %+v", siteID, resp.IDs)
return resp.IDs, nil
@@ -53,7 +54,7 @@ func (m *MemAdmin) Admins(siteID string) (ids []string, err error) {
func (m *MemAdmin) Email(siteID string) (email string, err error) {
resp, ok := m.data[siteID]
if !ok {
return "", errors.Errorf("site %s not found", siteID)
return "", fmt.Errorf("site %s not found", siteID)
}
return resp.Email, nil
@@ -63,7 +64,7 @@ func (m *MemAdmin) Email(siteID string) (email string, err error) {
func (m *MemAdmin) Enabled(siteID string) (ok bool, err error) {
resp, ok := m.data[siteID]
if !ok {
return false, errors.Errorf("site %s not found", siteID)
return false, fmt.Errorf("site %s not found", siteID)
}
return resp.Enabled, nil
}
@@ -72,7 +73,7 @@ func (m *MemAdmin) Enabled(siteID string) (ok bool, err error) {
func (m *MemAdmin) OnEvent(siteID string, ev admin.EventType) error {
resp, ok := m.data[siteID]
if !ok {
return errors.Errorf("site %s not found", siteID)
return fmt.Errorf("site %s not found", siteID)
}
if ev == admin.EvCreate {
resp.CountCreated++ // not a good idea, just for demo
+19 -17
View File
@@ -7,13 +7,12 @@
package accessor
import (
"fmt"
"log"
"sort"
"sync"
"time"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
"github.com/umputun/remark42/backend/app/store/engine"
)
@@ -58,7 +57,7 @@ func NewMemData() *MemData {
func (m *MemData) Create(comment store.Comment) (commentID string, err error) {
if ro, e := m.Flag(engine.FlagRequest{Flag: engine.ReadOnly, Locator: comment.Locator}); e == nil && ro {
return "", errors.Errorf("post %s is read-only", comment.Locator.URL)
return "", fmt.Errorf("post %s is read-only", comment.Locator.URL)
}
m.mu.Lock()
@@ -66,7 +65,7 @@ func (m *MemData) Create(comment store.Comment) (commentID string, err error) {
comments := m.posts[comment.Locator.SiteID]
for _, c := range comments { // don't allow duplicated IDs
if c.ID == comment.ID {
return "", errors.New("dup key")
return "", fmt.Errorf("dup key")
}
}
comments = append(comments, comment)
@@ -161,7 +160,7 @@ func (m *MemData) Count(req engine.FindRequest) (count int, err error) {
})
return len(comments), nil
default:
return 0, errors.Errorf("invalid count request %+v", req)
return 0, fmt.Errorf("invalid count request %+v", req)
}
}
@@ -176,7 +175,7 @@ func (m *MemData) Info(req engine.InfoRequest) (res []store.PostInfo, err error)
return c.Locator == req.Locator
})
if len(comments) == 0 {
return nil, errors.New("not found")
return nil, fmt.Errorf("not found")
}
info := store.PostInfo{
URL: req.Locator.URL,
@@ -235,7 +234,7 @@ func (m *MemData) Info(req engine.InfoRequest) (res []store.PostInfo, err error)
return res, nil
}
return nil, errors.Errorf("invalid info request %+v", req)
return nil, fmt.Errorf("invalid info request %+v", req)
}
// Flag sets and gets flag values
@@ -277,7 +276,7 @@ func (m *MemData) ListFlags(req engine.FlagRequest) (res []interface{}, err erro
return res, nil
}
return nil, errors.Errorf("flag %s not listable", req.Flag)
return nil, fmt.Errorf("flag %s not listable", req.Flag)
}
// UserDetail sets or gets single detail value, or gets all details fo§r requested site.
@@ -287,7 +286,7 @@ func (m *MemData) UserDetail(req engine.UserDetailRequest) ([]engine.UserDetailE
switch req.Detail {
case engine.UserEmail, engine.UserTelegram:
if req.UserID == "" {
return nil, errors.New("userid cannot be empty in request for single detail")
return nil, fmt.Errorf("userid cannot be empty in request for single detail")
}
m.mu.Lock()
@@ -306,9 +305,9 @@ func (m *MemData) UserDetail(req engine.UserDetailRequest) ([]engine.UserDetailE
defer m.mu.Unlock()
return m.listDetails(req.Locator)
}
return nil, errors.New("unsupported request with userdetail all")
return nil, fmt.Errorf("unsupported request with userdetail all")
default:
return nil, errors.Errorf("unsupported detail %q", req.Detail)
return nil, fmt.Errorf("unsupported detail %q", req.Detail)
}
}
@@ -337,13 +336,13 @@ func (m *MemData) Delete(req engine.DeleteRequest) error {
case req.Locator.SiteID != "" && req.Locator.URL == "" && req.CommentID == "" && req.UserID == "" && req.UserDetail == "": // delete site
if _, ok := m.posts[req.Locator.SiteID]; !ok {
return errors.New("not found")
return fmt.Errorf("not found")
}
m.posts[req.Locator.SiteID] = []store.Comment{}
return nil
}
return errors.Errorf("invalid delete request %+v", req)
return fmt.Errorf("invalid delete request %+v", req)
}
func (m *MemData) deleteComment(loc store.Locator, id string, mode store.DeleteMode) error {
@@ -352,7 +351,7 @@ func (m *MemData) deleteComment(loc store.Locator, id string, mode store.DeleteM
return c.Locator == loc && c.ID == id
})
if len(comments) == 0 {
return errors.New("not found")
return fmt.Errorf("not found")
}
comments[0].SetDeleted(mode)
@@ -430,7 +429,10 @@ func (m *MemData) setFlag(req engine.FlagRequest) (res bool, err error) {
info.ReadOnly = status
m.metaPosts[req.Locator] = info
}
return status, errors.Wrapf(err, "failed to set flag %+v", req)
if err != nil {
return false, fmt.Errorf("failed to set flag %+v: %w", req, err)
}
return status, nil
}
// getUserDetail returns UserDetailEntry with requested userDetail (omitting other details)
@@ -535,7 +537,7 @@ func (m *MemData) get(loc store.Locator, commentID string) (store.Comment, error
return c.Locator == loc && c.ID == commentID
})
if len(comments) == 0 {
return store.Comment{}, errors.New("not found")
return store.Comment{}, fmt.Errorf("not found")
}
return comments[0], nil
}
@@ -557,7 +559,7 @@ func (m *MemData) updateComment(comment store.Comment) error {
m.posts[comment.Locator.SiteID] = comments
return nil
}
return errors.New("not found")
return fmt.Errorf("not found")
}
func (m *MemData) match(comments []store.Comment, fn func(c store.Comment) bool) (res []store.Comment) {
@@ -8,11 +8,11 @@ package accessor
import (
"context"
"fmt"
"sync"
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store/image"
)
@@ -53,7 +53,7 @@ func (m *MemImage) ResetCleanupTimer(id string) error {
m.insertTime[id] = time.Now()
return nil
}
return errors.Errorf("image %s not found", id)
return fmt.Errorf("image %s not found", id)
}
// Load image by ID
@@ -65,7 +65,7 @@ func (m *MemImage) Load(id string) ([]byte, error) {
}
m.mu.RUnlock()
if !ok {
return nil, errors.Errorf("image %s not found", id)
return nil, fmt.Errorf("image %s not found", id)
}
return img, nil
}
@@ -76,7 +76,7 @@ func (m *MemImage) Commit(id string) error {
img, ok := m.imagesStaging[id]
m.mu.RUnlock()
if !ok {
return errors.Errorf("failed to commit %s, not found in staging", id)
return fmt.Errorf("failed to commit %s, not found in staging", id)
}
m.mu.Lock()
+1 -1
View File
@@ -6,7 +6,6 @@ require (
github.com/go-pkgz/jrpc v0.2.0
github.com/go-pkgz/lgr v0.10.4
github.com/jessevdk/go-flags v1.5.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.1
github.com/umputun/remark42/backend v1.9.0
)
@@ -29,6 +28,7 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/microcosm-cc/bluemonday v1.0.18 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/xid v1.4.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+6 -6
View File
@@ -1,10 +1,10 @@
package cmd
import (
"fmt"
"path"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
"github.com/go-pkgz/auth/avatar"
@@ -39,12 +39,12 @@ func (ac *AvatarCommand) Execute(_ []string) error {
src, err := ac.makeAvatarStore(ac.AvatarSrc)
if err != nil {
return errors.Wrapf(err, "can't make avatart store for %s", ac.AvatarSrc.Type)
return fmt.Errorf("can't make avatart store for %s: %w", ac.AvatarSrc.Type, err)
}
dst, err := ac.makeAvatarStore(ac.AvatarDst)
if err != nil {
return errors.Wrapf(err, "can't make avatart store for %s", ac.AvatarDst.Type)
return fmt.Errorf("can't make avatart store for %s: %w", ac.AvatarDst.Type, err)
}
if ac.migrator == nil {
@@ -72,14 +72,14 @@ func (ac *AvatarCommand) makeAvatarStore(gr AvatarGroup) (avatar.Store, error) {
switch gr.Type {
case "fs":
if err := makeDirs(gr.FS.Path); err != nil {
return nil, errors.Wrap(err, "failed to create avatar store")
return nil, fmt.Errorf("failed to create avatar store: %w", err)
}
return avatar.NewLocalFS(gr.FS.Path), nil
case "bolt":
if err := makeDirs(path.Dir(gr.Bolt.File)); err != nil {
return nil, errors.Wrap(err, "failed to create avatar store")
return nil, fmt.Errorf("failed to create avatar store: %w", err)
}
return avatar.NewBoltDB(gr.Bolt.File, bolt.Options{})
}
return nil, errors.Errorf("unsupported avatar store type %s", gr.Type)
return nil, fmt.Errorf("unsupported avatar store type %s", gr.Type)
}
+2 -2
View File
@@ -1,7 +1,7 @@
package cmd
import (
"errors"
"fmt"
"os"
"testing"
@@ -25,7 +25,7 @@ func TestAvatar_Execute(t *testing.T) {
assert.NoError(t, err)
// failed
cmd = AvatarCommand{migrator: &avatarMigratorMock{retCount: 0, retError: errors.New("failed blah")}}
cmd = AvatarCommand{migrator: &avatarMigratorMock{retCount: 0, retError: fmt.Errorf("failed blah")}}
cmd.SetCommon(CommonOpts{RemarkURL: "", SharedSecret: "123456"})
p = flags.NewParser(&cmd, flags.Default)
_, err = p.ParseArgs([]string{"--src.type=fs", "--src.fs.path=/tmp/ava-test", "--dst.type=bolt",
+4 -5
View File
@@ -9,7 +9,6 @@ import (
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
)
// BackupCommand set of flags and command for export
@@ -43,14 +42,14 @@ func (ec *BackupCommand) Execute(_ []string) error {
exportURL := fmt.Sprintf("%s/api/v1/admin/export?mode=file&site=%s", ec.RemarkURL, ec.Site)
req, err := http.NewRequest(http.MethodGet, exportURL, http.NoBody)
if err != nil {
return errors.Wrapf(err, "can't make export request for %s", exportURL)
return fmt.Errorf("can't make export request for %s: %w", exportURL, err)
}
req.SetBasicAuth("admin", ec.AdminPasswd)
// get with timeout
resp, err := client.Do(req.WithContext(ctx))
if err != nil {
return errors.Wrapf(err, "request failed for %s", exportURL)
return fmt.Errorf("request failed for %s: %w", exportURL, err)
}
defer func() {
if err = resp.Body.Close(); err != nil {
@@ -64,7 +63,7 @@ func (ec *BackupCommand) Execute(_ []string) error {
fh, err := os.Create(fname) //nolint:gosec // harmless
if err != nil {
return errors.Wrapf(err, "can't create backup file %s", fname)
return fmt.Errorf("can't create backup file %s: %w", fname, err)
}
defer func() { //nolint:gosec // false positive on defer without error check when it's checked here
if err = fh.Close(); err != nil {
@@ -73,7 +72,7 @@ func (ec *BackupCommand) Execute(_ []string) error {
}()
if _, err = io.Copy(fh, resp.Body); err != nil {
return errors.Wrapf(err, "failed to write backup file %s", fname)
return fmt.Errorf("failed to write backup file %s: %w", fname, err)
}
log.Printf("[INFO] export completed, file %s", fname)
+16 -17
View File
@@ -9,7 +9,6 @@ import (
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
)
@@ -39,7 +38,7 @@ func (cc *CleanupCommand) Execute(_ []string) error {
posts, err := cc.postsInRange(cc.From, cc.To)
if err != nil {
return errors.Wrap(err, "can't get posts")
return fmt.Errorf("can't get posts: %w", err)
}
log.Printf("[DEBUG] got %d posts", len(posts))
@@ -99,7 +98,7 @@ func (cc *CleanupCommand) procTitles(comments []store.Comment) {
func (cc *CleanupCommand) postsInRange(fromS, toS string) ([]store.PostInfo, error) {
posts, err := cc.listPosts()
if err != nil {
return nil, errors.Wrapf(err, "can't list posts for %s", cc.Site)
return nil, fmt.Errorf("can't list posts for %s: %w", cc.Site, err)
}
from, to := defaultFrom, defaultTo
@@ -107,14 +106,14 @@ func (cc *CleanupCommand) postsInRange(fromS, toS string) ([]store.PostInfo, err
if fromS != "" {
from, err = time.ParseInLocation("20060102", fromS, time.Local)
if err != nil {
return nil, errors.Wrap(err, "can't parse --from")
return nil, fmt.Errorf("can't parse --from: %w", err)
}
}
if toS != "" {
to, err = time.ParseInLocation("20060102", toS, time.Local)
if err != nil {
return nil, errors.Wrap(err, "can't parse --to")
return nil, fmt.Errorf("can't parse --to: %w", err)
}
}
@@ -133,17 +132,17 @@ func (cc *CleanupCommand) listPosts() ([]store.PostInfo, error) {
client := http.Client{Timeout: 30 * time.Second}
r, err := client.Get(listURL)
if err != nil {
return nil, errors.Wrapf(err, "get request failed for list of posts, site %s", cc.Site)
return nil, fmt.Errorf("get request failed for list of posts, site %s: %w", cc.Site, err)
}
defer func() { _ = r.Body.Close() }()
if r.StatusCode != 200 {
return nil, errors.Errorf("request %s failed with status %d", listURL, r.StatusCode)
return nil, fmt.Errorf("request %s failed with status %d", listURL, r.StatusCode)
}
list := []store.PostInfo{}
if err = json.NewDecoder(r.Body).Decode(&list); err != nil {
return nil, errors.Wrapf(err, "can't decode list of posts for site %s", cc.Site)
return nil, fmt.Errorf("can't decode list of posts for site %s: %w", cc.Site, err)
}
return list, nil
}
@@ -160,7 +159,7 @@ func (cc *CleanupCommand) listComments(postURL string) ([]store.Comment, error)
client := http.Client{Timeout: 30 * time.Second}
r, err = client.Get(commentsURL)
if err != nil {
return nil, errors.Wrapf(err, "get request failed for comments, %s", postURL)
return nil, fmt.Errorf("get request failed for comments, %s: %w", postURL, err)
}
if r.StatusCode == http.StatusTooManyRequests {
_ = r.Body.Close()
@@ -173,7 +172,7 @@ func (cc *CleanupCommand) listComments(postURL string) ([]store.Comment, error)
defer func() { _ = r.Body.Close() }()
if r.StatusCode != http.StatusOK {
return nil, errors.Errorf("request %s failed with status %d", commentsURL, r.StatusCode)
return nil, fmt.Errorf("request %s failed with status %d", commentsURL, r.StatusCode)
}
commentsWithInfo := struct {
@@ -182,7 +181,7 @@ func (cc *CleanupCommand) listComments(postURL string) ([]store.Comment, error)
}{}
if err = json.NewDecoder(r.Body).Decode(&commentsWithInfo); err != nil {
return nil, errors.Wrapf(err, "can't decode list of comments for %s", postURL)
return nil, fmt.Errorf("can't decode list of comments for %s: %w", postURL, err)
}
return commentsWithInfo.Comments, nil
}
@@ -192,18 +191,18 @@ func (cc *CleanupCommand) deleteComment(c store.Comment) error {
deleteURL := fmt.Sprintf("%s/api/v1/admin/comment/%s?site=%s&url=%s&format=plain", cc.RemarkURL, c.ID, cc.Site, c.Locator.URL)
req, err := http.NewRequest("DELETE", deleteURL, http.NoBody)
if err != nil {
return errors.Wrapf(err, "failed to make delete request for comment %s, %s", c.ID, c.Locator.URL)
return fmt.Errorf("failed to make delete request for comment %s, %s: %w", c.ID, c.Locator.URL, err)
}
req.SetBasicAuth("admin", cc.AdminPasswd)
client := http.Client{}
r, err := client.Do(req)
if err != nil {
return errors.Wrapf(err, "delete request failed for comment %s, %s", c.ID, c.Locator.URL)
return fmt.Errorf("delete request failed for comment %s, %s: %w", c.ID, c.Locator.URL, err)
}
defer func() { _ = r.Body.Close() }()
if r.StatusCode != http.StatusOK {
return errors.Errorf("delete request failed with status %s", r.Status)
return fmt.Errorf("delete request failed with status %s", r.Status)
}
return nil
}
@@ -213,18 +212,18 @@ func (cc *CleanupCommand) setTitle(c store.Comment) error {
titleURL := fmt.Sprintf("%s/api/v1/admin/title/%s?site=%s&url=%s&format=plain", cc.RemarkURL, c.ID, cc.Site, c.Locator.URL)
req, err := http.NewRequest("PUT", titleURL, http.NoBody)
if err != nil {
return errors.Wrapf(err, "failed to make title request for comment %s, %s", c.ID, c.Locator.URL)
return fmt.Errorf("failed to make title request for comment %s, %s: %w", c.ID, c.Locator.URL, err)
}
req.SetBasicAuth("admin", cc.AdminPasswd)
client := http.Client{}
r, err := client.Do(req)
if err != nil {
return errors.Wrapf(err, "title request failed for comment %s, %s", c.ID, c.Locator.URL)
return fmt.Errorf("title request failed for comment %s, %s: %w", c.ID, c.Locator.URL, err)
}
defer func() { _ = r.Body.Close() }()
if r.StatusCode != http.StatusOK {
return errors.Errorf("title request failed with status %s", r.Status)
return fmt.Errorf("title request failed with status %s", r.Status)
}
return nil
}
+4 -4
View File
@@ -4,6 +4,7 @@ package cmd
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
@@ -13,7 +14,6 @@ import (
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
)
// CommonOptionsCommander extends flags.Commander with SetCommon
@@ -87,7 +87,7 @@ func (p *fileParser) parse(now time.Time) (string, error) {
}
if err := template.Must(template.New("bb").Parse(fname)).Execute(&bb, fileTemplate); err != nil {
return "", errors.Wrapf(err, "failed to parse %q", fname)
return "", fmt.Errorf("failed to parse %q: %w", fname, err)
}
return bb.String(), nil
}
@@ -107,14 +107,14 @@ func responseError(resp *http.Response) error {
if e != nil {
body = []byte("")
}
return errors.Errorf("error response %q, %s", resp.Status, body)
return fmt.Errorf("error response %q, %s", resp.Status, body)
}
// mkdir -p for all dirs
func makeDirs(dirs ...string) error {
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0o700); err != nil { // If path is already a directory, MkdirAll does nothing
return errors.Wrapf(err, "can't make directory %s", dir)
return fmt.Errorf("can't make directory %s: %w", dir, err)
}
}
return nil
+6 -7
View File
@@ -11,7 +11,6 @@ import (
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
)
// ImportCommand set of flags and command for import
@@ -31,7 +30,7 @@ func (ic *ImportCommand) Execute(_ []string) error {
reader, err := ic.reader(ic.InputFile)
if err != nil {
return errors.Wrapf(err, "can't open import file %s", ic.InputFile)
return fmt.Errorf("can't open import file %s: %w", ic.InputFile, err)
}
client := http.Client{}
@@ -40,13 +39,13 @@ func (ic *ImportCommand) Execute(_ []string) error {
importURL := fmt.Sprintf("%s/api/v1/admin/import?site=%s&provider=%s", ic.RemarkURL, ic.Site, ic.Provider)
req, err := http.NewRequest(http.MethodPost, importURL, reader)
if err != nil {
return errors.Wrapf(err, "can't make import request for %s", importURL)
return fmt.Errorf("can't make import request for %s: %w", importURL, err)
}
req.SetBasicAuth("admin", ic.AdminPasswd)
resp, err := client.Do(req.WithContext(ctx)) // closes request's reader
if err != nil {
return errors.Wrapf(err, "request failed for %s", importURL)
return fmt.Errorf("request failed for %s: %w", importURL, err)
}
defer func() {
if err = resp.Body.Close(); err != nil {
@@ -59,7 +58,7 @@ func (ic *ImportCommand) Execute(_ []string) error {
body, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrap(err, "can't get response from importer")
return fmt.Errorf("can't get response from importer: %w", err)
}
log.Printf("[INFO] completed, status=%d, %s", resp.StatusCode, string(body))
@@ -70,13 +69,13 @@ func (ic *ImportCommand) Execute(_ []string) error {
func (ic *ImportCommand) reader(inp string) (reader io.Reader, err error) {
inpFile, err := os.Open(inp) // nolint
if err != nil {
return nil, errors.Wrapf(err, "import failed, can't open %s", inp)
return nil, fmt.Errorf("import failed, can't open %s: %w", inp, err)
}
reader = inpFile
if strings.HasSuffix(ic.InputFile, ".gz") {
if reader, err = gzip.NewReader(inpFile); err != nil {
return nil, errors.Wrap(err, "can't make gz reader")
return nil, fmt.Errorf("can't make gz reader: %w", err)
}
}
return reader, nil
+4 -5
View File
@@ -9,7 +9,6 @@ import (
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
)
// RemapCommand set of flags and command for change linkage between comments to
@@ -29,7 +28,7 @@ func (rc *RemapCommand) Execute(_ []string) error {
rulesReader, err := os.Open(rc.InputFile)
if err != nil {
return errors.Wrapf(err, "cant open file %s", rc.InputFile)
return fmt.Errorf("cant open file %s: %w", rc.InputFile, err)
}
client := http.Client{}
@@ -38,13 +37,13 @@ func (rc *RemapCommand) Execute(_ []string) error {
remapURL := fmt.Sprintf("%s/api/v1/admin/remap?site=%s", rc.RemarkURL, rc.Site)
req, err := http.NewRequest(http.MethodPost, remapURL, rulesReader)
if err != nil {
return errors.Wrapf(err, "can't make remap request for %s", remapURL)
return fmt.Errorf("can't make remap request for %s: %w", remapURL, err)
}
req.SetBasicAuth("admin", rc.AdminPasswd)
resp, err := client.Do(req.WithContext(ctx))
if err != nil {
return errors.Wrapf(err, "request failed for %s", remapURL)
return fmt.Errorf("request failed for %s: %w", remapURL, err)
}
defer func() {
if err = resp.Body.Close(); err != nil {
@@ -57,7 +56,7 @@ func (rc *RemapCommand) Execute(_ []string) error {
body, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrap(err, "can't get response")
return fmt.Errorf("can't get response: %w", err)
}
log.Printf("[INFO] completed, status=%d, %s", resp.StatusCode, string(body))
+35 -33
View File
@@ -18,7 +18,6 @@ import (
log "github.com/go-pkgz/lgr"
"github.com/golang-jwt/jwt"
"github.com/kyokomi/emoji/v2"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
"github.com/go-pkgz/auth"
@@ -450,27 +449,27 @@ func contains(s string, a []string) bool {
// doesn't start anything
func (s *ServerCommand) newServerApp(ctx context.Context) (*serverApp, error) {
if err := makeDirs(s.BackupLocation); err != nil {
return nil, errors.Wrap(err, "failed to create backup store")
return nil, fmt.Errorf("failed to create backup store: %w", err)
}
if !strings.HasPrefix(s.RemarkURL, "http://") && !strings.HasPrefix(s.RemarkURL, "https://") {
return nil, errors.Errorf("invalid remark42 url %s", s.RemarkURL)
return nil, fmt.Errorf("invalid remark42 url %s", s.RemarkURL)
}
log.Printf("[INFO] root url=%s", s.RemarkURL)
storeEngine, err := s.makeDataStore()
if err != nil {
return nil, errors.Wrap(err, "failed to make data store engine")
return nil, fmt.Errorf("failed to make data store engine: %w", err)
}
adminStore, err := s.makeAdminStore()
if err != nil {
return nil, errors.Wrap(err, "failed to make admin store")
return nil, fmt.Errorf("failed to make admin store: %w", err)
}
imageService, err := s.makePicturesStore()
if err != nil {
return nil, errors.Wrap(err, "failed to make pictures store")
return nil, fmt.Errorf("failed to make pictures store: %w", err)
}
log.Printf("[DEBUG] image service for url=%s, EditDuration=%v", imageService.ImageAPI, imageService.EditDuration)
@@ -492,13 +491,13 @@ func (s *ServerCommand) newServerApp(ctx context.Context) (*serverApp, error) {
loadingCache, err := s.makeCache()
if err != nil {
_ = dataService.Close()
return nil, errors.Wrap(err, "failed to make cache")
return nil, fmt.Errorf("failed to make cache: %w", err)
}
avatarStore, err := s.makeAvatarStore()
if err != nil {
_ = dataService.Close()
return nil, errors.Wrap(err, "failed to make avatar store")
return nil, fmt.Errorf("failed to make avatar store: %w", err)
}
authRefreshCache := newAuthRefreshCache()
authenticator := s.getAuthenticator(dataService, avatarStore, adminStore, authRefreshCache)
@@ -509,7 +508,7 @@ func (s *ServerCommand) newServerApp(ctx context.Context) (*serverApp, error) {
err = s.addAuthProviders(authenticator)
if err != nil {
_ = dataService.Close()
return nil, errors.Wrap(err, "failed to make authenticator")
return nil, fmt.Errorf("failed to make authenticator: %w", err)
}
exporter := &migrator.Native{DataStore: dataService}
@@ -548,7 +547,7 @@ func (s *ServerCommand) newServerApp(ctx context.Context) (*serverApp, error) {
sslConfig, err := s.makeSSLConfig()
if err != nil {
_ = dataService.Close()
return nil, errors.Wrap(err, "failed to make config of ssl server params")
return nil, fmt.Errorf("failed to make config of ssl server params: %w", err)
}
srv := &api.Rest{
@@ -587,7 +586,7 @@ func (s *ServerCommand) newServerApp(ctx context.Context) (*serverApp, error) {
da, errDevAuth := authenticator.DevAuth()
if errDevAuth != nil {
_ = dataService.Close()
return nil, errors.Wrap(errDevAuth, "can't make dev oauth2 server")
return nil, fmt.Errorf("can't make dev oauth2 server: %w", errDevAuth)
}
devAuth = da
}
@@ -687,7 +686,7 @@ func (s *ServerCommand) makeDataStore() (result engine.Interface, err error) {
switch s.Store.Type {
case "bolt":
if err = makeDirs(s.Store.Bolt.Path); err != nil {
return nil, errors.Wrap(err, "failed to create bolt store")
return nil, fmt.Errorf("failed to create bolt store: %w", err)
}
sites := []engine.BoltSite{}
for _, site := range s.Sites {
@@ -703,9 +702,12 @@ func (s *ServerCommand) makeDataStore() (result engine.Interface, err error) {
}}
return r, nil
default:
return nil, errors.Errorf("unsupported store type %s", s.Store.Type)
return nil, fmt.Errorf("unsupported store type %s", s.Store.Type)
}
return result, errors.Wrap(err, "can't initialize data store")
if err != nil {
return nil, fmt.Errorf("can't initialize data store: %w", err)
}
return result, nil
}
func (s *ServerCommand) makeAvatarStore() (avatar.Store, error) {
@@ -714,18 +716,18 @@ func (s *ServerCommand) makeAvatarStore() (avatar.Store, error) {
switch s.Avatar.Type {
case "fs":
if err := makeDirs(s.Avatar.FS.Path); err != nil {
return nil, errors.Wrap(err, "failed to create avatar store")
return nil, fmt.Errorf("failed to create avatar store: %w", err)
}
return avatar.NewLocalFS(s.Avatar.FS.Path), nil
case "bolt":
if err := makeDirs(path.Dir(s.Avatar.Bolt.File)); err != nil {
return nil, errors.Wrap(err, "failed to create avatar store")
return nil, fmt.Errorf("failed to create avatar store: %w", err)
}
return avatar.NewBoltDB(s.Avatar.Bolt.File, bolt.Options{})
case "uri":
return avatar.NewStore(s.Avatar.URI)
}
return nil, errors.Errorf("unsupported avatar store type %s", s.Avatar.Type)
return nil, fmt.Errorf("unsupported avatar store type %s", s.Avatar.Type)
}
func (s *ServerCommand) makePicturesStore() (*image.Service, error) {
@@ -746,7 +748,7 @@ func (s *ServerCommand) makePicturesStore() (*image.Service, error) {
return image.NewService(boltImageStore, imageServiceParams), nil
case "fs":
if err := makeDirs(s.Image.FS.Path); err != nil {
return nil, errors.Wrap(err, "failed to create pictures store")
return nil, fmt.Errorf("failed to create pictures store: %w", err)
}
return image.NewService(&image.FileSystem{
Location: s.Image.FS.Path,
@@ -762,7 +764,7 @@ func (s *ServerCommand) makePicturesStore() (*image.Service, error) {
AuthPasswd: s.Image.RPC.AuthPassword,
}}, imageServiceParams), nil
}
return nil, errors.Errorf("unsupported pictures store type %s", s.Image.Type)
return nil, fmt.Errorf("unsupported pictures store type %s", s.Image.Type)
}
func (s *ServerCommand) makeAdminStore() (admin.Store, error) {
@@ -788,7 +790,7 @@ func (s *ServerCommand) makeAdminStore() (admin.Store, error) {
}}
return r, nil
default:
return nil, errors.Errorf("unsupported admin store type %s", s.Admin.Type)
return nil, fmt.Errorf("unsupported admin store type %s", s.Admin.Type)
}
}
@@ -798,25 +800,25 @@ func (s *ServerCommand) makeCache() (LoadingCache, error) {
case "redis_pub_sub":
redisPubSub, err := eventbus.NewRedisPubSub(s.Cache.RedisAddr, "remark42-cache")
if err != nil {
return nil, errors.Wrap(err, "cache backend initialization, redis PubSub initialisation")
return nil, fmt.Errorf("cache backend initialization, redis PubSub initialisation: %w", err)
}
backend, err := cache.NewLruCache(cache.MaxCacheSize(s.Cache.Max.Size), cache.MaxValSize(s.Cache.Max.Value),
cache.MaxKeys(s.Cache.Max.Items), cache.EventBus(redisPubSub))
if err != nil {
return nil, errors.Wrap(err, "cache backend initialization")
return nil, fmt.Errorf("cache backend initialization: %w", err)
}
return cache.NewScache(backend), nil
case "mem":
backend, err := cache.NewLruCache(cache.MaxCacheSize(s.Cache.Max.Size), cache.MaxValSize(s.Cache.Max.Value),
cache.MaxKeys(s.Cache.Max.Items))
if err != nil {
return nil, errors.Wrap(err, "cache backend initialization")
return nil, fmt.Errorf("cache backend initialization: %w", err)
}
return cache.NewScache(backend), nil
case "none":
return cache.NewScache(&cache.Nop{}), nil
}
return nil, errors.Errorf("unsupported cache type %s", s.Cache.Type)
return nil, fmt.Errorf("unsupported cache type %s", s.Cache.Type)
}
func (s *ServerCommand) addAuthProviders(authenticator *auth.Service) error {
@@ -930,7 +932,7 @@ func (s *ServerCommand) loadEmailTemplate() (string, error) {
}
if err != nil {
return "", errors.Wrapf(err, "failed to read file %s", s.Auth.Email.MsgTemplate)
return "", fmt.Errorf("failed to read file %s: %w", s.Auth.Email.MsgTemplate, err)
}
return string(file), nil
@@ -986,7 +988,7 @@ func (s *ServerCommand) makeNotifyDestinations(authenticator *auth.Service) ([]n
}
webhook, err := notify.NewWebhook(client, whParams)
if err != nil {
return destinations, errors.Wrap(err, "failed to create webhook notification destination")
return destinations, fmt.Errorf("failed to create webhook notification destination: %w", err)
}
destinations = append(destinations, webhook)
}
@@ -994,7 +996,7 @@ func (s *ServerCommand) makeNotifyDestinations(authenticator *auth.Service) ([]n
if contains("slack", s.Notify.Admins) {
slack, err := notify.NewSlack(s.Notify.Slack.Token, s.Notify.Slack.Channel)
if err != nil {
return destinations, errors.Wrap(err, "failed to create slack notification destination")
return destinations, fmt.Errorf("failed to create slack notification destination: %w", err)
}
destinations = append(destinations, slack)
}
@@ -1022,7 +1024,7 @@ func (s *ServerCommand) makeNotifyDestinations(authenticator *auth.Service) ([]n
}
tkn, err := authenticator.TokenService().Token(claims)
if err != nil {
return "", errors.Wrapf(err, "failed to make unsubscription token")
return "", fmt.Errorf("failed to make unsubscription token: %w", err)
}
return tkn, nil
},
@@ -1040,7 +1042,7 @@ func (s *ServerCommand) makeNotifyDestinations(authenticator *auth.Service) ([]n
}
emailService, err := notify.NewEmail(emailParams, smtpParams)
if err != nil {
return destinations, errors.Wrap(err, "failed to create email notification destination")
return destinations, fmt.Errorf("failed to create email notification destination: %w", err)
}
destinations = append(destinations, emailService)
}
@@ -1051,7 +1053,7 @@ func (s *ServerCommand) makeNotifyDestinations(authenticator *auth.Service) ([]n
// constructs Telegram notify service
func (s *ServerCommand) makeTelegramNotify() (*notify.Telegram, error) {
if contains("telegram", s.Notify.Admins) && s.Notify.Telegram.Channel == "" {
return nil, errors.New("--notify.telegram.channel must be set for admin notifications to work")
return nil, fmt.Errorf("--notify.telegram.channel must be set for admin notifications to work")
}
telegramParams := notify.TelegramParams{
AdminChannelID: s.Notify.Telegram.Channel,
@@ -1062,7 +1064,7 @@ func (s *ServerCommand) makeTelegramNotify() (*notify.Telegram, error) {
}
tg, err := notify.NewTelegram(telegramParams)
if err != nil {
return nil, errors.Wrap(err, "failed to create telegram notification destination")
return nil, fmt.Errorf("failed to create telegram notification destination: %w", err)
}
return tg, nil
}
@@ -1073,10 +1075,10 @@ func (s *ServerCommand) makeSSLConfig() (config api.SSLConfig, err error) {
config.SSLMode = api.None
case "static":
if s.SSL.Cert == "" {
return config, errors.New("path to cert.pem is required")
return config, fmt.Errorf("path to cert.pem is required")
}
if s.SSL.Key == "" {
return config, errors.New("path to key.pem is required")
return config, fmt.Errorf("path to key.pem is required")
}
config.SSLMode = api.Static
config.Port = s.SSL.Port
+4 -5
View File
@@ -10,7 +10,6 @@ import (
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
)
// AutoBackup struct handles daily backups params for siteID
@@ -50,18 +49,18 @@ func (ab AutoBackup) makeBackup() (string, error) {
backupFile := fmt.Sprintf("%s/backup-%s-%s.gz", ab.BackupLocation, ab.SiteID, time.Now().Format("20060102"))
fh, err := os.Create(backupFile) //nolint:gosec // harmless
if err != nil {
return "", errors.Wrapf(err, "can't create backup file %s", backupFile)
return "", fmt.Errorf("can't create backup file %s: %w", backupFile, err)
}
gz := gzip.NewWriter(fh)
if _, err = ab.Exporter.Export(gz, ab.SiteID); err != nil {
return "", errors.Wrapf(err, "export failed for %s", ab.SiteID)
return "", fmt.Errorf("export failed for %s: %w", ab.SiteID, err)
}
if err = gz.Close(); err != nil {
return "", errors.Wrapf(err, "can't close gz for %s", backupFile)
return "", fmt.Errorf("can't close gz for %s: %w", backupFile, err)
}
if err = fh.Close(); err != nil {
return "", errors.Wrapf(err, "can't close file handler for %s", backupFile)
return "", fmt.Errorf("can't close file handler for %s: %w", backupFile, err)
}
log.Printf("[DEBUG] created backup file %s", backupFile)
return backupFile, nil
+3 -3
View File
@@ -2,10 +2,10 @@ package migrator
import (
"encoding/json"
"fmt"
"io"
"time"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
log "github.com/go-pkgz/lgr"
@@ -68,9 +68,9 @@ func (d *Commento) Import(r io.Reader, siteID string) (size int, err error) {
}
if failed > 0 {
err = errors.Errorf("failed to save %d comments", failed)
err = fmt.Errorf("failed to save %d comments", failed)
if passed == 0 {
err = errors.New("import failed")
err = fmt.Errorf("import failed")
}
}
+2 -1
View File
@@ -7,11 +7,12 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
bolt "go.etcd.io/bbolt"
"github.com/umputun/remark42/backend/app/store"
"github.com/umputun/remark42/backend/app/store/admin"
"github.com/umputun/remark42/backend/app/store/engine"
"github.com/umputun/remark42/backend/app/store/service"
bolt "go.etcd.io/bbolt"
)
func TestCommento_Import(t *testing.T) {
+3 -3
View File
@@ -2,12 +2,12 @@ package migrator
import (
"encoding/xml"
"fmt"
"io"
"strings"
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
)
@@ -68,9 +68,9 @@ func (d *Disqus) Import(r io.Reader, siteID string) (size int, err error) {
}
if failed > 0 {
err = errors.Errorf("failed to save %d comments", failed)
err = fmt.Errorf("failed to save %d comments", failed)
if passed == 0 {
err = errors.New("import failed")
err = fmt.Errorf("import failed")
}
}
+3 -3
View File
@@ -4,11 +4,11 @@
package migrator
import (
"fmt"
"io"
"os"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
"github.com/umputun/remark42/backend/app/store/service"
@@ -69,12 +69,12 @@ func ImportComments(p ImportParams) (int, error) {
case "native":
importer = &Native{DataStore: p.DataStore}
default:
return 0, errors.Errorf("unsupported import provider %s", p.Provider)
return 0, fmt.Errorf("unsupported import provider %s", p.Provider)
}
fh, err := os.Open(p.InputFile)
if err != nil {
return 0, errors.Wrapf(err, "can't open import file %s", p.InputFile)
return 0, fmt.Errorf("can't open import file %s: %w", p.InputFile, err)
}
defer func() { //nolint:gosec // false positive on defer without error check when it's checked here
+9 -9
View File
@@ -4,12 +4,12 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"sync/atomic"
log "github.com/go-pkgz/lgr"
"github.com/go-pkgz/syncs"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
"github.com/umputun/remark42/backend/app/store/service"
@@ -36,7 +36,7 @@ type meta struct {
// The final file is a valid json
func (n *Native) Export(w io.Writer, siteID string) (size int, err error) {
if err = n.exportMeta(siteID, w); err != nil {
return 0, errors.Wrapf(err, "failed to export meta for site %s", siteID)
return 0, fmt.Errorf("failed to export meta for site %s: %w", siteID, err)
}
topics, err := n.DataStore.List(siteID, 0, 0)
@@ -59,10 +59,10 @@ func (n *Native) Export(w io.Writer, siteID string) (size int, err error) {
enc.SetEscapeHTML(false)
if err = enc.Encode(comment); err != nil {
return commentsCount, errors.Wrapf(err, "can't marshal %v", comments)
return commentsCount, fmt.Errorf("can't marshal %v: %w", comments, err)
}
if _, err = w.Write(buf.Bytes()); err != nil {
return commentsCount, errors.Wrap(err, "can't write comment data")
return commentsCount, fmt.Errorf("can't write comment data: %w", err)
}
commentsCount++
}
@@ -76,11 +76,11 @@ func (n *Native) exportMeta(siteID string, w io.Writer) (err error) {
m := meta{Version: nativeVersion}
m.Users, m.Posts, err = n.DataStore.Metas(siteID)
if err != nil {
return errors.Wrap(err, "can't get meta")
return fmt.Errorf("can't get meta: %w", err)
}
if err = json.NewEncoder(w).Encode(m); err != nil {
return errors.Wrap(err, "can't encode meta")
return fmt.Errorf("can't encode meta: %w", err)
}
return nil
}
@@ -131,11 +131,11 @@ func (n *Native) Import(reader io.Reader, siteID string) (size int, err error) {
m := meta{}
dec := json.NewDecoder(reader)
if err = dec.Decode(&m); err != nil {
return 0, errors.Wrapf(err, "failed to import meta for site %s", siteID)
return 0, fmt.Errorf("failed to import meta for site %s: %w", siteID, err)
}
if m.Version != nativeVersion && m.Version != 0 { // this version allows back compatibility with 0 version
return 0, errors.Errorf("unexpected import file version %d", m.Version)
return 0, fmt.Errorf("unexpected import file version %d", m.Version)
}
if e := n.DataStore.DeleteAll(siteID); e != nil {
@@ -183,7 +183,7 @@ func (n *Native) Import(reader io.Reader, siteID string) (size int, err error) {
grp.Wait()
if failed > 0 {
return int(comments), errors.Errorf("failed to save %d comments", failed)
return int(comments), fmt.Errorf("failed to save %d comments", failed)
}
log.Printf("[INFO] imported %d comments from %d records", comments, total)
+3 -3
View File
@@ -2,12 +2,12 @@ package migrator
import (
"encoding/xml"
"fmt"
"html"
"io"
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
)
@@ -75,9 +75,9 @@ func (w *WordPress) Import(r io.Reader, siteID string) (size int, err error) {
}
if failed > 0 {
err = errors.Errorf("failed to save %d comments", failed)
err = fmt.Errorf("failed to save %d comments", failed)
if passed == 0 {
err = errors.New("import failed")
err = fmt.Errorf("import failed")
}
}
+20 -16
View File
@@ -129,7 +129,7 @@ func NewEmail(emailParams EmailParams, smtpParams SMTPParams) (*Email, error) {
// initialize templates
err := res.setTemplates()
if err != nil {
return nil, errors.Wrap(err, "can't set templates")
return nil, fmt.Errorf("can't set templates: %w", err)
}
log.Printf("[DEBUG] Create new email notifier for server %s with user %s, timeout=%s",
@@ -173,7 +173,7 @@ func (e *Email) setTemplates() error {
func (e *Email) Send(ctx context.Context, req Request) error {
select {
case <-ctx.Done():
return errors.Errorf("sending email messages about comment %q aborted due to canceled context", req.Comment.ID)
return fmt.Errorf("sending email messages about comment %q aborted due to canceled context", req.Comment.ID)
default:
}
@@ -181,12 +181,16 @@ func (e *Email) Send(ctx context.Context, req Request) error {
for _, email := range req.Emails {
err := e.buildAndSendMessage(ctx, req, email, false)
result = multierror.Append(errors.Wrapf(err, "problem sending user email notification to %q", email))
if err != nil {
result = multierror.Append(fmt.Errorf("problem sending user email notification to %q: %w", email, err))
}
}
for _, email := range e.AdminEmails {
err := e.buildAndSendMessage(ctx, req, email, true)
result = multierror.Append(errors.Wrapf(err, "problem sending admin email notification to %q", email))
if err != nil {
result = multierror.Append(fmt.Errorf("problem sending admin email notification to %q: %w", email, err))
}
}
return result.ErrorOrNil()
@@ -215,7 +219,7 @@ func (e *Email) SendVerification(ctx context.Context, req VerificationRequest) e
}
select {
case <-ctx.Done():
return errors.Errorf("sending message to %q aborted due to canceled context", req.User)
return fmt.Errorf("sending message to %q aborted due to canceled context", req.User)
default:
}
@@ -338,11 +342,11 @@ func (e *Email) buildMessage(subject, body, to, contentType, unsubscribeLink str
// Thread safe.
func (e *Email) sendMessage(m emailMessage) error {
if e.smtp == nil {
return errors.New("sendMessage called without client set")
return fmt.Errorf("sendMessage called without client set")
}
client, err := e.smtp.Create(e.SMTPParams)
if err != nil {
return errors.Wrap(err, "failed to make smtp Create")
return fmt.Errorf("failed to make smtp Create: %w", err)
}
defer func() {
@@ -355,15 +359,15 @@ func (e *Email) sendMessage(m emailMessage) error {
}()
if err = client.Mail(m.from); err != nil {
return errors.Wrapf(err, "bad from address %q", m.from)
return fmt.Errorf("bad from address %q: %w", m.from, err)
}
if err = client.Rcpt(m.to); err != nil {
return errors.Wrapf(err, "bad to address %q", m.to)
return fmt.Errorf("bad to address %q: %w", m.to, err)
}
writer, err := client.Data()
if err != nil {
return errors.Wrap(err, "can't make email writer")
return fmt.Errorf("can't make email writer: %w", err)
}
defer func() {
@@ -374,7 +378,7 @@ func (e *Email) sendMessage(m emailMessage) error {
buf := bytes.NewBufferString(m.message)
if _, err = buf.WriteTo(writer); err != nil {
return errors.Wrapf(err, "failed to send email body to %q", m.to)
return fmt.Errorf("failed to send email body to %q: %w", m.to, err)
}
return nil
@@ -394,7 +398,7 @@ func (s *emailClient) Create(params SMTPParams) (smtpClient, error) {
}
auth := smtp.PlainAuth("", params.Username, params.Password, params.Host)
if err := c.Auth(auth); err != nil {
return errors.Wrapf(err, "failed to auth to smtp %s:%d", params.Host, params.Port)
return fmt.Errorf("failed to auth to smtp %s:%d: %w", params.Host, params.Port, err)
}
return nil
}
@@ -409,22 +413,22 @@ func (s *emailClient) Create(params SMTPParams) (smtpClient, error) {
}
conn, err := tls.Dial("tcp", srvAddress, tlsConf)
if err != nil {
return nil, errors.Wrapf(err, "failed to dial smtp tls to %s", srvAddress)
return nil, fmt.Errorf("failed to dial smtp tls to %s: %w", srvAddress, err)
}
if c, err = smtp.NewClient(conn, params.Host); err != nil {
return nil, errors.Wrapf(err, "failed to make smtp client for %s", srvAddress)
return nil, fmt.Errorf("failed to make smtp client for %s: %w", srvAddress, err)
}
return c, authenticate(c)
}
conn, err := net.DialTimeout("tcp", srvAddress, params.TimeOut)
if err != nil {
return nil, errors.Wrapf(err, "timeout connecting to %s", srvAddress)
return nil, fmt.Errorf("timeout connecting to %s: %w", srvAddress, err)
}
c, err = smtp.NewClient(conn, params.Host)
if err != nil {
return nil, errors.Wrap(err, "failed to dial")
return nil, fmt.Errorf("failed to dial: %w", err)
}
return c, authenticate(c)
+8 -8
View File
@@ -3,7 +3,7 @@ package notify
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/smtp"
"sync"
@@ -361,7 +361,7 @@ type fakeTestSMTP struct {
func (f *fakeTestSMTP) Create(SMTPParams) (smtpClient, error) {
if f.fail["create"] {
return nil, errors.New("failed to create client")
return nil, fmt.Errorf("failed to create client")
}
return f, nil
}
@@ -373,7 +373,7 @@ func (f *fakeTestSMTP) Mail(m string) error {
f.mail = m
f.lock.Unlock()
if f.fail["mail"] {
return errors.New("failed to verify sender")
return fmt.Errorf("failed to verify sender")
}
return nil
}
@@ -383,7 +383,7 @@ func (f *fakeTestSMTP) Rcpt(r string) error {
f.rcpt = r
f.lock.Unlock()
if f.fail["rcpt"] {
return errors.New("failed to verify receiver")
return fmt.Errorf("failed to verify receiver")
}
return nil
}
@@ -393,7 +393,7 @@ func (f *fakeTestSMTP) Quit() error {
f.quitCount++
f.lock.Unlock()
if f.fail["quit"] {
return errors.New("failed to quit")
return fmt.Errorf("failed to quit")
}
return nil
}
@@ -401,14 +401,14 @@ func (f *fakeTestSMTP) Quit() error {
func (f *fakeTestSMTP) Close() error {
f.close = true
if f.fail["close"] {
return errors.New("failed to close")
return fmt.Errorf("failed to close")
}
return nil
}
func (f *fakeTestSMTP) Data() (io.WriteCloser, error) {
if f.fail["data"] {
return nil, errors.New("failed to send")
return nil, fmt.Errorf("failed to send")
}
return nopCloser{&f.buff}, nil
}
@@ -433,7 +433,7 @@ func (f *fakeTestSMTP) readQuitCount() int {
func TokenGenFn(user, _, _ string) (string, error) {
if user == "error" {
return "", errors.New("token generation error")
return "", fmt.Errorf("token generation error")
}
return "token", nil
}
+2 -3
View File
@@ -1,7 +1,6 @@
package notify
import (
"errors"
"fmt"
"math/rand"
"sync/atomic"
@@ -284,7 +283,7 @@ type mockStore struct {
func (m mockStore) getUserDetail(userID string) (string, error) {
detail, ok := m.userDetails[userID]
if !ok {
return "", errors.New("no such user")
return "", fmt.Errorf("no such user")
}
return detail, nil
}
@@ -292,7 +291,7 @@ func (m mockStore) getUserDetail(userID string) (string, error) {
func (m mockStore) Get(_ store.Locator, id string, _ store.User) (store.Comment, error) {
res, ok := m.data[id]
if !ok {
return store.Comment{}, errors.New("no such id")
return store.Comment{}, fmt.Errorf("no such id")
}
return res, nil
}
+3 -3
View File
@@ -2,9 +2,9 @@ package notify
import (
"context"
"fmt"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/slack-go/slack"
)
@@ -26,7 +26,7 @@ func NewSlack(token, channelName string, opts ...slack.Option) (*Slack, error) {
channelID, err := res.findChannelIDByName(channelName)
if err != nil {
return nil, errors.Wrap(err, "can not find slack channel '"+channelName+"'")
return nil, fmt.Errorf("can not find slack channel '"+channelName+"': %w", err)
}
res.channelID = channelID
@@ -91,5 +91,5 @@ func (t *Slack) findChannelIDByName(name string) (string, error) {
}
params.Cursor = next
}
return "", errors.New("no such channel")
return "", fmt.Errorf("no such channel")
}
+17 -18
View File
@@ -5,7 +5,6 @@ import (
"context"
"encoding/json"
"fmt"
"golang.org/x/net/html"
"io"
"net/http"
neturl "net/url"
@@ -15,12 +14,12 @@ import (
"sync/atomic"
"time"
"github.com/hashicorp/go-multierror"
"github.com/microcosm-cc/bluemonday"
log "github.com/go-pkgz/lgr"
"github.com/go-pkgz/repeater"
"github.com/hashicorp/go-multierror"
"github.com/microcosm-cc/bluemonday"
"github.com/pkg/errors"
"golang.org/x/net/html"
)
// TelegramParams contain settings for telegram notifications
@@ -111,7 +110,7 @@ func (t *Telegram) Send(ctx context.Context, req Request) error {
msg, err := buildMessage(req)
if err != nil {
return errors.Wrapf(err, "failed to make telegram message body for comment ID %s", req.Comment.ID)
return fmt.Errorf("failed to make telegram message body for comment ID %s: %w", req.Comment.ID, err)
}
if t.AdminChannelID != "" {
@@ -269,22 +268,22 @@ func (t *Telegram) CheckToken(token, user string) (telegram, site string, err er
t.requests.RUnlock()
if !ok {
return "", "", errors.New("request is not found")
return "", "", fmt.Errorf("request is not found")
}
if time.Now().After(authRequest.expires) {
t.requests.Lock()
delete(t.requests.data, token)
t.requests.Unlock()
return "", "", errors.New("request expired")
return "", "", fmt.Errorf("request expired")
}
if !authRequest.confirmed {
return "", "", errors.New("request is not verified yet")
return "", "", fmt.Errorf("request is not verified yet")
}
if authRequest.user != user {
return "", "", errors.New("user does not match original requester")
return "", "", fmt.Errorf("user does not match original requester")
}
// Delete request
@@ -333,7 +332,7 @@ func (t *Telegram) Run(ctx context.Context) {
// so that caller could get updates and send it not only there but to multiple sources
func (t *Telegram) ProcessUpdate(ctx context.Context, textUpdate string) error {
if atomic.LoadInt32(&t.run) != 0 {
return errors.New("Run goroutine should not be used with ProcessUpdate")
return fmt.Errorf("the Run goroutine should not be used with ProcessUpdate")
}
defer func() {
// as Run goroutine is not running, clean up old requests on each update
@@ -349,7 +348,7 @@ func (t *Telegram) ProcessUpdate(ctx context.Context, textUpdate string) error {
}()
var updates TelegramUpdate
if err := json.Unmarshal([]byte(textUpdate), &updates); err != nil {
return errors.Wrap(err, "failed to decode provided telegram update")
return fmt.Errorf("failed to decode provided telegram update: %w", err)
}
t.processUpdates(ctx, &updates)
return nil
@@ -377,7 +376,7 @@ func (t *Telegram) getUpdates(ctx context.Context) (*TelegramUpdate, error) {
err := t.Request(ctx, url, nil, &result)
if err != nil {
return nil, errors.Wrap(err, "failed to fetch updates")
return nil, fmt.Errorf("failed to fetch updates: %w", err)
}
for _, u := range result.Result {
@@ -445,7 +444,7 @@ func (t *Telegram) botInfo(ctx context.Context) (*TelegramBotInfo, error) {
return nil, err
}
if resp.Result == nil {
return nil, errors.New("received empty result")
return nil, fmt.Errorf("received empty result")
}
return resp.Result, nil
@@ -465,13 +464,13 @@ func (t *Telegram) Request(ctx context.Context, method string, b []byte, data in
req.Header.Set("Content-Type", "application/json; charset=utf-8")
}
if err != nil {
return errors.Wrap(err, "failed to create request")
return fmt.Errorf("failed to create request: %w", err)
}
client := http.Client{Timeout: t.Timeout}
resp, err := client.Do(req)
if err != nil {
return errors.Wrap(err, "failed to send request")
return fmt.Errorf("failed to send request: %w", err)
}
defer resp.Body.Close()
@@ -480,7 +479,7 @@ func (t *Telegram) Request(ctx context.Context, method string, b []byte, data in
}
if err = json.NewDecoder(resp.Body).Decode(data); err != nil {
return errors.Wrap(err, "failed to decode json response")
return fmt.Errorf("failed to decode json response: %w", err)
}
return nil
@@ -492,7 +491,7 @@ func (t *Telegram) parseError(r io.Reader, statusCode int) error {
Description string `json:"description"`
}{}
if err := json.NewDecoder(r).Decode(&tgErr); err != nil {
return errors.Errorf("unexpected telegram API status code %d", statusCode)
return fmt.Errorf("unexpected telegram API status code %d", statusCode)
}
return errors.Errorf("unexpected telegram API status code %d, error: %q", statusCode, tgErr.Description)
return fmt.Errorf("unexpected telegram API status code %d, error: %q", statusCode, tgErr.Description)
}
+1 -1
View File
@@ -453,7 +453,7 @@ func TestTelegram_TokenVerification(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
go tb.Run(ctx)
assert.Eventually(t, func() bool {
return tb.ProcessUpdate(ctx, "").Error() == "Run goroutine should not be used with ProcessUpdate"
return tb.ProcessUpdate(ctx, "").Error() == "the Run goroutine should not be used with ProcessUpdate"
}, time.Millisecond*100, time.Millisecond*10, "ProcessUpdate should not work same time as Run")
tb.AddToken("expired token", "user", "site", time.Now().Add(-time.Minute))
tb.requests.RLock()
+5 -5
View File
@@ -40,7 +40,7 @@ type Webhook struct {
func NewWebhook(client WebhookClient, params WebhookParams) (*Webhook, error) {
res := &Webhook{WebhookParams: params}
if res.WebhookURL == "" {
return nil, errors.New("webhook URL is required for webhook notifications")
return nil, fmt.Errorf("webhook URL is required for webhook notifications")
}
if res.Template == "" {
@@ -49,7 +49,7 @@ func NewWebhook(client WebhookClient, params WebhookParams) (*Webhook, error) {
payloadTmpl, err := template.New("webhook").Parse(res.Template)
if err != nil {
return nil, errors.Wrap(err, "unable to parse webhook template")
return nil, fmt.Errorf("unable to parse webhook template: %w", err)
}
res.webhookClient = client
@@ -65,12 +65,12 @@ func (t *Webhook) Send(ctx context.Context, req Request) error {
var payload bytes.Buffer
err := t.webhookTemplate.Execute(&payload, req.Comment)
if err != nil {
return errors.Wrap(err, "unable to compile webhook template")
return fmt.Errorf("unable to compile webhook template: %w", err)
}
httpReq, err := http.NewRequestWithContext(ctx, "POST", t.WebhookURL, &payload)
if err != nil {
return errors.Wrap(err, "unable to create webhook request")
return fmt.Errorf("unable to create webhook request: %w", err)
}
for _, h := range t.Headers {
@@ -83,7 +83,7 @@ func (t *Webhook) Send(ctx context.Context, req Request) error {
resp, err := t.webhookClient.Do(httpReq)
if err != nil {
return errors.Wrap(err, "webhook request failed")
return fmt.Errorf("webhook request failed: %w", err)
}
defer resp.Body.Close()
+3 -3
View File
@@ -3,7 +3,7 @@ package notify
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"testing"
@@ -31,7 +31,7 @@ type errReader struct {
}
func (errReader) Read(p []byte) (n int, err error) {
return 0, errors.New("test error")
return 0, fmt.Errorf("test error")
}
func TestWebhook_NewWebhook(t *testing.T) {
@@ -104,7 +104,7 @@ func TestWebhook_Send(t *testing.T) {
assert.Contains(t, err.Error(), "unable to create webhook request")
wh, err = NewWebhook(funcWebhookClient(func(*http.Request) (*http.Response, error) {
return nil, errors.New("request failed")
return nil, fmt.Errorf("request failed")
}), WebhookParams{WebhookURL: "https://not-existing-url.net"})
assert.NoError(t, err)
err = wh.Send(context.TODO(), Request{Comment: c})
+3 -3
View File
@@ -3,7 +3,7 @@ package providers
import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
"time"
@@ -46,7 +46,7 @@ func (m *mockTGRequester) Request(_ context.Context, _ string, _ []byte, data in
assert.NoError(m.t, json.Unmarshal([]byte(getUpdatesResp), data))
return nil
}
return errors.New("test error")
return fmt.Errorf("test error")
}
type mockTGUpdatesReceiver struct {
@@ -68,5 +68,5 @@ func (m *mockTGUpdatesReceiver) ProcessUpdate(_ context.Context, textUpdate stri
return nil
}
assert.Nil(m.t, result.Result)
return errors.New("test error")
return fmt.Errorf("test error")
}
+3 -3
View File
@@ -1,7 +1,7 @@
package api
import (
"errors"
"fmt"
"net/http"
"path"
"time"
@@ -103,7 +103,7 @@ func (a *admin) deleteMeRequestCtrl(w http.ResponseWriter, r *http.Request) {
// deleteme set by deleteMeCtrl, this check just to make sure we not trying to delete with leaked token
if !claims.User.BoolAttr("delete_me") {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("forbidden"), "can't use provided token", rest.ErrNoAccess)
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("forbidden"), "can't use provided token", rest.ErrNoAccess)
return
}
@@ -183,7 +183,7 @@ func (a *admin) setReadOnlyCtrl(w http.ResponseWriter, r *http.Request) {
// don't allow to reset ro for posts turned to ro by ReadOnlyAge
if !roStatus {
if info, e := a.dataService.Info(locator, a.readOnlyAge); e == nil && isRoByAge(info) {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"),
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("rejected"),
"read-only due the age", rest.ErrActionRejected)
return
}
+5 -6
View File
@@ -15,7 +15,6 @@ import (
cache "github.com/go-pkgz/lcw"
log "github.com/go-pkgz/lgr"
R "github.com/go-pkgz/rest"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/migrator"
"github.com/umputun/remark42/backend/app/rest"
@@ -47,7 +46,7 @@ func (m *Migrator) importCtrl(w http.ResponseWriter, r *http.Request) {
siteID := r.URL.Query().Get("site")
if m.isBusy(siteID) {
rest.SendErrorJSON(w, r, http.StatusConflict, errors.New("already running"),
rest.SendErrorJSON(w, r, http.StatusConflict, fmt.Errorf("already running"),
"import rejected", rest.ErrActionRejected)
return
}
@@ -70,7 +69,7 @@ func (m *Migrator) importFormCtrl(w http.ResponseWriter, r *http.Request) {
siteID := r.URL.Query().Get("site")
if m.isBusy(siteID) {
rest.SendErrorJSON(w, r, http.StatusConflict, errors.New("already running"),
rest.SendErrorJSON(w, r, http.StatusConflict, fmt.Errorf("already running"),
"import rejected", rest.ErrActionRejected)
return
}
@@ -253,15 +252,15 @@ func (m *Migrator) runImport(siteID, provider, tmpfile string) {
func (m *Migrator) saveTemp(r io.Reader) (string, error) {
tmpfile, err := ioutil.TempFile("", "remark42_import")
if err != nil {
return "", errors.Wrap(err, "can't make temp file")
return "", fmt.Errorf("can't make temp file: %w", err)
}
if _, err = io.Copy(tmpfile, r); err != nil {
return "", errors.Wrap(err, "can't copy to temp file")
return "", fmt.Errorf("can't copy to temp file: %w", err)
}
if err = tmpfile.Close(); err != nil {
return "", errors.Wrap(err, "can't close temp file")
return "", fmt.Errorf("can't close temp file: %w", err)
}
return tmpfile.Name(), nil
+1 -2
View File
@@ -21,7 +21,6 @@ import (
log "github.com/go-pkgz/lgr"
R "github.com/go-pkgz/rest"
"github.com/go-pkgz/rest/logger"
"github.com/pkg/errors"
"github.com/rakyll/statik/fs"
"github.com/umputun/remark42/backend/app/notify"
@@ -505,7 +504,7 @@ func encodeJSONWithHTML(v interface{}) ([]byte, error) {
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
if err := enc.Encode(v); err != nil {
return nil, errors.Wrap(err, "json encoding failed")
return nil, fmt.Errorf("json encoding failed: %w", err)
}
return buf.Bytes(), nil
}
+20 -21
View File
@@ -22,7 +22,6 @@ import (
R "github.com/go-pkgz/rest"
"github.com/golang-jwt/jwt"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/notify"
"github.com/umputun/remark42/backend/app/rest"
@@ -142,12 +141,12 @@ func (s *private) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
// check if user blocked
if s.dataService.IsBlocked(comment.Locator.SiteID, comment.User.ID) {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "user blocked", rest.ErrUserBlocked)
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("rejected"), "user blocked", rest.ErrUserBlocked)
return
}
if s.isReadOnly(comment.Locator) {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "old post, read-only", rest.ErrReadOnly)
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("rejected"), "old post, read-only", rest.ErrReadOnly)
return
}
@@ -207,7 +206,7 @@ func (s *private) updateCommentCtrl(w http.ResponseWriter, r *http.Request) {
}
if currComment.User.ID != user.ID {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"),
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("rejected"),
"can not edit comments for other users", rest.ErrNoAccess)
return
}
@@ -268,13 +267,13 @@ func (s *private) voteCtrl(w http.ResponseWriter, r *http.Request) {
vote := r.URL.Query().Get("vote") == "1"
if s.isReadOnly(locator) {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "old post, read-only", rest.ErrReadOnly)
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("rejected"), "old post, read-only", rest.ErrReadOnly)
return
}
// check if user blocked
if s.dataService.IsBlocked(locator.SiteID, user.ID) {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "user blocked", rest.ErrUserBlocked)
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("rejected"), "user blocked", rest.ErrUserBlocked)
return
}
@@ -317,7 +316,7 @@ func (s *private) sendEmailConfirmationCtrl(w http.ResponseWriter, r *http.Reque
siteID := r.URL.Query().Get("site")
if address == "" {
rest.SendErrorJSON(w, r, http.StatusBadRequest,
errors.New("missing parameter"), "address parameter is required", rest.ErrInternal)
fmt.Errorf("missing parameter"), "address parameter is required", rest.ErrInternal)
return
}
existingAddress, err := s.dataService.GetUserEmail(siteID, user.ID)
@@ -326,7 +325,7 @@ func (s *private) sendEmailConfirmationCtrl(w http.ResponseWriter, r *http.Reque
}
if address == existingAddress {
rest.SendErrorJSON(w, r, http.StatusConflict,
errors.New("already verified"), "email address is already verified for this user", rest.ErrInternal)
fmt.Errorf("already verified"), "email address is already verified for this user", rest.ErrInternal)
return
}
claims := token.Claims{
@@ -364,7 +363,7 @@ func (s *private) telegramSubscribeCtrl(w http.ResponseWriter, r *http.Request)
if s.telegramService == nil {
rest.SendErrorJSON(w, r, http.StatusInternalServerError,
errors.New("not enabled"), "telegram notifications are not enabled", rest.ErrActionRejected)
fmt.Errorf("not enabled"), "telegram notifications are not enabled", rest.ErrActionRejected)
return
}
@@ -373,13 +372,13 @@ func (s *private) telegramSubscribeCtrl(w http.ResponseWriter, r *http.Request)
// GET /telegram/subscribe?site=siteID (No token supplied)
siteID := r.URL.Query().Get("site")
if siteID == "" {
rest.SendErrorJSON(w, r, http.StatusBadRequest, errors.New("missing parameter"), "site parameter is required", rest.ErrInternal)
rest.SendErrorJSON(w, r, http.StatusBadRequest, fmt.Errorf("missing parameter"), "site parameter is required", rest.ErrInternal)
return
}
// we don't care as much if we can't retrieve the current value of that field for the user, so ignore the error
if existingAddress, _ := s.dataService.GetUserTelegram(siteID, user.ID); existingAddress != "" {
rest.SendErrorJSON(w, r, http.StatusConflict,
errors.New("already subscribed"), "telegram subscription is already set for this user, delete if first to re-subscribe", rest.ErrActionRejected)
fmt.Errorf("already subscribed"), "telegram subscription is already set for this user, delete if first to re-subscribe", rest.ErrActionRejected)
return
}
// Generate and send token
@@ -422,7 +421,7 @@ func (s *private) telegramSubscribeCtrl(w http.ResponseWriter, r *http.Request)
func (s *private) setConfirmedEmailCtrl(w http.ResponseWriter, r *http.Request) {
tkn := r.URL.Query().Get("tkn")
if tkn == "" {
rest.SendErrorJSON(w, r, http.StatusBadRequest, errors.New("missing parameter"), "token parameter is required", rest.ErrInternal)
rest.SendErrorJSON(w, r, http.StatusBadRequest, fmt.Errorf("missing parameter"), "token parameter is required", rest.ErrInternal)
return
}
user := rest.MustGetUserInfo(r)
@@ -435,14 +434,14 @@ func (s *private) setConfirmedEmailCtrl(w http.ResponseWriter, r *http.Request)
}
if s.authenticator.TokenService().IsExpired(confClaims) {
rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("expired"), "failed to verify confirmation token", rest.ErrInternal)
rest.SendErrorJSON(w, r, http.StatusForbidden, fmt.Errorf("expired"), "failed to verify confirmation token", rest.ErrInternal)
return
}
// Handshake.ID is user.ID + "::" + address
elems := strings.Split(confClaims.Handshake.ID, "::")
if len(elems) != 2 || elems[0] != user.ID {
rest.SendErrorJSON(w, r, http.StatusBadRequest, errors.New(confClaims.Handshake.ID), "invalid handshake token", rest.ErrInternal)
rest.SendErrorJSON(w, r, http.StatusBadRequest, fmt.Errorf("%s", confClaims.Handshake.ID), "invalid handshake token", rest.ErrInternal)
return
}
address := elems[1]
@@ -475,7 +474,7 @@ func (s *private) emailUnsubscribeCtrl(w http.ResponseWriter, r *http.Request) {
tkn := r.URL.Query().Get("tkn")
if tkn == "" {
rest.SendErrorHTML(w, r, http.StatusBadRequest,
errors.New("missing parameter"), "token parameter is required", rest.ErrInternal, s.templates)
fmt.Errorf("missing parameter"), "token parameter is required", rest.ErrInternal, s.templates)
return
}
siteID := r.URL.Query().Get("site")
@@ -488,7 +487,7 @@ func (s *private) emailUnsubscribeCtrl(w http.ResponseWriter, r *http.Request) {
if s.authenticator.TokenService().IsExpired(confClaims) {
rest.SendErrorHTML(w, r, http.StatusForbidden,
errors.New("expired"), "failed to verify confirmation token", rest.ErrInternal, s.templates)
fmt.Errorf("expired"), "failed to verify confirmation token", rest.ErrInternal, s.templates)
return
}
@@ -496,7 +495,7 @@ func (s *private) emailUnsubscribeCtrl(w http.ResponseWriter, r *http.Request) {
elems := strings.Split(confClaims.Handshake.ID, "::")
if len(elems) != 2 {
rest.SendErrorHTML(w, r, http.StatusBadRequest,
errors.New(confClaims.Handshake.ID), "invalid handshake token", rest.ErrInternal, s.templates)
fmt.Errorf("%s", confClaims.Handshake.ID), "invalid handshake token", rest.ErrInternal, s.templates)
return
}
userID := elems[0]
@@ -510,12 +509,12 @@ func (s *private) emailUnsubscribeCtrl(w http.ResponseWriter, r *http.Request) {
}
if existingAddress == "" {
rest.SendErrorHTML(w, r, http.StatusConflict,
errors.New("user is not subscribed"), "user does not have active email subscription", rest.ErrInternal, s.templates)
fmt.Errorf("user is not subscribed"), "user does not have active email subscription", rest.ErrInternal, s.templates)
return
}
if address != existingAddress {
rest.SendErrorHTML(w, r, http.StatusBadRequest,
errors.New("wrong email unsubscription"), "email address in request does not match known for this user",
fmt.Errorf("wrong email unsubscription"), "email address in request does not match known for this user",
rest.ErrInternal, s.templates)
return
}
@@ -727,11 +726,11 @@ func (s *private) isReadOnly(locator store.Locator) bool {
func randToken() (string, error) {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
return "", errors.Wrap(err, "can't get random")
return "", fmt.Errorf("can't get random: %w", err)
}
s := sha1.New() //nolint:gosec // not used for security
if _, err := s.Write(b); err != nil {
return "", errors.Wrap(err, "can't write randoms to sha1")
return "", fmt.Errorf("can't write randoms to sha1: %w", err)
}
return fmt.Sprintf("%x", s.Sum(nil)), nil
}
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"mime/multipart"
@@ -1287,7 +1286,7 @@ func (m *mockTelegram) GetBotUsername() string {
func (m *mockTelegram) CheckToken(string, string) (telegram, site string, err error) {
if m.notVerified {
return "", "", errors.New("not verified")
return "", "", fmt.Errorf("not verified")
}
return "good_telegram", m.site, nil
}
+4 -4
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"crypto/sha1" // nolint
"encoding/base64"
"fmt"
"io"
"net/http"
"os"
@@ -17,7 +18,6 @@ import (
cache "github.com/go-pkgz/lcw"
log "github.com/go-pkgz/lgr"
R "github.com/go-pkgz/rest"
"github.com/pkg/errors"
"github.com/skip2/go-qrcode"
"github.com/umputun/remark42/backend/app/rest"
@@ -372,12 +372,12 @@ func (s *public) robotsCtrl(w http.ResponseWriter, r *http.Request) {
func (s *public) telegramQrCtrl(w http.ResponseWriter, r *http.Request) {
text := r.URL.Query().Get("url")
if text == "" {
rest.SendErrorJSON(w, r, http.StatusBadRequest, errors.New("missing parameter"), "text parameter is required", rest.ErrInternal)
rest.SendErrorJSON(w, r, http.StatusBadRequest, fmt.Errorf("missing parameter"), "text parameter is required", rest.ErrInternal)
return
}
if !strings.HasPrefix(text, "https://t.me/") {
rest.SendErrorJSON(w, r, http.StatusBadRequest, errors.New("wrong parameter"), "text parameter should start with https://t.me/", rest.ErrInternal)
rest.SendErrorJSON(w, r, http.StatusBadRequest, fmt.Errorf("wrong parameter"), "text parameter should start with https://t.me/", rest.ErrInternal)
return
}
@@ -423,7 +423,7 @@ func (s *public) parseSince(r *http.Request) (time.Time, error) {
if since := r.URL.Query().Get("since"); since != "" {
unixTS, e := strconv.ParseInt(since, 10, 64)
if e != nil {
return time.Time{}, errors.Wrap(e, "can't translate since parameter")
return time.Time{}, fmt.Errorf("can't translate since parameter: %w", e)
}
sinceTS = time.Unix(unixTS/1000, 1000000*(unixTS%1000)) // since param in msec timestamp
}
+8 -9
View File
@@ -4,7 +4,6 @@ import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"math/rand"
@@ -277,13 +276,13 @@ func TestRest_parseError(t *testing.T) {
err error
res int
}{
{errors.New("can not vote for his own comment"), rest.ErrVoteSelf},
{errors.New("already voted for"), rest.ErrVoteDbl},
{errors.New("maximum number of votes exceeded for comment"), rest.ErrVoteMax},
{errors.New("minimal score reached for comment"), rest.ErrVoteMinScore},
{errors.New("too late to edit"), rest.ErrCommentEditExpired},
{errors.New("parent comment with reply can't be edited"), rest.ErrCommentEditChanged},
{errors.New("blah blah"), rest.ErrInternal},
{fmt.Errorf("can not vote for his own comment"), rest.ErrVoteSelf},
{fmt.Errorf("already voted for"), rest.ErrVoteDbl},
{fmt.Errorf("maximum number of votes exceeded for comment"), rest.ErrVoteMax},
{fmt.Errorf("minimal score reached for comment"), rest.ErrVoteMinScore},
{fmt.Errorf("too late to edit"), rest.ErrCommentEditExpired},
{fmt.Errorf("parent comment with reply can't be edited"), rest.ErrCommentEditChanged},
{fmt.Errorf("blah blah"), rest.ErrInternal},
}
for n, tt := range tbl {
@@ -398,7 +397,7 @@ func randomPath(tempDir, basename, suffix string) (string, error) {
return fname, nil
}
}
return "", errors.New("cannot create temp file")
return "", fmt.Errorf("cannot create temp file in %s", tempDir)
}
// startupT runs fully configured testing server
+1 -2
View File
@@ -8,7 +8,6 @@ import (
cache "github.com/go-pkgz/lcw"
log "github.com/go-pkgz/lgr"
"github.com/gorilla/feeds"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/rest"
"github.com/umputun/remark42/backend/app/store"
@@ -104,7 +103,7 @@ func (s *rss) repliesCtrl(w http.ResponseWriter, r *http.Request) {
data, err := s.cache.Get(key, func() (res []byte, e error) {
replies, userName, e := s.dataService.UserReplies(siteID, userID, maxRssItems, maxReplyDuration)
if e != nil {
return nil, errors.Wrap(e, "can't get last comments")
return nil, fmt.Errorf("can't get last comments: %w", e)
}
feed, e := s.toRssFeed(siteID, replies, "replies to "+userName)
+4 -5
View File
@@ -1,7 +1,6 @@
package rest
import (
"errors"
"fmt"
"io"
"net/http"
@@ -18,7 +17,7 @@ func TestSendErrorJSON(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/error" {
t.Log("http err request", r.URL)
SendErrorJSON(w, r, 500, errors.New("error 500"), "error details 123456", 123)
SendErrorJSON(w, r, 500, fmt.Errorf("error 500"), "error details 123456", 123)
return
}
w.WriteHeader(404)
@@ -48,7 +47,7 @@ func TestSendErrorHTML(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/error" {
t.Log("http err request", r.URL)
SendErrorHTML(w, r, 500, errors.New("error 500"), "error details 123456", 987, fs)
SendErrorHTML(w, r, 500, fmt.Errorf("error 500"), "error details 123456", 987, fs)
return
}
w.WriteHeader(404)
@@ -74,7 +73,7 @@ func TestErrorDetailsMsg(t *testing.T) {
req, err := http.NewRequest("GET", "https://example.com/test?k1=v1&k2=v2", http.NoBody)
require.NoError(t, err)
req.RemoteAddr = "1.2.3.4"
msg := errDetailsMsg(req, 500, errors.New("error 500"), "error details 123456", 123)
msg := errDetailsMsg(req, 500, fmt.Errorf("error 500"), "error details 123456", 123)
assert.Contains(t, msg, "error details 123456 - error 500 - 500 (123) - https://example.com/test?k1=v1&k2=v2 - [app/rest/httperrors_test.go:")
// error line in the middle of the message is not checked
assert.Contains(t, msg, " rest.TestErrorDetailsMsg]")
@@ -89,7 +88,7 @@ func TestErrorDetailsMsgWithUser(t *testing.T) {
req.RemoteAddr = "127.0.0.1:1234"
req = SetUserInfo(req, store.User{Name: "test", ID: "id"})
require.NoError(t, err)
msg := errDetailsMsg(req, 500, errors.New("error 500"), "error details 123456", 34567)
msg := errDetailsMsg(req, 500, fmt.Errorf("error 500"), "error details 123456", 34567)
assert.Contains(t, msg, "error details 123456 - error 500 - 500 (34567) - test/id - https://example.com/test?k1=v1&k2=v2 - [app/rest/httperrors_test.go:")
// error line in the middle of the message is not checked
assert.Contains(t, msg, " rest.TestErrorDetailsMsgWithUser]")
+6 -6
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
"net/http"
"strings"
@@ -12,7 +13,6 @@ import (
"github.com/PuerkitoBio/goquery"
log "github.com/go-pkgz/lgr"
"github.com/go-pkgz/repeater"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/rest"
"github.com/umputun/remark42/backend/app/store/image"
@@ -54,7 +54,7 @@ func (p Image) Convert(commentHTML string) string {
func (p Image) extract(commentHTML string, imgSrcPred func(string) bool) ([]string, error) {
doc, err := goquery.NewDocumentFromReader(strings.NewReader(commentHTML))
if err != nil {
return nil, errors.Wrap(err, "can't create document")
return nil, fmt.Errorf("can't create document: %w", err)
}
result := []string{}
doc.Find("img").Each(func(i int, s *goquery.Selection) {
@@ -150,23 +150,23 @@ func (p Image) downloadImage(ctx context.Context, imgURL string) ([]byte, error)
var e error
req, e := http.NewRequest("GET", imgURL, http.NoBody)
if e != nil {
return errors.Wrapf(e, "failed to make request for %s", imgURL)
return fmt.Errorf("failed to make request for %s: %w", imgURL, e)
}
resp, e = client.Do(req.WithContext(ctx)) //nolint:bodyclose // need a refactor to fix that
return e
})
if err != nil {
return nil, errors.Wrapf(err, "can't download image %s", imgURL)
return nil, fmt.Errorf("can't download image %s: %w", imgURL, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, errors.Errorf("got unsuccessful response status %d while fetching %s", resp.StatusCode, imgURL)
return nil, fmt.Errorf("got unsuccessful response status %d while fetching %s", resp.StatusCode, imgURL)
}
imgData, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Errorf("unable to read image body")
return nil, fmt.Errorf("unable to read image body")
}
return imgData, nil
}
+2 -2
View File
@@ -1,10 +1,10 @@
package rest
import (
"fmt"
"net/http"
"github.com/go-pkgz/auth/token"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
)
@@ -23,7 +23,7 @@ func MustGetUserInfo(r *http.Request) store.User {
func GetUserInfo(r *http.Request) (user store.User, err error) {
u, err := token.GetUserInfo(r)
if err != nil {
return store.User{}, errors.Wrap(err, "can't extract user info from the token")
return store.User{}, fmt.Errorf("can't extract user info from the token: %w", err)
}
return store.User{
+2 -2
View File
@@ -2,7 +2,7 @@
package admin
import (
"errors"
"fmt"
"strings"
log "github.com/go-pkgz/lgr"
@@ -50,7 +50,7 @@ func NewStaticKeyStore(key string) *StaticStore {
// Key returns static key, same for all sites
func (s *StaticStore) Key(_ string) (key string, err error) {
if s.key == "" {
return "", errors.New("empty key for static key store")
return "", fmt.Errorf("empty key for static key store")
}
return s.key, nil
}
+79 -63
View File
@@ -9,7 +9,6 @@ import (
log "github.com/go-pkgz/lgr"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
"github.com/umputun/remark42/backend/app/store"
@@ -57,7 +56,7 @@ func NewBoltDB(options bolt.Options, sites ...BoltSite) (*BoltDB, error) {
for _, site := range sites {
db, err := bolt.Open(site.FileName, 0o600, &options) //nolint:gocritic //octalLiteral is OK as FileMode
if err != nil {
return nil, errors.Wrapf(err, "failed to make boltdb for %s", site.FileName)
return nil, fmt.Errorf("failed to make boltdb for %s: %w", site.FileName, err)
}
// make top-level buckets
@@ -66,14 +65,14 @@ func NewBoltDB(options bolt.Options, sites ...BoltSite) (*BoltDB, error) {
err = db.Update(func(tx *bolt.Tx) error {
for _, bktName := range topBuckets {
if _, e := tx.CreateBucketIfNotExists([]byte(bktName)); e != nil {
return errors.Wrapf(e, "failed to create top level bucket %s", bktName)
return fmt.Errorf("failed to create top level bucket %s: %w", bktName, e)
}
}
return nil
})
if err != nil {
return nil, errors.Wrap(err, "failed to create top level bucket)")
return nil, fmt.Errorf("failed to create top level bucket): %w", err)
}
result.dbs[site.SiteID] = db
@@ -90,7 +89,7 @@ func (b *BoltDB) Create(comment store.Comment) (commentID string, err error) {
}
if b.checkFlag(FlagRequest{Locator: comment.Locator, Flag: ReadOnly}) {
return "", errors.Errorf("post %s is read-only", comment.Locator.URL)
return "", fmt.Errorf("post %s is read-only", comment.Locator.URL)
}
err = bdb.Update(func(tx *bolt.Tx) (err error) {
@@ -101,12 +100,12 @@ func (b *BoltDB) Create(comment store.Comment) (commentID string, err error) {
}
// check if key already in store, reject doubles
if postBkt.Get([]byte(comment.ID)) != nil {
return errors.Errorf("key %s already in store", comment.ID)
return fmt.Errorf("key %s already in store", comment.ID)
}
// serialize comment to json []byte for bolt and save
if err = b.save(postBkt, comment.ID, comment); err != nil {
return errors.Wrapf(err, "failed to put key %s to bucket %s", comment.ID, comment.Locator.URL)
return fmt.Errorf("failed to put key %s to bucket %s: %w", comment.ID, comment.Locator.URL, err)
}
ref := b.makeRef(comment) // reference combines url and comment id
@@ -115,21 +114,21 @@ func (b *BoltDB) Create(comment store.Comment) (commentID string, err error) {
lastBkt = tx.Bucket([]byte(lastBucketName))
commentTS := []byte(comment.Timestamp.Format(tsNano))
if err = lastBkt.Put(commentTS, ref); err != nil {
return errors.Wrapf(err, "can't put reference %s to %s", ref, lastBucketName)
return fmt.Errorf("can't put reference %s to %s: %w", ref, lastBucketName, err)
}
// add reference to commentID to "users" bucket
if userBkt, err = b.getUserBucket(tx, comment.User.ID); err != nil {
return errors.Wrapf(err, "can't get bucket %s", comment.User.ID)
return fmt.Errorf("can't get bucket %s: %w", comment.User.ID, err)
}
// put into individual user's bucket with ts as a key
if err = userBkt.Put(commentTS, ref); err != nil {
return errors.Wrapf(err, "failed to put user comment %s for %s", comment.ID, comment.User.ID)
return fmt.Errorf("failed to put user comment %s for %s: %w", comment.ID, comment.User.ID, err)
}
// set info with the count for post url
if _, err = b.setInfo(tx, comment); err != nil {
return errors.Wrapf(err, "failed to set info for %s", comment.Locator)
return fmt.Errorf("failed to set info for %s: %w", comment.Locator, err)
}
return nil
})
@@ -174,7 +173,7 @@ func (b *BoltDB) Find(req FindRequest) (comments []store.Comment, err error) {
return bucket.ForEach(func(k, v []byte) error {
comment := store.Comment{}
if e = json.Unmarshal(v, &comment); e != nil {
return errors.Wrap(e, "failed to unmarshal")
return fmt.Errorf("failed to unmarshal: %w", e)
}
if req.Since.IsZero() || comment.Timestamp.After(req.Since) {
comments = append(comments, comment)
@@ -211,7 +210,7 @@ func (b *BoltDB) UserDetail(req UserDetailRequest) ([]UserDetailEntry, error) {
switch req.Detail {
case UserEmail, UserTelegram:
if req.UserID == "" {
return nil, errors.New("userid cannot be empty in request for single detail")
return nil, fmt.Errorf("userid cannot be empty in request for single detail")
}
if req.Update == "" { // read detail value, no update requested
@@ -225,9 +224,9 @@ func (b *BoltDB) UserDetail(req UserDetailRequest) ([]UserDetailEntry, error) {
if req.Update == "" && req.UserID == "" { // read list of all details
return b.listDetails(req.Locator)
}
return nil, errors.New("unsupported request with userdetail all")
return nil, fmt.Errorf("unsupported request with userdetail all")
default:
return nil, errors.Errorf("unsupported detail %q", req.Detail)
return nil, fmt.Errorf("unsupported detail %q", req.Detail)
}
}
@@ -277,7 +276,7 @@ func (b *BoltDB) Count(req FindRequest) (count int, err error) {
usersBkt := tx.Bucket([]byte(userBucketName))
userIDBkt := usersBkt.Bucket([]byte(req.UserID))
if userIDBkt == nil {
return errors.Errorf("no comments for user %s in store for %s site", req.UserID, req.Locator.SiteID)
return fmt.Errorf("no comments for user %s in store for %s site", req.UserID, req.Locator.SiteID)
}
stats := userIDBkt.Stats()
count = stats.KeyN
@@ -286,7 +285,7 @@ func (b *BoltDB) Count(req FindRequest) (count int, err error) {
return count, err
}
return 0, errors.Errorf("invalid count request %+v", req)
return 0, fmt.Errorf("invalid count request %+v", req)
}
// Info get post(s) meta info
@@ -301,7 +300,7 @@ func (b *BoltDB) Info(req InfoRequest) ([]store.PostInfo, error) {
err = bdb.View(func(tx *bolt.Tx) error {
infoBkt := tx.Bucket([]byte(infoBucketName))
if e := b.load(infoBkt, req.Locator.URL, &info); e != nil {
return errors.Wrapf(e, "can't load info for %s", req.Locator.URL)
return fmt.Errorf("can't load info for %s: %w", req.Locator.URL, e)
}
return nil
})
@@ -331,7 +330,7 @@ func (b *BoltDB) Info(req InfoRequest) ([]store.PostInfo, error) {
infoBkt := tx.Bucket([]byte(infoBucketName))
info := store.PostInfo{}
if e := b.load(infoBkt, postURL, &info); e != nil {
return errors.Wrapf(e, "can't load info for %s", postURL)
return fmt.Errorf("can't load info for %s: %w", postURL, e)
}
list = append(list, info)
if req.Limit > 0 && len(list) >= req.Limit {
@@ -343,7 +342,7 @@ func (b *BoltDB) Info(req InfoRequest) ([]store.PostInfo, error) {
return list, err
}
return nil, errors.Errorf("invalid info request %+v", req)
return nil, fmt.Errorf("invalid info request %+v", req)
}
// ListFlags get list of flagged keys, like blocked & verified user
@@ -372,7 +371,7 @@ func (b *BoltDB) ListFlags(req FlagRequest) (res []interface{}, err error) {
return bucket.ForEach(func(k []byte, v []byte) error {
ts, errParse := time.ParseInLocation(tsNano, string(v), time.Local)
if errParse != nil {
return errors.Wrap(errParse, "can't parse block ts")
return fmt.Errorf("can't parse block ts: %w", errParse)
}
if time.Now().Before(ts) {
// get user name from comment user section
@@ -389,7 +388,7 @@ func (b *BoltDB) ListFlags(req FlagRequest) (res []interface{}, err error) {
})
return res, err
}
return nil, errors.Errorf("flag %s not listable", req.Flag)
return nil, fmt.Errorf("flag %s not listable", req.Flag)
}
// Delete post(s), user, comment, user details, or everything
@@ -410,15 +409,17 @@ func (b *BoltDB) Delete(req DeleteRequest) error {
return b.deleteAll(bdb, req.Locator.SiteID)
}
return errors.Errorf("invalid delete request %+v", req)
return fmt.Errorf("invalid delete request %+v", req)
}
// Close boltdb store
func (b *BoltDB) Close() error {
errs := new(multierror.Error)
for site, db := range b.dbs {
err := errors.Wrapf(db.Close(), "can't close site %s", site)
errs = multierror.Append(errs, err)
err := db.Close()
if err != nil {
errs = multierror.Append(errs, fmt.Errorf("can't close site %s: %w", site, err))
}
}
return errs.ErrorOrNil()
}
@@ -496,7 +497,7 @@ func (b *BoltDB) userComments(siteID, userID string, limit, skip int) (comments
usersBkt := tx.Bucket([]byte(userBucketName))
userIDBkt := usersBkt.Bucket([]byte(userID))
if userIDBkt == nil {
return errors.Errorf("no comments for user %s in store", userID)
return fmt.Errorf("no comments for user %s in store", userID)
}
c := userIDBkt.Cursor()
@@ -522,7 +523,7 @@ func (b *BoltDB) userComments(siteID, userID string, limit, skip int) (comments
for _, v := range commentRefs {
url, commentID, errParse := b.parseRef([]byte(v))
if errParse != nil {
return comments, errors.Wrapf(errParse, "can't parse reference %s", v)
return comments, fmt.Errorf("can't parse reference %s: %w", v, errParse)
}
getReq := GetRequest{Locator: store.Locator{SiteID: siteID, URL: url}, CommentID: commentID}
if c, errRef := b.Get(getReq); errRef == nil {
@@ -600,20 +601,20 @@ func (b *BoltDB) setFlag(req FlagRequest) (res bool, err error) {
val = time.Now().Add(req.TTL).Format(tsNano)
}
if e = bucket.Put([]byte(key), []byte(val)); e != nil {
return errors.Wrapf(e, "failed to put blocked to %s", key)
return fmt.Errorf("failed to put blocked to %s: %w", key, e)
}
res = true
return nil
}
if e = bucket.Put([]byte(key), []byte(time.Now().Format(tsNano))); e != nil {
return errors.Wrapf(e, "failed to set flag %s for %s", req.Flag, req.Locator.URL)
return fmt.Errorf("failed to set flag %s for %s: %w", req.Flag, req.Locator.URL, e)
}
res = true
return nil
case FlagFalse:
if e = bucket.Delete([]byte(key)); e != nil {
return errors.Wrapf(e, "failed to clean flag %s for %s", req.Flag, req.Locator.URL)
return fmt.Errorf("failed to clean flag %s for %s: %w", req.Flag, req.Locator.URL, e)
}
res = false
}
@@ -632,7 +633,7 @@ func (b *BoltDB) flagBucket(tx *bolt.Tx, flag Flag) (bkt *bolt.Bucket, err error
case Verified:
bkt = tx.Bucket([]byte(verifiedBucketName))
default:
return nil, errors.Errorf("unsupported flag %v", flag)
return nil, fmt.Errorf("unsupported flag %v", flag)
}
return bkt, nil
}
@@ -652,7 +653,7 @@ func (b *BoltDB) getUserDetail(req UserDetailRequest) (result []UserDetailEntry,
// return no error in case of absent entry
if value != nil {
if err = json.Unmarshal(value, &entry); err != nil {
return errors.Wrap(e, "failed to unmarshal entry")
return fmt.Errorf("failed to unmarshal entry: %w", e)
}
switch req.Detail {
case UserEmail:
@@ -682,7 +683,7 @@ func (b *BoltDB) setUserDetail(req UserDetailRequest) (result []UserDetailEntry,
// return no error in case of absent entry
if value != nil {
if err = json.Unmarshal(value, &entry); err != nil {
return errors.Wrap(e, "failed to unmarshal entry")
return fmt.Errorf("failed to unmarshal entry: %w", e)
}
}
return nil
@@ -705,7 +706,10 @@ func (b *BoltDB) setUserDetail(req UserDetailRequest) (result []UserDetailEntry,
err = bdb.Update(func(tx *bolt.Tx) error {
err = b.save(tx.Bucket([]byte(userDetailsBucketName)), req.UserID, entry)
return errors.Wrapf(err, "failed to update detail %s for %s in %s", req.Detail, req.UserID, req.Locator.SiteID)
if err != nil {
return fmt.Errorf("failed to update detail %s for %s in %s: %w", req.Detail, req.UserID, req.Locator.SiteID, err)
}
return nil
})
return []UserDetailEntry{entry}, err
@@ -723,7 +727,7 @@ func (b *BoltDB) listDetails(loc store.Locator) (result []UserDetailEntry, err e
bucket := tx.Bucket([]byte(userDetailsBucketName))
return bucket.ForEach(func(userID, value []byte) error {
if err = json.Unmarshal(value, &entry); err != nil {
return errors.Wrap(e, "failed to unmarshal entry")
return fmt.Errorf("failed to unmarshal entry: %w", e)
}
result = append(result, entry)
return nil
@@ -741,7 +745,7 @@ func (b *BoltDB) deleteUserDetail(bdb *bolt.DB, userID string, userDetail UserDe
// return no error in case of absent entry
if value != nil {
if err := json.Unmarshal(value, &entry); err != nil {
return errors.Wrap(err, "failed to unmarshal entry")
return fmt.Errorf("failed to unmarshal entry: %w", err)
}
}
return nil
@@ -768,14 +772,20 @@ func (b *BoltDB) deleteUserDetail(bdb *bolt.DB, userID string, userDetail UserDe
// if entry doesn't have non-empty details, we should delete it
return bdb.Update(func(tx *bolt.Tx) error {
err := tx.Bucket([]byte(userDetailsBucketName)).Delete([]byte(userID))
return errors.Wrapf(err, "failed to delete user detail %s for %s", userDetail, userID)
if err != nil {
return fmt.Errorf("failed to delete user detail %s for %s: %w", userDetail, userID, err)
}
return nil
})
}
return bdb.Update(func(tx *bolt.Tx) error {
// updated entry is not empty and we need to store it's updated copy
err := b.save(tx.Bucket([]byte(userDetailsBucketName)), userID, entry)
return errors.Wrapf(err, "failed to update detail %s for %s", userDetail, userID)
if err != nil {
return fmt.Errorf("failed to update detail %s for %s: %w", userDetail, userID, err)
}
return nil
})
}
@@ -788,13 +798,13 @@ func (b *BoltDB) deleteComment(bdb *bolt.DB, locator store.Locator, commentID st
comment := store.Comment{}
if e = b.load(postBkt, commentID, &comment); e != nil {
return errors.Wrapf(e, "can't load key %s from bucket %s", commentID, locator.URL)
return fmt.Errorf("can't load key %s from bucket %s: %w", commentID, locator.URL, e)
}
if !comment.Deleted {
// decrement comments count for post url
if _, e = b.count(tx, comment.Locator.URL, -1); e != nil {
return errors.Wrapf(e, "failed to decrement count for %s", comment.Locator)
return fmt.Errorf("failed to decrement count for %s: %w", comment.Locator, e)
}
}
@@ -802,13 +812,13 @@ func (b *BoltDB) deleteComment(bdb *bolt.DB, locator store.Locator, commentID st
comment.SetDeleted(mode)
if e = b.save(postBkt, commentID, comment); e != nil {
return errors.Wrapf(e, "can't save deleted comment for key %s from bucket %s", commentID, locator.URL)
return fmt.Errorf("can't save deleted comment for key %s from bucket %s: %w", commentID, locator.URL, e)
}
// delete from "last" bucket
lastBkt := tx.Bucket([]byte(lastBucketName))
if e = lastBkt.Delete([]byte(commentID)); e != nil {
return errors.Wrapf(e, "can't delete key %s from bucket %s", commentID, lastBucketName)
return fmt.Errorf("can't delete key %s from bucket %s: %w", commentID, lastBucketName, e)
}
return nil
@@ -824,16 +834,19 @@ func (b *BoltDB) deleteAll(bdb *bolt.DB, siteID string) error {
err := bdb.Update(func(tx *bolt.Tx) error {
for _, bktName := range toDelete {
if e := tx.DeleteBucket([]byte(bktName)); e != nil {
return errors.Wrapf(e, "failed to delete top level bucket %s", bktName)
return fmt.Errorf("failed to delete top level bucket %s: %w", bktName, e)
}
if _, e := tx.CreateBucketIfNotExists([]byte(bktName)); e != nil {
return errors.Wrapf(e, "failed to create top level bucket %s", bktName)
return fmt.Errorf("failed to create top level bucket %s: %w", bktName, e)
}
}
return nil
})
return errors.Wrapf(err, "failed to delete top level buckets from site %s", siteID)
if err != nil {
return fmt.Errorf("failed to delete top level buckets from site %s: %w", siteID, err)
}
return nil
}
// deleteUser removes all comments and details for given user. Everything will be market as deleted
@@ -860,14 +873,17 @@ func (b *BoltDB) deleteUser(bdb *bolt.DB, siteID, userID string, mode store.Dele
err = postBkt.ForEach(func(postURL []byte, commentVal []byte) error {
comment := store.Comment{}
if err = json.Unmarshal(commentVal, &comment); err != nil {
return errors.Wrap(err, "failed to unmarshal")
return fmt.Errorf("failed to unmarshal: %w", err)
}
if comment.User.ID == userID {
comments = append(comments, commentInfo{locator: comment.Locator, commentID: comment.ID})
}
return nil
})
return errors.Wrapf(err, "failed to collect list of comments for deletion from %s", postInfo.URL)
if err != nil {
return fmt.Errorf("failed to collect list of comments for deletion from %s: %w", postInfo.URL, err)
}
return nil
})
if err != nil {
return err
@@ -879,7 +895,7 @@ func (b *BoltDB) deleteUser(bdb *bolt.DB, siteID, userID string, mode store.Dele
// delete collected comments
for _, ci := range comments {
if e := b.deleteComment(bdb, ci.locator, ci.commentID, mode); e != nil {
return errors.Wrapf(err, "failed to delete comment %+v", ci)
return fmt.Errorf("failed to delete comment %+v: %w", ci, err)
}
}
@@ -889,19 +905,19 @@ func (b *BoltDB) deleteUser(bdb *bolt.DB, siteID, userID string, mode store.Dele
usersBkt := tx.Bucket([]byte(userBucketName))
if usersBkt != nil {
if e := usersBkt.DeleteBucket([]byte(userID)); e != nil {
return errors.Wrapf(err, "failed to delete user bucket for %s", userID)
return fmt.Errorf("failed to delete user bucket for %s: %w", userID, err)
}
}
return nil
})
if err != nil {
return errors.Wrap(err, "can't delete user meta")
return fmt.Errorf("can't delete user meta: %w", err)
}
}
if len(comments) == 0 {
return errors.Errorf("unknown user %s", userID)
return fmt.Errorf("unknown user %s", userID)
}
return b.deleteUserDetail(bdb, userID, AllUserDetails)
@@ -911,11 +927,11 @@ func (b *BoltDB) deleteUser(bdb *bolt.DB, siteID, userID string, mode store.Dele
func (b *BoltDB) getPostBucket(tx *bolt.Tx, postURL string) (*bolt.Bucket, error) {
postsBkt := tx.Bucket([]byte(postsBucketName))
if postsBkt == nil {
return nil, errors.Errorf("no bucket %s", postsBucketName)
return nil, fmt.Errorf("no bucket %s", postsBucketName)
}
res := postsBkt.Bucket([]byte(postURL))
if res == nil {
return nil, errors.Errorf("no bucket %s in store", postURL)
return nil, fmt.Errorf("no bucket %s in store", postURL)
}
return res, nil
}
@@ -924,11 +940,11 @@ func (b *BoltDB) getPostBucket(tx *bolt.Tx, postURL string) (*bolt.Bucket, error
func (b *BoltDB) makePostBucket(tx *bolt.Tx, postURL string) (*bolt.Bucket, error) {
postsBkt := tx.Bucket([]byte(postsBucketName))
if postsBkt == nil {
return nil, errors.Errorf("no bucket %s", postsBucketName)
return nil, fmt.Errorf("no bucket %s", postsBucketName)
}
res, err := postsBkt.CreateBucketIfNotExists([]byte(postURL))
if err != nil {
return nil, errors.Wrapf(err, "no bucket %s in store", postURL)
return nil, fmt.Errorf("no bucket %s in store: %w", postURL, err)
}
return res, nil
}
@@ -937,7 +953,7 @@ func (b *BoltDB) getUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, error)
usersBkt := tx.Bucket([]byte(userBucketName))
userIDBkt, e := usersBkt.CreateBucketIfNotExists([]byte(userID)) // get bucket for userID
if e != nil {
return nil, errors.Wrapf(e, "can't get bucket %s", userID)
return nil, fmt.Errorf("can't get bucket %s: %w", userID, e)
}
return userIDBkt, nil
}
@@ -945,14 +961,14 @@ func (b *BoltDB) getUserBucket(tx *bolt.Tx, userID string) (*bolt.Bucket, error)
// save marshaled value to key for bucket. Should run in update tx
func (b *BoltDB) save(bkt *bolt.Bucket, key string, value interface{}) (err error) {
if value == nil {
return errors.Errorf("can't save nil value for %s", key)
return fmt.Errorf("can't save nil value for %s", key)
}
jdata, jerr := json.Marshal(value)
if jerr != nil {
return errors.Wrap(jerr, "can't marshal comment")
return fmt.Errorf("can't marshal comment: %w", jerr)
}
if err = bkt.Put([]byte(key), jdata); err != nil {
return errors.Wrapf(err, "failed to save key %s", key)
return fmt.Errorf("failed to save key %s: %w", key, err)
}
return nil
}
@@ -961,11 +977,11 @@ func (b *BoltDB) save(bkt *bolt.Bucket, key string, value interface{}) (err erro
func (b *BoltDB) load(bkt *bolt.Bucket, key string, res interface{}) error {
value := bkt.Get([]byte(key))
if value == nil {
return errors.Errorf("no value for %s", key)
return fmt.Errorf("no value for %s", key)
}
if err := json.Unmarshal(value, &res); err != nil {
return errors.Wrap(err, "failed to unmarshal")
return fmt.Errorf("failed to unmarshal: %w", err)
}
return nil
}
@@ -1008,7 +1024,7 @@ func (b *BoltDB) db(siteID string) (*bolt.DB, error) {
if res, ok := b.dbs[siteID]; ok {
return res, nil
}
return nil, errors.Errorf("site %q not found", siteID)
return nil, fmt.Errorf("site %q not found", siteID)
}
// makeRef creates reference combining url and comment id
@@ -1020,7 +1036,7 @@ func (b *BoltDB) makeRef(comment store.Comment) []byte {
func (b *BoltDB) parseRef(val []byte) (url, id string, err error) {
elems := strings.Split(string(val), "!!")
if len(elems) != 2 {
return "", "", errors.Errorf("invalid reference value %s", string(val))
return "", "", fmt.Errorf("invalid reference value %s", string(val))
}
return elems[0], elems[1], nil
}
+25 -19
View File
@@ -4,10 +4,10 @@ import (
"bytes"
"context"
"encoding/binary"
"fmt"
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
)
@@ -27,23 +27,23 @@ type Bolt struct {
func NewBoltStorage(fileName string, options bolt.Options) (*Bolt, error) {
db, err := bolt.Open(fileName, 0o600, &options) //nolint:gocritic //octalLiteral is OK as FileMode
if err != nil {
return nil, errors.Wrapf(err, "failed to make boltdb for %s", fileName)
return nil, fmt.Errorf("failed to make boltdb for %s: %w", fileName, err)
}
err = db.Update(func(tx *bolt.Tx) error {
if _, e := tx.CreateBucketIfNotExists([]byte(imagesBktName)); e != nil {
return errors.Wrapf(e, "failed to create top level bucket %s", imagesBktName)
return fmt.Errorf("failed to create top level bucket %s: %w", imagesBktName, e)
}
if _, e := tx.CreateBucketIfNotExists([]byte(imagesStagedBktName)); e != nil {
return errors.Wrapf(e, "failed to create top level bucket %s", imagesStagedBktName)
return fmt.Errorf("failed to create top level bucket %s: %w", imagesStagedBktName, e)
}
if _, e := tx.CreateBucketIfNotExists([]byte(insertTimeBktName)); e != nil {
return errors.Wrapf(e, "failed to create top level bucket %s", insertTimeBktName)
return fmt.Errorf("failed to create top level bucket %s: %w", insertTimeBktName, e)
}
return nil
})
if err != nil {
return nil, errors.Wrapf(err, "failed to initialize boltdb db %q buckets", fileName)
return nil, fmt.Errorf("failed to initialize boltdb db %q buckets: %w", fileName, err)
}
return &Bolt{
db: db,
@@ -55,14 +55,14 @@ func NewBoltStorage(fileName string, options bolt.Options) (*Bolt, error) {
func (b *Bolt) Save(id string, img []byte) error {
return b.db.Update(func(tx *bolt.Tx) error {
if err := tx.Bucket([]byte(imagesStagedBktName)).Put([]byte(id), img); err != nil {
return errors.Wrapf(err, "can't put to bucket with %s", id)
return fmt.Errorf("can't put to bucket with %s: %w", id, err)
}
tsBuf := &bytes.Buffer{}
if err := binary.Write(tsBuf, binary.LittleEndian, time.Now().UnixNano()); err != nil {
return errors.Wrapf(err, "can't serialize timestamp for %s", id)
return fmt.Errorf("can't serialize timestamp for %s: %w", id, err)
}
if err := tx.Bucket([]byte(insertTimeBktName)).Put([]byte(id), tsBuf.Bytes()); err != nil {
return errors.Wrapf(err, "can't put to bucket with %s", id)
return fmt.Errorf("can't put to bucket with %s: %w", id, err)
}
return nil
})
@@ -74,10 +74,13 @@ func (b *Bolt) Commit(id string) error {
return b.db.Update(func(tx *bolt.Tx) error {
data := tx.Bucket([]byte(imagesStagedBktName)).Get([]byte(id))
if data == nil {
return errors.Errorf("failed to commit %s, not found in staging", id)
return fmt.Errorf("failed to commit %s, not found in staging", id)
}
err := tx.Bucket([]byte(imagesBktName)).Put([]byte(id), data)
return errors.Wrapf(err, "can't put to bucket with %s", id)
if err != nil {
return fmt.Errorf("can't put to bucket with %s: %w", id, err)
}
return nil
})
}
@@ -86,10 +89,10 @@ func (b *Bolt) ResetCleanupTimer(id string) error {
return b.db.Update(func(tx *bolt.Tx) error {
tsBuf := &bytes.Buffer{}
if err := binary.Write(tsBuf, binary.LittleEndian, time.Now().UnixNano()); err != nil {
return errors.Wrapf(err, "can't serialize timestamp for %s", id)
return fmt.Errorf("can't serialize timestamp for %s: %w", id, err)
}
if err := tx.Bucket([]byte(insertTimeBktName)).Put([]byte(id), tsBuf.Bytes()); err != nil {
return errors.Wrapf(err, "can't put to bucket with %s", id)
return fmt.Errorf("can't put to bucket with %s: %w", id, err)
}
return nil
})
@@ -104,7 +107,7 @@ func (b *Bolt) Load(id string) ([]byte, error) {
data = tx.Bucket([]byte(imagesStagedBktName)).Get([]byte(id))
}
if data == nil {
return errors.Errorf("can't load image %s", id)
return fmt.Errorf("can't load image %s", id)
}
return nil
})
@@ -126,7 +129,7 @@ func (b *Bolt) Cleanup(_ context.Context, ttl time.Duration) error {
var ts int64
err := binary.Read(bytes.NewReader(tsData), binary.LittleEndian, &ts)
if err != nil {
return errors.Wrapf(err, "failed to deserialize timestamp for %s", id)
return fmt.Errorf("failed to deserialize timestamp for %s: %w", id, err)
}
age := time.Since(time.Unix(0, ts))
@@ -136,7 +139,7 @@ func (b *Bolt) Cleanup(_ context.Context, ttl time.Duration) error {
idsToRemove = append(idsToRemove, id)
err := c.Delete()
if err != nil {
return errors.Wrapf(err, "failed to remove timestamp for %s", id)
return fmt.Errorf("failed to remove timestamp for %s: %w", id, err)
}
}
}
@@ -144,7 +147,7 @@ func (b *Bolt) Cleanup(_ context.Context, ttl time.Duration) error {
for _, id := range idsToRemove {
err := imgBkt.Delete(id)
if err != nil {
return errors.Wrapf(err, "failed to remove image for %s", id)
return fmt.Errorf("failed to remove image for %s: %w", id, err)
}
}
return nil
@@ -161,7 +164,7 @@ func (b *Bolt) Info() (StoreInfo, error) {
var createdRaw int64
err := binary.Read(bytes.NewReader(tsData), binary.LittleEndian, &createdRaw)
if err != nil {
return errors.Wrapf(err, "failed to deserialize timestamp for %s", id)
return fmt.Errorf("failed to deserialize timestamp for %s: %w", id, err)
}
created := time.Unix(0, createdRaw)
@@ -171,5 +174,8 @@ func (b *Bolt) Info() (StoreInfo, error) {
}
return nil
})
return StoreInfo{FirstStagingImageTS: ts}, errors.Wrapf(err, "problem retrieving first timestamp from staging images")
if err != nil {
return StoreInfo{}, fmt.Errorf("problem retrieving first timestamp from staging images: %w", err)
}
return StoreInfo{FirstStagingImageTS: ts}, nil
}
+23 -14
View File
@@ -15,7 +15,6 @@ import (
"time"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
)
// FileSystem provides image Store for local files. Saves and loads files from Location, restricts max size.
@@ -38,11 +37,11 @@ func (f *FileSystem) Save(id string, img []byte) error {
dst := f.location(f.Staging, id)
if err := os.MkdirAll(path.Dir(dst), 0o700); err != nil {
return errors.Wrap(err, "can't make image directory")
return fmt.Errorf("can't make image directory: %w", err)
}
if err := os.WriteFile(dst, img, 0o600); err != nil {
return errors.Wrapf(err, "can't write image file with id %s", id)
return fmt.Errorf("can't write image file with id %s: %w", id, err)
}
log.Printf("[DEBUG] file %s saved for image %s, size=%d", dst, id, len(img))
@@ -55,11 +54,13 @@ func (f *FileSystem) Commit(id string) error {
stagingImage, permImage := f.location(f.Staging, id), f.location(f.Location, id)
if err := os.MkdirAll(path.Dir(permImage), 0o700); err != nil {
return errors.Wrap(err, "can't make image directory")
return fmt.Errorf("can't make image directory: %w", err)
}
err := os.Rename(stagingImage, permImage)
return errors.Wrapf(err, "failed to commit image %s", id)
if err := os.Rename(stagingImage, permImage); err != nil {
return fmt.Errorf("failed to commit image %s: %w", id, err)
}
return nil
}
// ResetCleanupTimer resets cleanup timer for the image
@@ -67,13 +68,15 @@ func (f *FileSystem) ResetCleanupTimer(id string) error {
file := f.location(f.Staging, id)
_, err := os.Stat(file)
if err != nil {
return errors.Wrapf(err, "can't get image stats for %s", id)
return fmt.Errorf("can't get image stats for %s: %w", id, err)
}
// we don't need to update access time (second arg),
// but reading it is platform-dependent and looks different on darwin and linux,
// so it's easier to update it as well
err = os.Chtimes(file, time.Now(), time.Now())
return errors.Wrapf(err, "problem updating %s modification time", file)
if err = os.Chtimes(file, time.Now(), time.Now()); err != nil {
return fmt.Errorf("problem updating %s modification time: %w", file, err)
}
return nil
}
// Load image from FS. Uses id to get partition subdirectory.
@@ -86,17 +89,20 @@ func (f *FileSystem) Load(id string) ([]byte, error) {
file = f.location(f.Staging, id)
_, err = os.Stat(file)
}
return file, errors.Wrapf(err, "can't get image stats for %s", id)
if err != nil {
return file, fmt.Errorf("can't get image stats for %s: %w", id, err)
}
return file, nil
}
imgFile, err := img(id)
if err != nil {
return nil, errors.Wrapf(err, "can't get image file for %s", id)
return nil, fmt.Errorf("can't get image file for %s: %w", id, err)
}
fh, err := os.Open(imgFile) //nolint:gosec // we open file from known location
if err != nil {
return nil, errors.Wrapf(err, "can't load image %s", id)
return nil, fmt.Errorf("can't load image %s: %w", id, err)
}
return io.ReadAll(fh)
}
@@ -124,7 +130,10 @@ func (f *FileSystem) Cleanup(_ context.Context, ttl time.Duration) error {
}
return nil
})
return errors.Wrap(err, "failed to cleanup images")
if err != nil {
return fmt.Errorf("failed to cleanup images: %w", err)
}
return nil
}
// Info returns meta information about storage
@@ -149,7 +158,7 @@ func (f *FileSystem) Info() (StoreInfo, error) {
return nil
})
if err != nil {
return StoreInfo{}, errors.Wrapf(err, "problem retrieving first timestamp from staging images on fs")
return StoreInfo{}, fmt.Errorf("problem retrieving first timestamp from staging images on fs: %w", err)
}
return StoreInfo{FirstStagingImageTS: ts}, nil
}
+5 -6
View File
@@ -28,7 +28,6 @@ import (
"github.com/PuerkitoBio/goquery"
log "github.com/go-pkgz/lgr"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/rs/xid"
"golang.org/x/image/draw"
)
@@ -97,7 +96,7 @@ func (s *Service) SubmitAndCommit(idsFn func() []string) error {
for _, id := range idsFn() {
err := s.store.Commit(id)
if err != nil {
errs = multierror.Append(errs, errors.Wrapf(err, "failed to commit image %s", id))
errs = multierror.Append(errs, fmt.Errorf("failed to commit image %s: %w", id, err))
}
}
return errs.ErrorOrNil()
@@ -269,7 +268,7 @@ func (s *Service) ImgContentType(img []byte) string {
func (s *Service) prepareImage(r io.Reader) ([]byte, error) {
data, err := readAndValidateImage(r, s.MaxSize)
if err != nil {
return nil, errors.Wrapf(err, "can't load image")
return nil, fmt.Errorf("can't load image: %w", err)
}
data = resize(data, s.MaxWidth, s.MaxHeight)
@@ -343,12 +342,12 @@ func readAndValidateImage(r io.Reader, maxSize int) ([]byte, error) {
}
if len(data) > maxSize {
return nil, errors.Errorf("file is too large (limit=%d)", maxSize)
return nil, fmt.Errorf("file is too large (limit=%d)", maxSize)
}
// read header first, needs it to check if data is valid png/gif/jpeg
if !isValidImage(data[:512]) {
return nil, errors.Errorf("file format not allowed")
return nil, fmt.Errorf("file format not allowed")
}
return data, nil
@@ -372,7 +371,7 @@ func Sha1Str(s string) string {
func CachedImgID(imgURL string) (string, error) {
parsedURL, err := url.Parse(imgURL)
if err != nil {
return "", errors.Wrapf(err, "can parse url %s", imgURL)
return "", fmt.Errorf("can parse url %s: %w", imgURL, err)
}
return fmt.Sprintf("cached_images/%s-%s", Sha1Str(parsedURL.Hostname()), Sha1Str(imgURL)), nil
}
+30 -28
View File
@@ -3,6 +3,7 @@
package service
import (
"fmt"
"math"
"sort"
"strings"
@@ -13,7 +14,6 @@ import (
log "github.com/go-pkgz/lgr"
"github.com/google/uuid"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
"github.com/umputun/remark42/backend/app/store"
"github.com/umputun/remark42/backend/app/store/admin"
@@ -77,12 +77,12 @@ const UnlimitedVotes = -1
var nonAdminUser = store.User{}
// ErrRestrictedWordsFound returned in case comment text contains restricted words
var ErrRestrictedWordsFound = errors.New("comment contains restricted words")
var ErrRestrictedWordsFound = fmt.Errorf("comment contains restricted words")
// Create prepares comment and forward to Interface.Create
func (s *DataStore) Create(comment store.Comment) (commentID string, err error) {
if comment, err = s.prepareNewComment(comment); err != nil {
return "", errors.Wrap(err, "failed to prepare comment")
return "", fmt.Errorf("failed to prepare comment: %w", err)
}
if s.RestrictedWordsMatcher != nil && s.RestrictedWordsMatcher.Match(comment.Locator.SiteID, comment.Text) {
@@ -250,7 +250,9 @@ func (s *DataStore) ResubmitStagingImages(sites []string) error {
for _, site := range sites {
locator := store.Locator{SiteID: site}
comments, err := s.FindSince(locator, "time", store.User{}, ts)
result = multierror.Append(result, errors.Wrapf(err, "problem finding comments for site %s", site))
if err != nil {
result = multierror.Append(result, fmt.Errorf("problem finding comments for site %s: %w", site, err))
}
for _, c := range comments {
s.submitImages(c)
}
@@ -303,7 +305,7 @@ func (s *DataStore) prepareNewComment(comment store.Comment) (store.Comment, err
secret, err := s.getSecret(comment.Locator.SiteID)
if err != nil {
return store.Comment{}, errors.Wrapf(err, "can't get secret for site %s", comment.Locator.SiteID)
return store.Comment{}, fmt.Errorf("can't get secret for site %s: %w", comment.Locator.SiteID, err)
}
comment.User.HashIP(secret) // replace ip by hash
return comment, nil
@@ -347,7 +349,7 @@ func (s *DataStore) Vote(req VoteReq) (comment store.Comment, err error) {
}
if comment.User.ID == req.UserID && req.UserID != "dev" {
return comment, errors.Errorf("user %s can not vote for his own comment %s", req.UserID, req.CommentID)
return comment, fmt.Errorf("user %s can not vote for his own comment %s", req.UserID, req.CommentID)
}
if comment.Votes == nil {
@@ -356,16 +358,16 @@ func (s *DataStore) Vote(req VoteReq) (comment store.Comment, err error) {
v, voted := comment.Votes[req.UserID]
if voted && v == req.Val { // voted before and same vote (+/-) again. Change allowed, i.e. +, - or -, + is fine
return comment, errors.Errorf("user %s already voted for %s", req.UserID, req.CommentID)
return comment, fmt.Errorf("user %s already voted for %s", req.UserID, req.CommentID)
}
secret, err := s.getSecret(comment.Locator.SiteID)
if err != nil {
return store.Comment{}, errors.Wrapf(err, "can't get secret for site %s", comment.Locator.SiteID)
return store.Comment{}, fmt.Errorf("can't get secret for site %s: %w", comment.Locator.SiteID, err)
}
userIPHash := store.HashValue(req.UserIP, secret)
if s.isSameIPVote(req, userIPHash, comment) {
return comment, errors.Errorf("the same ip %s already voted for %s", userIPHash, req.CommentID)
return comment, fmt.Errorf("the same ip %s already voted for %s", userIPHash, req.CommentID)
}
maxVotes := s.MaxVotes // 0 value allowed and treated as "no comments allowed"
@@ -374,11 +376,11 @@ func (s *DataStore) Vote(req VoteReq) (comment store.Comment, err error) {
}
if maxVotes >= 0 && len(comment.Votes) >= maxVotes {
return comment, errors.Errorf("maximum number of votes exceeded for comment %s", req.CommentID)
return comment, fmt.Errorf("maximum number of votes exceeded for comment %s", req.CommentID)
}
if s.PositiveScore && comment.Score <= 0 && !req.Val {
return comment, errors.Errorf("minimal score reached for comment %s", req.CommentID)
return comment, fmt.Errorf("minimal score reached for comment %s", req.CommentID)
}
// add ip hash to voted ip map
@@ -472,12 +474,12 @@ func (s *DataStore) EditComment(locator store.Locator, commentID string, req Edi
// edit allowed in editDuration window only
if s.EditDuration > 0 && time.Now().After(comment.Timestamp.Add(s.EditDuration)) {
return errors.Errorf("too late to edit %s", commentID)
return fmt.Errorf("too late to edit %s", commentID)
}
// edit rejected on replayed threads
if s.HasReplies(comment) {
return errors.Errorf("parent comment with reply can't be edited, %s", commentID)
return fmt.Errorf("parent comment with reply can't be edited, %s", commentID)
}
return nil
}
@@ -555,7 +557,7 @@ func (s *DataStore) HasReplies(comment store.Comment) bool {
func (s *DataStore) UserReplies(siteID, userID string, limit int, duration time.Duration) ([]store.Comment, string, error) {
comments, e := s.Last(siteID, maxLastCommentsReply, time.Time{}, nonAdminUser)
if e != nil {
return nil, "", errors.Wrap(e, "can't get last comments")
return nil, "", fmt.Errorf("can't get last comments: %w", e)
}
replies := []store.Comment{}
@@ -574,7 +576,7 @@ func (s *DataStore) UserReplies(siteID, userID string, limit int, duration time.
if c.ParentID != "" && !c.Deleted && c.User.ID != userID { // not interested in replies to yourself
var pc store.Comment
if pc, e = s.Get(c.Locator, c.ParentID, nonAdminUser); e != nil {
return nil, "", errors.Wrap(e, "can't get parent comment")
return nil, "", fmt.Errorf("can't get parent comment: %w", e)
}
if pc.User.ID == userID {
replies = append(replies, c)
@@ -588,7 +590,7 @@ func (s *DataStore) UserReplies(siteID, userID string, limit int, duration time.
// SetTitle puts title from the locator.URL page and overwrites any existing title
func (s *DataStore) SetTitle(locator store.Locator, commentID string) (comment store.Comment, err error) {
if s.TitleExtractor == nil {
return comment, errors.New("no title extractor")
return comment, fmt.Errorf("no title extractor")
}
comment, err = s.Engine.Get(engine.GetRequest{Locator: locator, CommentID: commentID})
@@ -626,13 +628,13 @@ func (s *DataStore) ValidateComment(c *store.Comment) error {
maxSize = defaultCommentMaxSize
}
if c.Orig == "" {
return errors.New("empty comment text")
return fmt.Errorf("empty comment text")
}
if len([]rune(c.Orig)) > maxSize {
return errors.Errorf("comment text exceeded max allowed size %d (%d)", maxSize, len([]rune(c.Orig)))
return fmt.Errorf("comment text exceeded max allowed size %d (%d)", maxSize, len([]rune(c.Orig)))
}
if c.User.ID == "" || c.User.Name == "" {
return errors.Errorf("empty user info")
return fmt.Errorf("empty user info")
}
return nil
}
@@ -711,7 +713,7 @@ func (s *DataStore) SetBlock(siteID, userID string, status bool, ttl time.Durati
func (s *DataStore) BlockedUsers(siteID string) (res []store.BlockedUser, err error) {
blocked, e := s.Engine.ListFlags(engine.FlagRequest{Locator: store.Locator{SiteID: siteID}, Flag: engine.Blocked})
if e != nil {
return nil, errors.Wrapf(err, "can't get list of blocked users for %s", siteID)
return nil, fmt.Errorf("can't get list of blocked users for %s: %w", siteID, err)
}
for _, v := range blocked {
res = append(res, v.(store.BlockedUser))
@@ -727,7 +729,7 @@ func (s *DataStore) Info(locator store.Locator, readonlyAge int) (store.PostInfo
return store.PostInfo{}, err
}
if len(res) == 0 {
return store.PostInfo{}, errors.Errorf("post %+v not found", locator)
return store.PostInfo{}, fmt.Errorf("post %+v not found", locator)
}
return res[0], nil
}
@@ -767,7 +769,7 @@ func (s *DataStore) Metas(siteID string) (umetas []UserMetaData, pmetas []PostMe
// set posts meta
posts, err := s.Engine.Info(engine.InfoRequest{Locator: store.Locator{SiteID: siteID}})
if err != nil {
return nil, nil, errors.Wrapf(err, "can't get list of posts for %s", siteID)
return nil, nil, fmt.Errorf("can't get list of posts for %s: %w", siteID, err)
}
for _, p := range posts {
@@ -782,7 +784,7 @@ func (s *DataStore) Metas(siteID string) (umetas []UserMetaData, pmetas []PostMe
// process blocked users
blocked, err := s.BlockedUsers(siteID)
if err != nil {
return nil, nil, errors.Wrapf(err, "can't get list of blocked users for %s", siteID)
return nil, nil, fmt.Errorf("can't get list of blocked users for %s: %w", siteID, err)
}
for _, b := range blocked {
val, ok := m[b.ID]
@@ -797,7 +799,7 @@ func (s *DataStore) Metas(siteID string) (umetas []UserMetaData, pmetas []PostMe
// process verified users
verified, err := s.Engine.ListFlags(engine.FlagRequest{Locator: store.Locator{SiteID: siteID}, Flag: engine.Verified})
if err != nil {
return nil, nil, errors.Wrapf(err, "can't get list of verified users for %s", siteID)
return nil, nil, fmt.Errorf("can't get list of verified users for %s: %w", siteID, err)
}
for _, vi := range verified {
v := vi.(string)
@@ -812,7 +814,7 @@ func (s *DataStore) Metas(siteID string) (umetas []UserMetaData, pmetas []PostMe
// process users details
usersDetails, err := s.Engine.UserDetail(engine.UserDetailRequest{Locator: store.Locator{SiteID: siteID}, Detail: engine.AllUserDetails})
if err != nil {
return nil, nil, errors.Wrapf(err, "can't get user details for %s", siteID)
return nil, nil, fmt.Errorf("can't get user details for %s: %w", siteID, err)
}
for _, entry := range usersDetails {
val, ok := m[entry.UserID]
@@ -982,15 +984,15 @@ func (s *DataStore) prepVotes(c store.Comment, user store.User) store.Comment {
// Note: secret shared across sites, but some sites can be disabled.
func (s *DataStore) getSecret(siteID string) (secret string, err error) {
if secret, err = s.AdminStore.Key("any"); err != nil {
return "", errors.Wrapf(err, "can't get secret for site %s", siteID)
return "", fmt.Errorf("can't get secret for site %s: %w", siteID, err)
}
ok, err := s.AdminStore.Enabled(siteID)
if err != nil {
return "", errors.Wrapf(err, "can't check secret enabled for site %s", siteID)
return "", fmt.Errorf("can't check secret enabled for site %s: %w", siteID, err)
}
if !ok {
return "", errors.Errorf("site %s disabled", siteID)
return "", fmt.Errorf("site %s disabled", siteID)
}
return secret, nil
}
+5 -6
View File
@@ -17,7 +17,6 @@ import (
"time"
"github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
bolt "go.etcd.io/bbolt"
@@ -754,10 +753,10 @@ func TestService_ValidateComment(t *testing.T) {
inp store.Comment
err error
}{
{inp: store.Comment{}, err: errors.New("empty comment text")},
{inp: store.Comment{}, err: fmt.Errorf("empty comment text")},
{inp: store.Comment{Orig: "something blah", User: store.User{ID: "myid", Name: "name"}}, err: nil},
{inp: store.Comment{Orig: "something blah", User: store.User{ID: "myid"}}, err: errors.New("empty user info")},
{inp: store.Comment{Orig: longText, User: store.User{ID: "myid", Name: "name"}}, err: errors.New("comment text exceeded max allowed size 2000 (4000)")},
{inp: store.Comment{Orig: "something blah", User: store.User{ID: "myid"}}, err: fmt.Errorf("empty user info")},
{inp: store.Comment{Orig: longText, User: store.User{ID: "myid", Name: "name"}}, err: fmt.Errorf("comment text exceeded max allowed size 2000 (4000)")},
}
for n, tt := range tbl {
@@ -1439,7 +1438,7 @@ func TestService_ResubmitStagingImages(t *testing.T) {
bError := DataStore{Engine: eng, EditDuration: 10 * time.Millisecond, ImageService: imgSvcError}
// resubmit will receive error from image storage and should return it
mockStoreError.On("Info").Once().Return(image.StoreInfo{}, errors.New("mock_err"))
mockStoreError.On("Info").Once().Return(image.StoreInfo{}, fmt.Errorf("mock_err"))
err = bError.ResubmitStagingImages([]string{"radio-t"})
assert.EqualError(t, err, "mock_err")
@@ -1459,7 +1458,7 @@ func TestService_ResubmitStagingImages_EngineError(t *testing.T) {
site1Req := engine.FindRequest{Locator: store.Locator{SiteID: "site1", URL: ""}, Sort: "time", Since: time.Time{}.Add(time.Second)}
site2Req := engine.FindRequest{Locator: store.Locator{SiteID: "site2", URL: ""}, Sort: "time", Since: time.Time{}.Add(time.Second)}
engineMock.On("Find", site1Req).Return(nil, nil)
engineMock.On("Find", site2Req).Return(nil, errors.New("mockError"))
engineMock.On("Find", site2Req).Return(nil, fmt.Errorf("mockError"))
b := DataStore{Engine: &engineMock, EditDuration: 10 * time.Millisecond, ImageService: imgSvc}
// One call without error and one with error
+4 -4
View File
@@ -1,6 +1,7 @@
package service
import (
"fmt"
"io"
"net/http"
"strings"
@@ -8,7 +9,6 @@ import (
"github.com/go-pkgz/lcw"
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"golang.org/x/net/html"
)
@@ -43,7 +43,7 @@ func (t *TitleExtractor) Get(url string) (string, error) {
b, err := t.cache.Get(url, func() (interface{}, error) {
resp, err := client.Get(url)
if err != nil {
return nil, errors.Wrapf(err, "failed to load page %s", url)
return nil, fmt.Errorf("failed to load page %s: %w", url, err)
}
defer func() {
if err = resp.Body.Close(); err != nil {
@@ -51,12 +51,12 @@ func (t *TitleExtractor) Get(url string) (string, error) {
}
}()
if resp.StatusCode != 200 {
return nil, errors.Errorf("can't load page %s, code %d", url, resp.StatusCode)
return nil, fmt.Errorf("can't load page %s, code %d", url, resp.StatusCode)
}
title, ok := t.getTitle(resp.Body)
if !ok {
return nil, errors.Errorf("can't get title for %s", url)
return nil, fmt.Errorf("can't get title for %s", url)
}
return title, nil
})
+2 -2
View File
@@ -2,7 +2,7 @@ package store
import (
"crypto/sha1"
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
@@ -63,4 +63,4 @@ func (mock mockHash) Sum(_ []byte) []byte { return nil }
func (mock mockHash) Reset() {}
func (mock mockHash) Size() int { return 0 }
func (mock mockHash) BlockSize() int { return 0 }
func (mock mockHash) Write(_ []byte) (n int, err error) { return 0, errors.New("error") }
func (mock mockHash) Write(_ []byte) (n int, err error) { return 0, fmt.Errorf("error") }