diff --git a/.golangci.yml b/.golangci.yml index d6e348e6112065096d6acd30f6d70ca42d393b72_LmdvbGFuZ2NpLnltbA==..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_LmdvbGFuZ2NpLnltbA== 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -35,7 +35,7 @@ - dupl # Tool for code clone detection style v1.0.0 - dupword # âš™ï¸ Checks for duplicate words in the source code. comment ✔ 1.50.0 - durationcheck # check for two durations multiplied together bugs v1.37.0 - # - err113 # Go linter to check the errors handling expressions. style, error v1.26.0 + - err113 # Go linter to check the errors handling expressions. style, error v1.26.0 - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occations, where the check for the returned error can be omitted. bugs 1.44.0 - errname # Checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error. style v1.42.0 - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. bugs, error v1.32.0 diff --git a/auth/auth.go b/auth/auth.go index d6e348e6112065096d6acd30f6d70ca42d393b72_YXV0aC9hdXRoLmdv..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_YXV0aC9hdXRoLmdv 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -37,6 +37,10 @@ // CookieName is the name of the auth cookie. CookieName = "rednerd-auth" + + // ErrCookieNotFound is the custom error returned when + // http.ErrNoCookie is returned. + ErrCookieNotFound = errors.New("cookie not found") ) // TokenOptions holds options related to the JWT. @@ -259,7 +263,7 @@ cookie, err := request.Cookie(CookieName) if err != nil { if errors.Is(err, http.ErrNoCookie) { - return nil, fmt.Errorf("no %s cookie found", CookieName) + return nil, fmt.Errorf("%w: %s", ErrCookieNotFound, CookieName) } return nil, err diff --git a/cmd/rednerd/cmd/admin_password.go b/cmd/rednerd/cmd/admin_password.go index d6e348e6112065096d6acd30f6d70ca42d393b72_Y21kL3JlZG5lcmQvY21kL2FkbWluX3Bhc3N3b3JkLmdv..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_Y21kL3JlZG5lcmQvY21kL2FkbWluX3Bhc3N3b3JkLmdv 100644 --- a/cmd/rednerd/cmd/admin_password.go +++ b/cmd/rednerd/cmd/admin_password.go @@ -17,8 +17,13 @@ "orus.io/orus-io/rednerd/utils" ) -// ErrPasswordMismatch is returned if the password aren't equal. -var ErrPasswordMismatch = errors.New("password mismatch") +var ( + // ErrPasswordMismatch is returned if the password aren't equal. + ErrPasswordMismatch = errors.New("password mismatch") + // ErrInvalidArgsNumber is returned the number of args sent to the Execute + // function are invalid. + ErrInvalidArgsNumber = errors.New("invalid number of args") +) // AdminPasswordCmd is the 'admin-user' command. type AdminPasswordCmd struct { @@ -31,7 +36,7 @@ defer cancel() if len(args) > 1 { - return fmt.Errorf("invalid number of args. Expects 1 or 0, got %d", len(args)) + return fmt.Errorf("%w. Expects 1 or 0, got %d", ErrInvalidArgsNumber, len(args)) } db, err := sqlx.Open("postgres", cmd.options.Dsn) diff --git a/engines/wpd/engine.go b/engines/wpd/engine.go index d6e348e6112065096d6acd30f6d70ca42d393b72_ZW5naW5lcy93cGQvZW5naW5lLmdv..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_ZW5naW5lcy93cGQvZW5naW5lLmdv 100644 --- a/engines/wpd/engine.go +++ b/engines/wpd/engine.go @@ -14,8 +14,9 @@ ) var ( - ErrInvalidInputType = errors.New("invalid input type") - ErrInvalidOutputType = errors.New("invalid output type") + ErrInvalidInputType = errors.New("invalid input type") + ErrInvalidOutputType = errors.New("invalid output type") + ErrWPDResultIDJobIDMismatch = errors.New("wpd: result id does not match jobid") ) // BufferCloser is a bytes.Buffer that implements io.ReadCloser. @@ -134,7 +135,10 @@ } if rid != id { - return fmt.Errorf("wpd: result id does not match jobid. Expected: %d, got: %d", id, rid) + return fmt.Errorf( + "%w. Expected: %d, got: %d", + ErrWPDResultIDJobIDMismatch, id, rid, + ) } w.Metadata().Update(metadata) diff --git a/engines/wpd/wpd.go b/engines/wpd/wpd.go index d6e348e6112065096d6acd30f6d70ca42d393b72_ZW5naW5lcy93cGQvd3BkLmdv..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_ZW5naW5lcy93cGQvd3BkLmdv 100644 --- a/engines/wpd/wpd.go +++ b/engines/wpd/wpd.go @@ -15,10 +15,11 @@ ) var ( - ErrJobFailed = errors.New("wpd render job failed") - ErrWpdFailed = errors.New("wpd stopped") - ErrReadTimeout = errors.New("read timeout") - ErrInvalidWPDInfo = errors.New("invalid wpd info") + ErrJobFailed = errors.New("wpd render job failed") + ErrWpdFailed = errors.New("wpd stopped") + ErrReadTimeout = errors.New("read timeout") + ErrInvalidWPDInfo = errors.New("invalid wpd info") + ErrInvalidWPDProtocolVersion = errors.New("invalid wpd protocol version") ) // Request ... @@ -84,7 +85,10 @@ log.Err(err).Msg("problem stopping the runner") } - return nil, fmt.Errorf("invalid wpd protocol version. Expects \"1\", got: \"%s\"", info.Protocol) + return nil, fmt.Errorf( + "%w. Expects \"1\", got: \"%s\"", + ErrInvalidWPDProtocolVersion, info.Protocol, + ) } return &processWPD{ @@ -163,6 +167,7 @@ return p.process.Kill() } +//nolint:err113 func (p *processWPD) receiveResponse() (*Response, error) { line, err := p.reader.ReadBytes(byte('\n')) if err != nil { diff --git a/lib/mailsender.go b/lib/mailsender.go index d6e348e6112065096d6acd30f6d70ca42d393b72_bGliL21haWxzZW5kZXIuZ28=..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_bGliL21haWxzZW5kZXIuZ28= 100644 --- a/lib/mailsender.go +++ b/lib/mailsender.go @@ -17,6 +17,8 @@ "orus.io/orus-io/rednerd/models" ) +var ErrUnknownSMTPAuthType = errors.New("unknown smtp-auth type") + //nolint:lll type MailSender struct { Mode string `long:"mode" ini-name:"mode" default:"smtp" choice:"smtp"` @@ -47,7 +49,7 @@ s.auth = smtp.PlainAuth("", s.SMTPUser, s.SMTPPassword, s.SMTPHost) default: - return fmt.Errorf("unknown smtp-auth type: %s", s.SMTPAuth) + return fmt.Errorf("%w: %s", ErrUnknownSMTPAuthType, s.SMTPAuth) } s.addr = s.SMTPHost + ":" + s.SMTPPort diff --git a/lib/redner.go b/lib/redner.go index d6e348e6112065096d6acd30f6d70ca42d393b72_bGliL3JlZG5lci5nbw==..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_bGliL3JlZG5lci5nbw== 100644 --- a/lib/redner.go +++ b/lib/redner.go @@ -14,7 +14,11 @@ "orus.io/orus-io/rednerd/rendering" ) -var ErrReadBody = errors.New("could not read rendered email body") +var ( + ErrReadBody = errors.New("could not read rendered email body") + ErrUnsuitableType = errors.New("template produces a type unsuitable for emails") + ErrUserNoEmail = errors.New("user has no email") +) var DefaultResetMailTemplate = models.Template{ Language: "mustache", @@ -80,7 +84,7 @@ } if targetType != "text/plain" && targetType != "text/html" { - return fmt.Errorf("template produces a type unsuitable for emails: %s", targetType) + return fmt.Errorf("%w: %s", ErrUnsuitableType, targetType) } template.Metadata.Add("to", "{{user.email}}") @@ -135,7 +139,7 @@ user models.UserDB, template *models.Template, ) error { if user.Email == "" { - return fmt.Errorf("user %s has no email", user.Username) + return fmt.Errorf("%w: %s", ErrUserNoEmail, user.Username) } if template == nil { diff --git a/rendering/engine_registry.go b/rendering/engine_registry.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVuZGVyaW5nL2VuZ2luZV9yZWdpc3RyeS5nbw==..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVuZGVyaW5nL2VuZ2luZV9yZWdpc3RyeS5nbw== 100644 --- a/rendering/engine_registry.go +++ b/rendering/engine_registry.go @@ -18,6 +18,8 @@ ErrDuplicateName = errors.New("duplicate engine name") ErrEmptyPipeline = errors.New("empty pipeline") + + ErrNoEngine = errors.New("no engine found to convert") ) // EngineTransitionInfo describes a. @@ -281,7 +283,7 @@ } if len(engines) == 0 { - return nil, fmt.Errorf("no engine found to convert from %s to %s", fromType, toType) + return nil, fmt.Errorf("%w from %s to %s", ErrNoEngine, fromType, toType) } return engines[0], nil diff --git a/rendering/pooled_engine.go b/rendering/pooled_engine.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVuZGVyaW5nL3Bvb2xlZF9lbmdpbmUuZ28=..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVuZGVyaW5nL3Bvb2xlZF9lbmdpbmUuZ28= 100644 --- a/rendering/pooled_engine.go +++ b/rendering/pooled_engine.go @@ -136,6 +136,7 @@ return nil } +//nolint:err113 func errPanic(anyErr any) error { err, ok := anyErr.(error) if ok { diff --git a/restapi/handlers/account-template-add.go b/restapi/handlers/account-template-add.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy9hY2NvdW50LXRlbXBsYXRlLWFkZC5nbw==..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy9hY2NvdW50LXRlbXBsYXRlLWFkZC5nbw== 100644 --- a/restapi/handlers/account-template-add.go +++ b/restapi/handlers/account-template-add.go @@ -17,9 +17,6 @@ "orus.io/orus-io/rednerd/restapi/operations/template_management" ) -// ErrEmptyField is returned when a mandatory field is empty. -var ErrEmptyField = errors.New("empty field") - // NewAccountTemplateAddHandler returns a AccountTemplateAddHandler. func NewAccountTemplateAddHandler(db *sqlx.DB, now func() time.Time) *AccountTemplateAddHandler { return &AccountTemplateAddHandler{ diff --git a/restapi/handlers/account-template-preview.go b/restapi/handlers/account-template-preview.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy9hY2NvdW50LXRlbXBsYXRlLXByZXZpZXcuZ28=..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy9hY2NvdW50LXRlbXBsYXRlLXByZXZpZXcuZ28= 100644 --- a/restapi/handlers/account-template-preview.go +++ b/restapi/handlers/account-template-preview.go @@ -64,7 +64,10 @@ } if len(documents) != 1 { - return InternalError(fmt.Errorf("expected 1 rendered document, got %d", len(documents)), log, false) + return InternalError(fmt.Errorf( + "%w: expected 1, got %d", + ErrRenderedDocumentSizeMismatch, len(documents), + ), log, false) } doc := documents[0] diff --git a/restapi/handlers/account-template-update.go b/restapi/handlers/account-template-update.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy9hY2NvdW50LXRlbXBsYXRlLXVwZGF0ZS5nbw==..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy9hY2NvdW50LXRlbXBsYXRlLXVwZGF0ZS5nbw== 100644 --- a/restapi/handlers/account-template-update.go +++ b/restapi/handlers/account-template-update.go @@ -17,9 +17,6 @@ "orus.io/orus-io/rednerd/utils" ) -// ErrDuplicateTemplateName is returned if the template's name already exists. -var ErrDuplicateTemplateName = errors.New("duplicate template name") - // NewAccountTemplateUpdateHandler returns a AccountTemplateUpdateHandler. func NewAccountTemplateUpdateHandler(db *sqlx.DB) *AccountTemplateUpdateHandler { columns := utils.StringSliceFilterOut( diff --git a/restapi/handlers/auth-userinfo.go b/restapi/handlers/auth-userinfo.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy9hdXRoLXVzZXJpbmZvLmdv..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy9hdXRoLXVzZXJpbmZvLmdv 100644 --- a/restapi/handlers/auth-userinfo.go +++ b/restapi/handlers/auth-userinfo.go @@ -16,9 +16,6 @@ "orus.io/orus-io/rednerd/restapi/operations/auth" ) -// ErrInvalidAuth is returned if the principal is nil. -var ErrInvalidAuth = errors.New("invalid auth") - // NewAuthUserinfoHandler returns a Userinfo handler. func NewAuthUserinfoHandler(db *sqlx.DB) *AuthUserinfo { return &AuthUserinfo{db} @@ -53,7 +50,7 @@ var user models.UserDB if err := database.GetContext(ctx, tx, &user, userQuery, log); err != nil { if errors.Is(err, sql.ErrNoRows) { - return nil, fmt.Errorf("unknown user %s", principal.Subject) + return nil, fmt.Errorf("%w %s", ErrNoSuchUser, principal.Subject) } return nil, err diff --git a/restapi/handlers/errors.go b/restapi/handlers/errors.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy9lcnJvcnMuZ28=..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy9lcnJvcnMuZ28= 100644 --- a/restapi/handlers/errors.go +++ b/restapi/handlers/errors.go @@ -12,4 +12,27 @@ "orus.io/orus-io/rednerd/utils" ) +var ( + // ErrNoSuchTemplate is returned if the template does not exist. + ErrNoSuchTemplate = errors.New("no such template") + // ErrDuplicateTemplateName is returned if the template's name already exists. + ErrDuplicateTemplateName = errors.New("duplicate template name") + // ErrNoTemplateNorDocuments is returned if the render request has no template + // nor a document. + ErrNoTemplateNorDocument = errors.New("no template nor document. Must have one") + // ErrNoTemplate is returned if the template-varlist request has no template. + ErrNoTemplate = errors.New("no template given") + // ErrUnknownTemplateLanguage is returned when the language is unknown. + ErrUnknownTemplateLanguage = errors.New("unknown template language") + // ErrEmptyField is returned when a mandatory field is empty. + ErrEmptyField = errors.New("empty field") + // ErrNoSuchUser is returned if the user does not exist. + ErrNoSuchUser = errors.New("no such user") + // ErrInvalidAuth is returned if the principal is nil. + ErrInvalidAuth = errors.New("invalid auth") + // ErrRenderedDocumentSizeMismatch is returned when there is an expected + // Number of documents that /render must return (like when previewing). + ErrRenderedDocumentSizeMismatch = errors.New("rendered documents size mismatch") +) + func NotFound(msg string) utils.HTTPError { @@ -15,5 +38,5 @@ func NotFound(msg string) utils.HTTPError { - return utils.NewHTTPError(http.StatusNotFound, errors.New(msg)) + return utils.NewHTTPError(http.StatusNotFound, errors.New(msg)) //nolint:err113 } func NotFoundf(msg string, args ...any) utils.HTTPError { diff --git a/restapi/handlers/render.go b/restapi/handlers/render.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy9yZW5kZXIuZ28=..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy9yZW5kZXIuZ28= 100644 --- a/restapi/handlers/render.go +++ b/restapi/handlers/render.go @@ -13,10 +13,6 @@ "orus.io/orus-io/rednerd/utils" ) -// ErrNoTemplateNorDocuments is returned if the render request has no template -// nor a document. -var ErrNoTemplateNorDocument = errors.New("no template nor document. Must have one") - // NewRenderHandler creates a RenderHandler. func NewRenderHandler(renderer *redner.Renderer) *RenderHandler { return &RenderHandler{renderer} diff --git a/restapi/handlers/template-varlist.go b/restapi/handlers/template-varlist.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy90ZW1wbGF0ZS12YXJsaXN0Lmdv..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy90ZW1wbGF0ZS12YXJsaXN0Lmdv 100644 --- a/restapi/handlers/template-varlist.go +++ b/restapi/handlers/template-varlist.go @@ -18,9 +18,6 @@ "orus.io/orus-io/rednerd/utils" ) -// ErrNoTemplate is returned if the template-varlist request has no template. -var ErrNoTemplate = errors.New("no template given") - // NewTemplateVarlistHandler creates a TemplateVarlistHandler. func NewTemplateVarlistHandler( db *sqlx.DB, @@ -58,7 +55,7 @@ ); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, BadRequestError(fmt.Errorf( - "no such template: %s/%s", template.Account, template.Name)) + "%w: %s/%s", ErrNoSuchTemplate, template.Account, template.Name)) } return nil, err @@ -72,7 +69,7 @@ if err != nil { if errors.Is(err, rendering.ErrUnknownTemplateLanguage) { return nil, utils.HTTPBadRequest( - fmt.Errorf("unknown template language: %s", template.Language)) + fmt.Errorf("%w: %s", ErrUnknownTemplateLanguage, template.Language)) } return nil, utils.HTTPBadRequest( diff --git a/restapi/handlers/user-apikey-delete.go b/restapi/handlers/user-apikey-delete.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy91c2VyLWFwaWtleS1kZWxldGUuZ28=..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy91c2VyLWFwaWtleS1kZWxldGUuZ28= 100644 --- a/restapi/handlers/user-apikey-delete.go +++ b/restapi/handlers/user-apikey-delete.go @@ -14,9 +14,6 @@ "orus.io/orus-io/rednerd/restapi/operations/user_management" ) -// ErrNoSuchUserApikey is returned if the user does not exist. -var ErrNoSuchUserApikey = errors.New("no such user") - // NewUserApikeyDeleteHandler returns a UserApikeyDeleteHandler. func NewUserApikeyDeleteHandler(db *sqlx.DB) *UserApikeyDeleteHandler { return &UserApikeyDeleteHandler{ @@ -60,7 +57,7 @@ } if count == 0 { - return ErrNoSuchUserApikey + return ErrNoSuchUser } return tx.Commit() @@ -81,7 +78,7 @@ err := h.handle(params.HTTPRequest.Context(), params.Username, params.Name) if err != nil { - if errors.Is(err, ErrNoSuchUserApikey) { + if errors.Is(err, ErrNoSuchUser) { return user_management.NewUserApikeyDeleteNotFound() } diff --git a/restapi/handlers/user-delete.go b/restapi/handlers/user-delete.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9oYW5kbGVycy91c2VyLWRlbGV0ZS5nbw==..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9oYW5kbGVycy91c2VyLWRlbGV0ZS5nbw== 100644 --- a/restapi/handlers/user-delete.go +++ b/restapi/handlers/user-delete.go @@ -14,9 +14,6 @@ "orus.io/orus-io/rednerd/restapi/operations/user_management" ) -// ErrNoSuchUser is returned if the user does not exist. -var ErrNoSuchUser = errors.New("no such user") - // NewUserDeleteHandler returns a UserDeleteHandler. func NewUserDeleteHandler(db *sqlx.DB) *UserDeleteHandler { return &UserDeleteHandler{ diff --git a/restapi/server_.go b/restapi/server_.go index d6e348e6112065096d6acd30f6d70ca42d393b72_cmVzdGFwaS9zZXJ2ZXJfLmdv..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_cmVzdGFwaS9zZXJ2ZXJfLmdv 100644 --- a/restapi/server_.go +++ b/restapi/server_.go @@ -210,8 +210,10 @@ go func(l net.Listener) { defer wg.Done() - if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { - s.Fatalf("%v", err) + if err := domainSocket.Serve(l); err != nil { + if !errors.Is(err, http.ErrServerClosed) { + s.Fatalf("%v", err) + } } s.Logf("Stopped serving rednerd at unix://%s", s.SocketPath) @@ -245,8 +247,10 @@ go func(l net.Listener) { defer wg.Done() - if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { - s.Fatalf("%v", err) + if err := httpServer.Serve(l); err != nil { + if !errors.Is(err, http.ErrServerClosed) { + s.Fatalf("%v", err) + } } s.Logf("Stopped serving rednerd at http://%s", l.Addr()) @@ -362,8 +366,10 @@ go func(l net.Listener) { defer wg.Done() - if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { - s.Fatalf("%v", err) + if err := httpsServer.Serve(l); err != nil { + if !errors.Is(err, http.ErrServerClosed) { + s.Fatalf("%v", err) + } } s.Logf("Stopped serving rednerd at https://%s", l.Addr()) diff --git a/utils/http.go b/utils/http.go index d6e348e6112065096d6acd30f6d70ca42d393b72_dXRpbHMvaHR0cC5nbw==..dd1f18f5d92a42427873aa909bdf3be2cc6eb5b4_dXRpbHMvaHR0cC5nbw== 100644 --- a/utils/http.go +++ b/utils/http.go @@ -17,7 +17,7 @@ func NewHTTPErrorf(statusCode int, err string, args ...interface{}) HTTPError { return HTTPError{ StatusCode: statusCode, - Err: fmt.Errorf(err, args...), + Err: fmt.Errorf(err, args...), //nolint:err113 } }