From 56368f39633fa8f98e27618b4556b5d174ea5b8a Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 6 Dec 2022 14:15:48 +0800 Subject: [PATCH] refactor: use ctx in models --- models/actions/run.go | 29 ++------ models/actions/run_job.go | 14 +--- models/actions/run_list.go | 4 +- models/actions/runner.go | 74 ++++--------------- models/actions/runner_token.go | 27 ++----- models/actions/task.go | 35 +-------- models/actions/utils.go | 30 ++++++++ .../actions/{task_test.go => utils_test.go} | 0 modules/notification/actions/helper.go | 2 +- routers/api/actions/runner/runner.go | 6 +- routers/api/actions/runner/unary.go | 6 +- routers/common/runners.go | 19 ++--- routers/web/repo/actions/actions.go | 2 +- routers/web/repo/actions/view.go | 4 +- 14 files changed, 87 insertions(+), 165 deletions(-) rename models/actions/{task_test.go => utils_test.go} (100%) diff --git a/models/actions/run.go b/models/actions/run.go index 50c468d85a..ed2753148d 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/json" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "github.com/nektos/act/pkg/jobparser" "golang.org/x/exp/slices" @@ -129,8 +130,8 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err } // InsertRun inserts a bot run -func InsertRun(run *ActionRun, jobs []*jobparser.SingleWorkflow) error { - ctx, commiter, err := db.TxContext(db.DefaultContext) +func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error { + ctx, commiter, err := db.TxContext(ctx) if err != nil { return err } @@ -193,29 +194,13 @@ func InsertRun(run *ActionRun, jobs []*jobparser.SingleWorkflow) error { return commiter.Commit() } -// ErrRunNotExist represents an error for bot run not exist -type ErrRunNotExist struct { - ID int64 - RepoID int64 - Index int64 -} - -func (err ErrRunNotExist) Error() string { - if err.RepoID > 0 { - return fmt.Sprintf("run repe_id [%d] index [%d] is not exist", err.RepoID, err.Index) - } - return fmt.Sprintf("run [%d] is not exist", err.ID) -} - func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) { var run ActionRun has, err := db.GetEngine(ctx).Where("id=?", id).Get(&run) if err != nil { return nil, err } else if !has { - return nil, ErrRunNotExist{ - ID: id, - } + return nil, fmt.Errorf("run with id %d: %w", id, util.ErrNotExist) } return &run, nil @@ -230,10 +215,8 @@ func GetRunByIndex(ctx context.Context, repoID, index int64) (*ActionRun, error) if err != nil { return nil, err } else if !has { - return nil, ErrRunNotExist{ - RepoID: repoID, - Index: index, - } + return nil, fmt.Errorf("run with index %d %d: %w", repoID, index, util.ErrNotExist) + } return run, nil diff --git a/models/actions/run_job.go b/models/actions/run_job.go index 54e4ac8702..9237d20ba2 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "golang.org/x/exp/slices" "xorm.io/builder" @@ -78,24 +79,13 @@ func (job *ActionRunJob) LoadAttributes(ctx context.Context) error { return job.Run.LoadAttributes(ctx) } -// ErrRunJobNotExist represents an error for bot run job not exist -type ErrRunJobNotExist struct { - ID int64 -} - -func (err ErrRunJobNotExist) Error() string { - return fmt.Sprintf("run job [%d] is not exist", err.ID) -} - func GetRunJobByID(ctx context.Context, id int64) (*ActionRunJob, error) { var job ActionRunJob has, err := db.GetEngine(ctx).Where("id=?", id).Get(&job) if err != nil { return nil, err } else if !has { - return nil, ErrRunJobNotExist{ - ID: id, - } + return nil, fmt.Errorf("run job with id %d: %w", id, util.ErrNotExist) } return &job, nil diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 841455260c..759f987694 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -41,10 +41,10 @@ func (runs RunList) GetRepoIDs() []int64 { return repoIDs } -func (runs RunList) LoadTriggerUser() error { +func (runs RunList) LoadTriggerUser(ctx context.Context) error { userIDs := runs.GetUserIDs() users := make(map[int64]*user_model.User, len(userIDs)) - if err := db.GetEngine(db.DefaultContext).In("id", userIDs).Find(&users); err != nil { + if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil { return err } for _, run := range runs { diff --git a/models/actions/runner.go b/models/actions/runner.go index b45fbe7dcb..cd955f1b5b 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -13,26 +13,12 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" runnerv1 "code.gitea.io/actions-proto-go/runner/v1" "xorm.io/builder" ) -// ErrRunnerNotExist represents an error for bot runner not exist -type ErrRunnerNotExist struct { - ID int64 - UUID string - Token string -} - -func (err ErrRunnerNotExist) Error() string { - if err.UUID != "" { - return fmt.Sprintf("Bot runner ID [%s] is not exist", err.UUID) - } - - return fmt.Sprintf("Bot runner token [%s] is not exist", err.Token) -} - // ActionRunner represents runner machines type ActionRunner struct { ID int64 @@ -193,16 +179,16 @@ func (opts FindRunnerOptions) toOrder() string { return "last_online DESC" } -func CountRunners(opts FindRunnerOptions) (int64, error) { - return db.GetEngine(db.DefaultContext). +func CountRunners(ctx context.Context, opts FindRunnerOptions) (int64, error) { + return db.GetEngine(ctx). Table(ActionRunner{}). Where(opts.toCond()). OrderBy(opts.toOrder()). Count() } -func FindRunners(opts FindRunnerOptions) (runners RunnerList, err error) { - sess := db.GetEngine(db.DefaultContext). +func FindRunners(ctx context.Context, opts FindRunnerOptions) (runners RunnerList, err error) { + sess := db.GetEngine(ctx). Where(opts.toCond()). OrderBy(opts.toOrder()) if opts.Page > 0 { @@ -211,47 +197,27 @@ func FindRunners(opts FindRunnerOptions) (runners RunnerList, err error) { return runners, sess.Find(&runners) } -// GetUsableRunner returns the usable runner -func GetUsableRunner(opts FindRunnerOptions) (*ActionRunner, error) { - var runner ActionRunner - has, err := db.GetEngine(db.DefaultContext). - Where(opts.toCond()). - Asc("last_online"). - Get(&runner) - if err != nil { - return nil, err - } - if !has { - return nil, ErrRunnerNotExist{} - } - - return &runner, nil -} - // GetRunnerByUUID returns a bot runner via uuid -func GetRunnerByUUID(uuid string) (*ActionRunner, error) { +func GetRunnerByUUID(ctx context.Context, uuid string) (*ActionRunner, error) { var runner ActionRunner - has, err := db.GetEngine(db.DefaultContext).Where("uuid=?", uuid).Get(&runner) + has, err := db.GetEngine(ctx).Where("uuid=?", uuid).Get(&runner) if err != nil { return nil, err } else if !has { - return nil, ErrRunnerNotExist{ - UUID: uuid, - } + return nil, fmt.Errorf("runner with uuid %s: %w", uuid, util.ErrNotExist) } return &runner, nil } // GetRunnerByID returns a bot runner via id -func GetRunnerByID(id int64) (*ActionRunner, error) { +func GetRunnerByID(ctx context.Context, id int64) (*ActionRunner, error) { var runner ActionRunner - has, err := db.GetEngine(db.DefaultContext).Where("id=?", id).Get(&runner) + has, err := db.GetEngine(ctx).Where("id=?", id).Get(&runner) if err != nil { return nil, err } else if !has { - return nil, ErrRunnerNotExist{ - ID: id, - } + return nil, fmt.Errorf("runner with id %d: %w", id, util.ErrNotExist) + } return &runner, nil } @@ -275,20 +241,8 @@ func DeleteRunner(ctx context.Context, r *ActionRunner) error { return err } -// FindRunnersByRepoID returns all workers for the repository -func FindRunnersByRepoID(repoID int64) ([]*ActionRunner, error) { - var runners []*ActionRunner - err := db.GetEngine(db.DefaultContext).Where("repo_id=? OR repo_id=0", repoID). - Find(&runners) - if err != nil { - return nil, err - } - err = db.GetEngine(db.DefaultContext).Join("INNER", "repository", "repository.owner_id = bot_runner.owner_id").Find(&runners) - return runners, err -} - -// NewRunner creates new runner. -func NewRunner(ctx context.Context, t *ActionRunner) error { +// CreateRunner creates new runner. +func CreateRunner(ctx context.Context, t *ActionRunner) error { _, err := db.GetEngine(ctx).Insert(t) return err } diff --git a/models/actions/runner_token.go b/models/actions/runner_token.go index a71bbb6251..72e42b2d7b 100644 --- a/models/actions/runner_token.go +++ b/models/actions/runner_token.go @@ -14,15 +14,6 @@ import ( "code.gitea.io/gitea/modules/util" ) -// ErrRunnerNotExist represents an error for bot runner not exist -type ErrRunnerTokenNotExist struct { - Token string -} - -func (err ErrRunnerTokenNotExist) Error() string { - return fmt.Sprintf("runner token [%s] is not exist", err.Token) -} - // ActionRunnerToken represents runner tokens type ActionRunnerToken struct { ID int64 @@ -43,15 +34,13 @@ func init() { } // GetRunnerToken returns a bot runner via token -func GetRunnerToken(token string) (*ActionRunnerToken, error) { +func GetRunnerToken(ctx context.Context, token string) (*ActionRunnerToken, error) { var runnerToken ActionRunnerToken - has, err := db.GetEngine(db.DefaultContext).Where("token=?", token).Get(&runnerToken) + has, err := db.GetEngine(ctx).Where("token=?", token).Get(&runnerToken) if err != nil { return nil, err } else if !has { - return nil, ErrRunnerTokenNotExist{ - Token: token, - } + return nil, fmt.Errorf("runner token %q: %w", token, util.ErrNotExist) } return &runnerToken, nil } @@ -69,7 +58,7 @@ func UpdateRunnerToken(ctx context.Context, r *ActionRunnerToken, cols ...string } // NewRunnerToken creates a new runner token -func NewRunnerToken(ownerID, repoID int64) (*ActionRunnerToken, error) { +func NewRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) { token, err := util.CryptoRandomString(40) if err != nil { return nil, err @@ -80,18 +69,18 @@ func NewRunnerToken(ownerID, repoID int64) (*ActionRunnerToken, error) { IsActive: false, Token: token, } - _, err = db.GetEngine(db.DefaultContext).Insert(runnerToken) + _, err = db.GetEngine(ctx).Insert(runnerToken) return runnerToken, err } // GetUnactivatedRunnerToken returns a unactivated runner token -func GetUnactivatedRunnerToken(ownerID, repoID int64) (*ActionRunnerToken, error) { +func GetUnactivatedRunnerToken(ctx context.Context, ownerID, repoID int64) (*ActionRunnerToken, error) { var runnerToken ActionRunnerToken - has, err := db.GetEngine(db.DefaultContext).Where("owner_id=? AND repo_id=? AND is_active=?", ownerID, repoID, false).OrderBy("id DESC").Get(&runnerToken) + has, err := db.GetEngine(ctx).Where("owner_id=? AND repo_id=? AND is_active=?", ownerID, repoID, false).OrderBy("id DESC").Get(&runnerToken) if err != nil { return nil, err } else if !has { - return nil, ErrRunnerTokenNotExist{} + return nil, fmt.Errorf("runner token: %w", util.ErrNotExist) } return &runnerToken, nil } diff --git a/models/actions/task.go b/models/actions/task.go index c577938a3f..f4e49642cc 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -4,13 +4,9 @@ package actions import ( - "bytes" "context" "crypto/subtle" - "encoding/binary" - "errors" "fmt" - "io" "strconv" "time" @@ -188,31 +184,6 @@ func (task *ActionTask) GenerateToken() (err error) { return err } -type LogIndexes []int64 - -func (indexes *LogIndexes) FromDB(b []byte) error { - reader := bytes.NewReader(b) - for { - v, err := binary.ReadVarint(reader) - if err != nil { - if errors.Is(err, io.EOF) { - return nil - } - return fmt.Errorf("binary ReadVarint: %w", err) - } - *indexes = append(*indexes, v) - } -} - -func (indexes *LogIndexes) ToDB() ([]byte, error) { - buf, i := make([]byte, binary.MaxVarintLen64*len(*indexes)), 0 - for _, v := range *indexes { - n := binary.PutVarint(buf[i:], v) - i += n - } - return buf[:i], nil -} - func GetTaskByID(ctx context.Context, id int64) (*ActionTask, error) { var task ActionTask has, err := db.GetEngine(ctx).Where("id=?", id).Get(&task) @@ -247,7 +218,7 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro TokenLastEight: lastEight, } // Re-get the task from the db in case it has been deleted in the intervening period - has, err := db.GetEngine(db.DefaultContext).ID(id).Get(task) + has, err := db.GetEngine(ctx).ID(id).Get(task) if err != nil { return nil, err } @@ -406,13 +377,13 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error { return err } -func UpdateTaskByState(state *runnerv1.TaskState) (*ActionTask, error) { +func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionTask, error) { stepStates := map[int64]*runnerv1.StepState{} for _, v := range state.Steps { stepStates[v.Id] = v } - ctx, commiter, err := db.TxContext(db.DefaultContext) + ctx, commiter, err := db.TxContext(ctx) if err != nil { return nil, err } diff --git a/models/actions/utils.go b/models/actions/utils.go index 0d0df292ca..c89628a6c8 100644 --- a/models/actions/utils.go +++ b/models/actions/utils.go @@ -4,7 +4,12 @@ package actions import ( + "bytes" + "encoding/binary" "encoding/hex" + "errors" + "fmt" + "io" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/util" @@ -23,3 +28,28 @@ func generateSaltedToken() (string, string, string, string, error) { hash := auth_model.HashToken(token, salt) return token, salt, hash, token[:8], nil } + +type LogIndexes []int64 + +func (indexes *LogIndexes) FromDB(b []byte) error { + reader := bytes.NewReader(b) + for { + v, err := binary.ReadVarint(reader) + if err != nil { + if errors.Is(err, io.EOF) { + return nil + } + return fmt.Errorf("binary ReadVarint: %w", err) + } + *indexes = append(*indexes, v) + } +} + +func (indexes *LogIndexes) ToDB() ([]byte, error) { + buf, i := make([]byte, binary.MaxVarintLen64*len(*indexes)), 0 + for _, v := range *indexes { + n := binary.PutVarint(buf[i:], v) + i += n + } + return buf[:i], nil +} diff --git a/models/actions/task_test.go b/models/actions/utils_test.go similarity index 100% rename from models/actions/task_test.go rename to models/actions/utils_test.go diff --git a/modules/notification/actions/helper.go b/modules/notification/actions/helper.go index c3127ff3b5..84bf960ff1 100644 --- a/modules/notification/actions/helper.go +++ b/modules/notification/actions/helper.go @@ -154,7 +154,7 @@ func notify(ctx context.Context, input *notifyInput) error { log.Error("jobparser.Parse: %v", err) continue } - if err := actions_model.InsertRun(&run, jobs); err != nil { + if err := actions_model.InsertRun(ctx, &run, jobs); err != nil { log.Error("InsertRun: %v", err) continue } diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go index 579341b0ef..fbe102fba1 100644 --- a/routers/api/actions/runner/runner.go +++ b/routers/api/actions/runner/runner.go @@ -42,7 +42,7 @@ func (s *Service) Register( return nil, errors.New("missing runner token, name") } - runnerToken, err := actions_model.GetRunnerToken(req.Msg.Token) + runnerToken, err := actions_model.GetRunnerToken(ctx, req.Msg.Token) if err != nil { return nil, errors.New("runner token not found") } @@ -65,7 +65,7 @@ func (s *Service) Register( } // create new runner - if err := actions_model.NewRunner(ctx, runner); err != nil { + if err := actions_model.CreateRunner(ctx, runner); err != nil { return nil, errors.New("can't create new runner") } @@ -146,7 +146,7 @@ func (s *Service) UpdateTask( }), nil } - task, err = actions_model.UpdateTaskByState(req.Msg.State) + task, err = actions_model.UpdateTaskByState(ctx, req.Msg.State) if err != nil { return nil, status.Errorf(codes.Internal, "update task: %v", err) } diff --git a/routers/api/actions/runner/unary.go b/routers/api/actions/runner/unary.go index 9571beb7c7..f1d74141dc 100644 --- a/routers/api/actions/runner/unary.go +++ b/routers/api/actions/runner/unary.go @@ -6,12 +6,14 @@ package runner import ( "context" "crypto/subtle" + "errors" "strings" actions_model "code.gitea.io/gitea/models/actions" auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "github.com/bufbuild/connect-go" "google.golang.org/grpc/codes" @@ -31,9 +33,9 @@ var WithRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar } uuid := request.Header().Get(uuidHeaderKey) token := request.Header().Get(tokenHeaderKey) - runner, err := actions_model.GetRunnerByUUID(uuid) + runner, err := actions_model.GetRunnerByUUID(ctx, uuid) if err != nil { - if _, ok := err.(actions_model.ErrRunnerNotExist); ok { + if errors.Is(err, util.ErrNotExist) { return nil, status.Error(codes.Unauthenticated, "unregistered runner") } return nil, status.Error(codes.Internal, err.Error()) diff --git a/routers/common/runners.go b/routers/common/runners.go index 7010d3f16a..0aab67b8c3 100644 --- a/routers/common/runners.go +++ b/routers/common/runners.go @@ -13,19 +13,20 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/forms" ) // RunnersList render common runners list page func RunnersList(ctx *context.Context, tplName base.TplName, opts actions_model.FindRunnerOptions) { - count, err := actions_model.CountRunners(opts) + count, err := actions_model.CountRunners(ctx, opts) if err != nil { ctx.ServerError("AdminRunners", err) return } - runners, err := actions_model.FindRunners(opts) + runners, err := actions_model.FindRunners(ctx, opts) if err != nil { ctx.ServerError("AdminRunners", err) return @@ -37,9 +38,9 @@ func RunnersList(ctx *context.Context, tplName base.TplName, opts actions_model. // ownid=0,repo_id=0,means this token is used for global var token *actions_model.ActionRunnerToken - token, err = actions_model.GetUnactivatedRunnerToken(opts.OwnerID, opts.RepoID) - if _, ok := err.(actions_model.ErrRunnerTokenNotExist); ok { - token, err = actions_model.NewRunnerToken(opts.OwnerID, opts.RepoID) + token, err = actions_model.GetUnactivatedRunnerToken(ctx, opts.OwnerID, opts.RepoID) + if errors.Is(err, util.ErrNotExist) { + token, err = actions_model.NewRunnerToken(ctx, opts.OwnerID, opts.RepoID) if err != nil { ctx.ServerError("CreateRunnerToken", err) return @@ -64,7 +65,7 @@ func RunnersList(ctx *context.Context, tplName base.TplName, opts actions_model. // RunnerDetails render runner details page func RunnerDetails(ctx *context.Context, tplName base.TplName, page int, runnerID, ownerID, repoID int64) { - runner, err := actions_model.GetRunnerByID(runnerID) + runner, err := actions_model.GetRunnerByID(ctx, runnerID) if err != nil { ctx.ServerError("GetRunnerByID", err) return @@ -116,7 +117,7 @@ func RunnerDetails(ctx *context.Context, tplName base.TplName, page int, runnerI // RunnerDetailsEditPost response for edit runner details func RunnerDetailsEditPost(ctx *context.Context, runnerID, ownerID, repoID int64, redirectTo string) { - runner, err := actions_model.GetRunnerByID(runnerID) + runner, err := actions_model.GetRunnerByID(ctx, runnerID) if err != nil { log.Warn("RunnerDetailsEditPost.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL) ctx.ServerError("RunnerDetailsEditPost.GetRunnerByID", err) @@ -148,7 +149,7 @@ func RunnerDetailsEditPost(ctx *context.Context, runnerID, ownerID, repoID int64 // RunnerResetRegistrationToken reset registration token func RunnerResetRegistrationToken(ctx *context.Context, ownerID, repoID int64, redirectTo string) { - _, err := actions_model.NewRunnerToken(ownerID, repoID) + _, err := actions_model.NewRunnerToken(ctx, ownerID, repoID) if err != nil { ctx.ServerError("ResetRunnerRegistrationToken", err) return @@ -162,7 +163,7 @@ func RunnerResetRegistrationToken(ctx *context.Context, ownerID, repoID int64, r func RunnerDeletePost(ctx *context.Context, runnerID int64, successRedirectTo, failedRedirectTo string, ) { - runner, err := actions_model.GetRunnerByID(runnerID) + runner, err := actions_model.GetRunnerByID(ctx, runnerID) if err != nil { log.Warn("DeleteRunnerPost.GetRunnerByID failed: %v, url: %s", err, ctx.Req.URL) ctx.ServerError("DeleteRunnerPost.GetRunnerByID", err) diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 3a8c777c24..ab8e693efc 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -118,7 +118,7 @@ func List(ctx *context.Context) { run.Repo = ctx.Repo.Repository } - if err := runs.LoadTriggerUser(); err != nil { + if err := runs.LoadTriggerUser(ctx); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return } diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index dbabf6e4b1..756409f52f 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -5,6 +5,7 @@ package actions import ( "context" + "errors" "fmt" "net/http" "time" @@ -14,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/actions" context_module "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" actions_service "code.gitea.io/gitea/services/actions" @@ -271,7 +273,7 @@ func Cancel(ctx *context_module.Context) { func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions_model.ActionRunJob, []*actions_model.ActionRunJob) { run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) if err != nil { - if _, ok := err.(actions_model.ErrRunNotExist); ok { + if errors.Is(err, util.ErrNotExist) { ctx.Error(http.StatusNotFound, err.Error()) return nil, nil }