From ba86db1263fe1f45dc6ef6e1502f28233a5acbe1 Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Wed, 20 Apr 2022 00:10:31 +0200 Subject: [PATCH] replace errors package with fmt.Errorf https://gist.github.com/Peltoche/60b8b81dfbf70164d0e2b88988003229 was used for it, thanks to @Peltoche for publishing it. --- .../_example/memory_store/accessor/admin.go | 11 +- .../_example/memory_store/accessor/data.go | 36 ++--- .../_example/memory_store/accessor/image.go | 8 +- backend/_example/memory_store/go.mod | 2 +- backend/app/cmd/avatar.go | 12 +- backend/app/cmd/avatar_test.go | 4 +- backend/app/cmd/backup.go | 9 +- backend/app/cmd/cleanup.go | 33 ++-- backend/app/cmd/cmd.go | 8 +- backend/app/cmd/import.go | 13 +- backend/app/cmd/remap.go | 9 +- backend/app/cmd/server.go | 68 +++++---- backend/app/migrator/backup.go | 9 +- backend/app/migrator/commento.go | 6 +- backend/app/migrator/commento_test.go | 3 +- backend/app/migrator/disqus.go | 6 +- backend/app/migrator/migrator.go | 6 +- backend/app/migrator/native.go | 18 +-- backend/app/migrator/wordpress.go | 6 +- backend/app/notify/email.go | 36 +++-- backend/app/notify/email_test.go | 16 +- backend/app/notify/notify_test.go | 5 +- backend/app/notify/slack.go | 6 +- backend/app/notify/telegram.go | 35 +++-- backend/app/notify/telegram_test.go | 2 +- backend/app/notify/webhook.go | 10 +- backend/app/notify/webhook_test.go | 6 +- backend/app/providers/telegram_test.go | 6 +- backend/app/rest/api/admin.go | 6 +- backend/app/rest/api/migrator.go | 11 +- backend/app/rest/api/rest.go | 3 +- backend/app/rest/api/rest_private.go | 41 +++-- backend/app/rest/api/rest_private_test.go | 3 +- backend/app/rest/api/rest_public.go | 8 +- backend/app/rest/api/rest_test.go | 17 +-- backend/app/rest/api/rss.go | 3 +- backend/app/rest/httperrors_test.go | 9 +- backend/app/rest/proxy/image.go | 12 +- backend/app/rest/user.go | 4 +- backend/app/store/admin/admin.go | 4 +- backend/app/store/engine/bolt.go | 142 ++++++++++-------- backend/app/store/image/bolt_store.go | 44 +++--- backend/app/store/image/fs_store.go | 37 +++-- backend/app/store/image/image.go | 11 +- backend/app/store/service/service.go | 58 +++---- backend/app/store/service/service_test.go | 11 +- backend/app/store/service/title.go | 8 +- backend/app/store/user_test.go | 4 +- 48 files changed, 426 insertions(+), 399 deletions(-) diff --git a/backend/_example/memory_store/accessor/admin.go b/backend/_example/memory_store/accessor/admin.go index 95a6fde7..d0cbf866 100644 --- a/backend/_example/memory_store/accessor/admin.go +++ b/backend/_example/memory_store/accessor/admin.go @@ -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 diff --git a/backend/_example/memory_store/accessor/data.go b/backend/_example/memory_store/accessor/data.go index 179bf0af..f12e941a 100644 --- a/backend/_example/memory_store/accessor/data.go +++ b/backend/_example/memory_store/accessor/data.go @@ -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) { diff --git a/backend/_example/memory_store/accessor/image.go b/backend/_example/memory_store/accessor/image.go index dfe8dc57..a5757f5e 100644 --- a/backend/_example/memory_store/accessor/image.go +++ b/backend/_example/memory_store/accessor/image.go @@ -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() diff --git a/backend/_example/memory_store/go.mod b/backend/_example/memory_store/go.mod index 42144957..bfbdb519 100644 --- a/backend/_example/memory_store/go.mod +++ b/backend/_example/memory_store/go.mod @@ -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 diff --git a/backend/app/cmd/avatar.go b/backend/app/cmd/avatar.go index f6f14b1c..3baa4185 100644 --- a/backend/app/cmd/avatar.go +++ b/backend/app/cmd/avatar.go @@ -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) } diff --git a/backend/app/cmd/avatar_test.go b/backend/app/cmd/avatar_test.go index 5f4a8fa9..3d84fa62 100644 --- a/backend/app/cmd/avatar_test.go +++ b/backend/app/cmd/avatar_test.go @@ -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", diff --git a/backend/app/cmd/backup.go b/backend/app/cmd/backup.go index 2c5124a0..7f8c6748 100644 --- a/backend/app/cmd/backup.go +++ b/backend/app/cmd/backup.go @@ -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) diff --git a/backend/app/cmd/cleanup.go b/backend/app/cmd/cleanup.go index a842e906..4409977a 100644 --- a/backend/app/cmd/cleanup.go +++ b/backend/app/cmd/cleanup.go @@ -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 } diff --git a/backend/app/cmd/cmd.go b/backend/app/cmd/cmd.go index c630f3c0..2af3e729 100644 --- a/backend/app/cmd/cmd.go +++ b/backend/app/cmd/cmd.go @@ -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 diff --git a/backend/app/cmd/import.go b/backend/app/cmd/import.go index a7b990a8..3626d9ad 100644 --- a/backend/app/cmd/import.go +++ b/backend/app/cmd/import.go @@ -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 diff --git a/backend/app/cmd/remap.go b/backend/app/cmd/remap.go index 10bead5f..31acb8fe 100644 --- a/backend/app/cmd/remap.go +++ b/backend/app/cmd/remap.go @@ -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)) diff --git a/backend/app/cmd/server.go b/backend/app/cmd/server.go index dd11bf1a..9ed9e220 100644 --- a/backend/app/cmd/server.go +++ b/backend/app/cmd/server.go @@ -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 diff --git a/backend/app/migrator/backup.go b/backend/app/migrator/backup.go index 0c9b6b32..37a29024 100644 --- a/backend/app/migrator/backup.go +++ b/backend/app/migrator/backup.go @@ -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 diff --git a/backend/app/migrator/commento.go b/backend/app/migrator/commento.go index 2886302a..a5c06fce 100644 --- a/backend/app/migrator/commento.go +++ b/backend/app/migrator/commento.go @@ -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") } } diff --git a/backend/app/migrator/commento_test.go b/backend/app/migrator/commento_test.go index fe0212e3..f79e9ae0 100644 --- a/backend/app/migrator/commento_test.go +++ b/backend/app/migrator/commento_test.go @@ -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) { diff --git a/backend/app/migrator/disqus.go b/backend/app/migrator/disqus.go index d5055ba0..ef08fb7e 100644 --- a/backend/app/migrator/disqus.go +++ b/backend/app/migrator/disqus.go @@ -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") } } diff --git a/backend/app/migrator/migrator.go b/backend/app/migrator/migrator.go index d7671f92..9ecdec87 100644 --- a/backend/app/migrator/migrator.go +++ b/backend/app/migrator/migrator.go @@ -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 diff --git a/backend/app/migrator/native.go b/backend/app/migrator/native.go index 43f6f65f..1fe81fcd 100644 --- a/backend/app/migrator/native.go +++ b/backend/app/migrator/native.go @@ -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) diff --git a/backend/app/migrator/wordpress.go b/backend/app/migrator/wordpress.go index 0b9e3ece..1bb224c3 100644 --- a/backend/app/migrator/wordpress.go +++ b/backend/app/migrator/wordpress.go @@ -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") } } diff --git a/backend/app/notify/email.go b/backend/app/notify/email.go index eca91dd7..6e858eb2 100644 --- a/backend/app/notify/email.go +++ b/backend/app/notify/email.go @@ -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) diff --git a/backend/app/notify/email_test.go b/backend/app/notify/email_test.go index afafca4a..091f18f2 100644 --- a/backend/app/notify/email_test.go +++ b/backend/app/notify/email_test.go @@ -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 } diff --git a/backend/app/notify/notify_test.go b/backend/app/notify/notify_test.go index 7f696a08..91e677d9 100644 --- a/backend/app/notify/notify_test.go +++ b/backend/app/notify/notify_test.go @@ -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 } diff --git a/backend/app/notify/slack.go b/backend/app/notify/slack.go index 3828c4bc..9cee44ef 100644 --- a/backend/app/notify/slack.go +++ b/backend/app/notify/slack.go @@ -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") } diff --git a/backend/app/notify/telegram.go b/backend/app/notify/telegram.go index 9a5f923e..b383c12e 100644 --- a/backend/app/notify/telegram.go +++ b/backend/app/notify/telegram.go @@ -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) } diff --git a/backend/app/notify/telegram_test.go b/backend/app/notify/telegram_test.go index fbca73d2..9d448e52 100644 --- a/backend/app/notify/telegram_test.go +++ b/backend/app/notify/telegram_test.go @@ -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() diff --git a/backend/app/notify/webhook.go b/backend/app/notify/webhook.go index ff22757a..722968a7 100644 --- a/backend/app/notify/webhook.go +++ b/backend/app/notify/webhook.go @@ -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() diff --git a/backend/app/notify/webhook_test.go b/backend/app/notify/webhook_test.go index cb864044..abcd2bed 100644 --- a/backend/app/notify/webhook_test.go +++ b/backend/app/notify/webhook_test.go @@ -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}) diff --git a/backend/app/providers/telegram_test.go b/backend/app/providers/telegram_test.go index 5f479382..684be12c 100644 --- a/backend/app/providers/telegram_test.go +++ b/backend/app/providers/telegram_test.go @@ -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") } diff --git a/backend/app/rest/api/admin.go b/backend/app/rest/api/admin.go index d1e75790..635bae1e 100644 --- a/backend/app/rest/api/admin.go +++ b/backend/app/rest/api/admin.go @@ -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 } diff --git a/backend/app/rest/api/migrator.go b/backend/app/rest/api/migrator.go index 70dd908d..bd056c88 100644 --- a/backend/app/rest/api/migrator.go +++ b/backend/app/rest/api/migrator.go @@ -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 diff --git a/backend/app/rest/api/rest.go b/backend/app/rest/api/rest.go index 0907fbe2..664259b0 100644 --- a/backend/app/rest/api/rest.go +++ b/backend/app/rest/api/rest.go @@ -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 } diff --git a/backend/app/rest/api/rest_private.go b/backend/app/rest/api/rest_private.go index f44b2a75..618a4251 100644 --- a/backend/app/rest/api/rest_private.go +++ b/backend/app/rest/api/rest_private.go @@ -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 } diff --git a/backend/app/rest/api/rest_private_test.go b/backend/app/rest/api/rest_private_test.go index fd9bbe18..3347e78d 100644 --- a/backend/app/rest/api/rest_private_test.go +++ b/backend/app/rest/api/rest_private_test.go @@ -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 } diff --git a/backend/app/rest/api/rest_public.go b/backend/app/rest/api/rest_public.go index 42e2f478..5ebd704d 100644 --- a/backend/app/rest/api/rest_public.go +++ b/backend/app/rest/api/rest_public.go @@ -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 } diff --git a/backend/app/rest/api/rest_test.go b/backend/app/rest/api/rest_test.go index 4084412e..a504c827 100644 --- a/backend/app/rest/api/rest_test.go +++ b/backend/app/rest/api/rest_test.go @@ -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 diff --git a/backend/app/rest/api/rss.go b/backend/app/rest/api/rss.go index bf3da0ac..4be92da0 100644 --- a/backend/app/rest/api/rss.go +++ b/backend/app/rest/api/rss.go @@ -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) diff --git a/backend/app/rest/httperrors_test.go b/backend/app/rest/httperrors_test.go index cc7b0872..5b4e7308 100644 --- a/backend/app/rest/httperrors_test.go +++ b/backend/app/rest/httperrors_test.go @@ -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]") diff --git a/backend/app/rest/proxy/image.go b/backend/app/rest/proxy/image.go index 77e5ceac..be6ce9e5 100644 --- a/backend/app/rest/proxy/image.go +++ b/backend/app/rest/proxy/image.go @@ -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 } diff --git a/backend/app/rest/user.go b/backend/app/rest/user.go index 6e4c9b5d..f908bea9 100644 --- a/backend/app/rest/user.go +++ b/backend/app/rest/user.go @@ -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{ diff --git a/backend/app/store/admin/admin.go b/backend/app/store/admin/admin.go index 7a023ada..d45c0fc1 100644 --- a/backend/app/store/admin/admin.go +++ b/backend/app/store/admin/admin.go @@ -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 } diff --git a/backend/app/store/engine/bolt.go b/backend/app/store/engine/bolt.go index 8028048a..b33e935e 100644 --- a/backend/app/store/engine/bolt.go +++ b/backend/app/store/engine/bolt.go @@ -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 } diff --git a/backend/app/store/image/bolt_store.go b/backend/app/store/image/bolt_store.go index 7465eac2..9d9fe9b2 100644 --- a/backend/app/store/image/bolt_store.go +++ b/backend/app/store/image/bolt_store.go @@ -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 } diff --git a/backend/app/store/image/fs_store.go b/backend/app/store/image/fs_store.go index ae338697..09ca5e78 100644 --- a/backend/app/store/image/fs_store.go +++ b/backend/app/store/image/fs_store.go @@ -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 } diff --git a/backend/app/store/image/image.go b/backend/app/store/image/image.go index cbe1170e..aae6f4de 100644 --- a/backend/app/store/image/image.go +++ b/backend/app/store/image/image.go @@ -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 } diff --git a/backend/app/store/service/service.go b/backend/app/store/service/service.go index ebf41cbc..e60a77bd 100644 --- a/backend/app/store/service/service.go +++ b/backend/app/store/service/service.go @@ -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 } diff --git a/backend/app/store/service/service_test.go b/backend/app/store/service/service_test.go index 88785ec6..a7dd6e62 100644 --- a/backend/app/store/service/service_test.go +++ b/backend/app/store/service/service_test.go @@ -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 diff --git a/backend/app/store/service/title.go b/backend/app/store/service/title.go index e30f12c9..01b6a127 100644 --- a/backend/app/store/service/title.go +++ b/backend/app/store/service/title.go @@ -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 }) diff --git a/backend/app/store/user_test.go b/backend/app/store/user_test.go index 5db80d7d..303f024d 100644 --- a/backend/app/store/user_test.go +++ b/backend/app/store/user_test.go @@ -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") }