{{template "repo/header" .}} {{$showDiffBox := false}} -
+

{{if and $.PageIsComparePull $.IsSigned (not .Repository.IsArchived)}} {{ctx.Locale.Tr "repo.pulls.compare_changes"}} @@ -235,7 +235,7 @@

{{if $showDiffBox}} -
+
{{template "repo/commits_table" .}} {{template "repo/diff/box" .}}
From 1075ff74b5050f671c5f9824ae39390230b3c85d Mon Sep 17 00:00:00 2001 From: Earl Warren <109468362+earl-warren@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:34:25 +0100 Subject: [PATCH 12/19] Use restricted sanitizer for repository description (#28141) - Currently the repository description uses the same sanitizer as a normal markdown document. This means that element such as heading and images are allowed and can be abused. - Create a minimal restricted sanitizer for the repository description, which only allows what the postprocessor currently allows, which are links and emojis. - Added unit testing. - Resolves https://codeberg.org/forgejo/forgejo/issues/1202 - Resolves https://codeberg.org/Codeberg/Community/issues/1122 (cherry picked from commit 631c87cc2347f0036a75dcd21e24429bbca28207) Co-authored-by: Gusted --- models/repo/repo.go | 4 ++-- modules/markup/sanitizer.go | 35 +++++++++++++++++++++++++++++--- modules/markup/sanitizer_test.go | 22 ++++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index c4b215e074..db3709f1e8 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -584,9 +584,9 @@ func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML { }, repo.Description) if err != nil { log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err) - return template.HTML(markup.Sanitize(repo.Description)) + return template.HTML(markup.SanitizeDescription(repo.Description)) } - return template.HTML(markup.Sanitize(desc)) + return template.HTML(markup.SanitizeDescription(desc)) } // CloneLink represents different types of clone URLs of repository. diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go index 48c08831f1..992e85b989 100644 --- a/modules/markup/sanitizer.go +++ b/modules/markup/sanitizer.go @@ -18,9 +18,10 @@ import ( // Sanitizer is a protection wrapper of *bluemonday.Policy which does not allow // any modification to the underlying policies once it's been created. type Sanitizer struct { - defaultPolicy *bluemonday.Policy - rendererPolicies map[string]*bluemonday.Policy - init sync.Once + defaultPolicy *bluemonday.Policy + descriptionPolicy *bluemonday.Policy + rendererPolicies map[string]*bluemonday.Policy + init sync.Once } var ( @@ -41,6 +42,7 @@ func NewSanitizer() { func InitializeSanitizer() { sanitizer.rendererPolicies = map[string]*bluemonday.Policy{} sanitizer.defaultPolicy = createDefaultPolicy() + sanitizer.descriptionPolicy = createRepoDescriptionPolicy() for name, renderer := range renderers { sanitizerRules := renderer.SanitizerRules() @@ -161,6 +163,27 @@ func createDefaultPolicy() *bluemonday.Policy { return policy } +// createRepoDescriptionPolicy returns a minimal more strict policy that is used for +// repository descriptions. +func createRepoDescriptionPolicy() *bluemonday.Policy { + policy := bluemonday.NewPolicy() + + // Allow italics and bold. + policy.AllowElements("i", "b", "em", "strong") + + // Allow code. + policy.AllowElements("code") + + // Allow links + policy.AllowAttrs("href", "target", "rel").OnElements("a") + + // Allow classes for emojis + policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img", "span") + policy.AllowAttrs("aria-label").OnElements("span") + + return policy +} + func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) { for _, rule := range rules { if rule.AllowDataURIImages { @@ -176,6 +199,12 @@ func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitize } } +// SanitizeDescription sanitizes the HTML generated for a repository description. +func SanitizeDescription(s string) string { + NewSanitizer() + return sanitizer.descriptionPolicy.Sanitize(s) +} + // Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist. func Sanitize(s string) string { NewSanitizer() diff --git a/modules/markup/sanitizer_test.go b/modules/markup/sanitizer_test.go index 0bc63ff0a7..b7b8792bd7 100644 --- a/modules/markup/sanitizer_test.go +++ b/modules/markup/sanitizer_test.go @@ -73,6 +73,28 @@ func Test_Sanitizer(t *testing.T) { } } +func TestDescriptionSanitizer(t *testing.T) { + NewSanitizer() + + testCases := []string{ + `

Title

`, `Title`, + `image`, ``, + `THUMBS UP`, `THUMBS UP`, + `Hello World`, `Hello World`, + `
`, ``, + `https://example.com`, `https://example.com`, + `Important!`, `Important!`, + `
Click me! Nothing to see here.
`, `Click me! Nothing to see here.`, + ``, ``, + `I have a strong opinion about this.`, `I have a strong opinion about this.`, + `Provides alternative wg(8) tool`, `Provides alternative wg(8) tool`, + } + + for i := 0; i < len(testCases); i += 2 { + assert.Equal(t, testCases[i+1], SanitizeDescription(testCases[i])) + } +} + func TestSanitizeNonEscape(t *testing.T) { descStr := "<script>alert(document.domain)</script>" From d24a8223ce1e47a0c9b103aae07f67c3112ca048 Mon Sep 17 00:00:00 2001 From: NintenHero <37460517+MichaelHinrichs@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:58:12 -0400 Subject: [PATCH 13/19] Edit Discord Badge (#28188) Use white Discord icon. Label the badge as "Discord". Use the official Discord logo's color value of 5865F2. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aac793efe8..dc396465a7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - + From df1e7d0067bb39913eb681ccc920649884fb1938 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 24 Nov 2023 11:49:41 +0800 Subject: [PATCH 14/19] Use db.Find instead of writing methods for every object (#28084) For those simple objects, it's unnecessary to write the find and count methods again and again. --- cmd/admin_auth.go | 3 +- models/actions/artifact.go | 50 ++--- models/actions/run.go | 4 +- models/actions/run_job_list.go | 16 +- models/actions/run_list.go | 16 +- models/actions/runner.go | 23 +- models/actions/schedule_list.go | 16 +- models/actions/schedule_spec_list.go | 19 +- models/actions/task_list.go | 17 +- models/actions/variable.go | 11 +- models/activities/notification.go | 207 +++++++++--------- models/activities/notification_test.go | 23 +- models/activities/statistic.go | 4 +- models/asymkey/ssh_key.go | 63 +++--- models/asymkey/ssh_key_commit_verification.go | 5 +- models/asymkey/ssh_key_deploy.go | 22 +- models/auth/access_token.go | 37 +--- models/auth/access_token_test.go | 6 +- models/auth/oauth2.go | 28 +-- models/auth/oauth2_list.go | 32 +++ models/auth/source.go | 20 +- models/db/context.go | 5 + models/db/list.go | 80 +++++-- models/db/list_test.go | 13 +- models/issues/comment.go | 2 +- models/organization/org.go | 21 +- models/organization/org_test.go | 8 +- models/project/project.go | 25 +-- models/project/project_test.go | 6 +- models/secret/secret.go | 18 +- models/user/external_login_user.go | 36 +-- models/webhook/webhook.go | 23 +- models/webhook/webhook_test.go | 8 +- routers/api/actions/artifacts.go | 8 +- routers/api/actions/artifacts_chunks.go | 6 +- routers/api/actions/runner/utils.go | 11 +- routers/api/v1/notify/notifications.go | 13 +- routers/api/v1/notify/repo.go | 9 +- routers/api/v1/notify/user.go | 9 +- routers/api/v1/org/action.go | 9 +- routers/api/v1/org/org.go | 9 +- routers/api/v1/repo/hook.go | 9 +- routers/api/v1/repo/key.go | 10 +- routers/api/v1/user/app.go | 15 +- routers/api/v1/user/key.go | 26 ++- routers/api/v1/utils/hook.go | 9 +- routers/web/admin/applications.go | 5 +- routers/web/admin/auths.go | 6 +- routers/web/admin/users.go | 8 +- routers/web/org/projects.go | 10 +- routers/web/org/setting.go | 2 +- routers/web/org/setting_oauth2.go | 5 +- routers/web/repo/actions/actions.go | 9 +- routers/web/repo/actions/view.go | 7 +- routers/web/repo/issue.go | 40 ++-- routers/web/repo/projects.go | 8 +- routers/web/repo/setting/deploy_key.go | 4 +- routers/web/repo/setting/webhook.go | 3 +- routers/web/shared/actions/runners.go | 18 +- routers/web/shared/actions/variables.go | 2 +- routers/web/shared/secrets/secrets.go | 3 +- routers/web/shared/user/header.go | 4 +- routers/web/user/home.go | 4 +- routers/web/user/notification.go | 35 ++- routers/web/user/setting/applications.go | 7 +- routers/web/user/setting/keys.go | 5 +- routers/web/user/setting/profile.go | 8 +- routers/web/user/setting/security/security.go | 10 +- routers/web/user/setting/webhooks.go | 3 +- services/actions/clear_tasks.go | 4 +- services/actions/job_emitter.go | 2 +- services/actions/notifier_helper.go | 7 +- services/asymkey/ssh_key_test.go | 5 +- services/auth/httpsign.go | 5 +- services/auth/signin.go | 2 +- services/auth/source/oauth2/init.go | 3 +- services/auth/source/oauth2/providers.go | 3 +- services/auth/sspi.go | 3 +- services/auth/sync.go | 2 +- services/migrations/update.go | 13 +- services/pull/review.go | 6 +- services/repository/delete.go | 6 +- services/repository/hooks.go | 2 +- services/secrets/secrets.go | 6 +- services/user/user.go | 2 +- services/webhook/webhook.go | 5 +- tests/integration/auth_ldap_test.go | 2 +- tests/integration/org_count_test.go | 2 +- 88 files changed, 611 insertions(+), 685 deletions(-) create mode 100644 models/auth/oauth2_list.go diff --git a/cmd/admin_auth.go b/cmd/admin_auth.go index 014ddf329f..ec92e342d4 100644 --- a/cmd/admin_auth.go +++ b/cmd/admin_auth.go @@ -9,6 +9,7 @@ import ( "text/tabwriter" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" auth_service "code.gitea.io/gitea/services/auth" "github.com/urfave/cli/v2" @@ -62,7 +63,7 @@ func runListAuth(c *cli.Context) error { return err } - authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{}) + authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{}) if err != nil { return err } diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 849a90fd10..42bd9c23cb 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -14,6 +14,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) // ArtifactStatus is the status of an artifact, uploading, expired or need-delete @@ -108,29 +110,37 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro return err } -// ListArtifactsByRunID returns all artifacts of a run -func ListArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=?", runID).Find(&arts) +type FindArtifactsOptions struct { + db.ListOptions + RepoID int64 + RunID int64 + ArtifactName string + Status int } -// ListArtifactsByRunIDAndArtifactName returns an artifacts of a run by artifact name -func ListArtifactsByRunIDAndArtifactName(ctx context.Context, runID int64, artifactName string) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, artifactName).Find(&arts) -} +func (opts FindArtifactsOptions) ToConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + if opts.RunID > 0 { + cond = cond.And(builder.Eq{"run_id": opts.RunID}) + } + if opts.ArtifactName != "" { + cond = cond.And(builder.Eq{"artifact_name": opts.ArtifactName}) + } + if opts.Status > 0 { + cond = cond.And(builder.Eq{"status": opts.Status}) + } -// ListUploadedArtifactsByRunID returns all uploaded artifacts of a run -func ListUploadedArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=? AND status=?", runID, ArtifactStatusUploadConfirmed).Find(&arts) + return cond } // ActionArtifactMeta is the meta data of an artifact type ActionArtifactMeta struct { ArtifactName string FileSize int64 - Status int64 + Status ArtifactStatus } // ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run @@ -143,18 +153,6 @@ func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtif Find(&arts) } -// ListArtifactsByRepoID returns all artifacts of a repo -func ListArtifactsByRepoID(ctx context.Context, repoID int64) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("repo_id=?", repoID).Find(&arts) -} - -// ListArtifactsByRunIDAndName returns artifacts by name of a run -func ListArtifactsByRunIDAndName(ctx context.Context, runID int64, name string) ([]*ActionArtifact, error) { - arts := make([]*ActionArtifact, 0, 10) - return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, name).Find(&arts) -} - // ListNeedExpiredArtifacts returns all need expired artifacts but not deleted func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { arts := make([]*ActionArtifact, 0, 10) diff --git a/models/actions/run.go b/models/actions/run.go index 8078613fb8..4656aa22a2 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -170,7 +170,7 @@ func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) err // CancelRunningJobs cancels all running and waiting jobs associated with a specific workflow. func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string) error { // Find all runs in the specified repository, reference, and workflow with statuses 'Running' or 'Waiting'. - runs, total, err := FindRuns(ctx, FindRunOptions{ + runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{ RepoID: repoID, Ref: ref, WorkflowID: workflowID, @@ -188,7 +188,7 @@ func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string // Iterate over each found run and cancel its associated jobs. for _, run := range runs { // Find all jobs associated with the current run. - jobs, _, err := FindRunJobs(ctx, FindRunJobOptions{ + jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{ RunID: run.ID, }) if err != nil { diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go index a166396694..6ea6cb9d3b 100644 --- a/models/actions/run_job_list.go +++ b/models/actions/run_job_list.go @@ -61,7 +61,7 @@ type FindRunJobOptions struct { UpdatedBefore timeutil.TimeStamp } -func (opts FindRunJobOptions) toConds() builder.Cond { +func (opts FindRunJobOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RunID > 0 { cond = cond.And(builder.Eq{"run_id": opts.RunID}) @@ -83,17 +83,3 @@ func (opts FindRunJobOptions) toConds() builder.Cond { } return cond } - -func FindRunJobs(ctx context.Context, opts FindRunJobOptions) (ActionJobList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var tasks ActionJobList - total, err := e.FindAndCount(&tasks) - return tasks, total, err -} - -func CountRunJobs(ctx context.Context, opts FindRunJobOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionRunJob)) -} diff --git a/models/actions/run_list.go b/models/actions/run_list.go index cd053ea7b5..375c46221b 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -75,7 +75,7 @@ type FindRunOptions struct { Status []Status } -func (opts FindRunOptions) toConds() builder.Cond { +func (opts FindRunOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -101,18 +101,8 @@ func (opts FindRunOptions) toConds() builder.Cond { return cond } -func FindRuns(ctx context.Context, opts FindRunOptions) (RunList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var runs RunList - total, err := e.Desc("id").FindAndCount(&runs) - return runs, total, err -} - -func CountRuns(ctx context.Context, opts FindRunOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionRun)) +func (opts FindRunOptions) ToOrders() string { + return "`id` DESC" } type StatusInfo struct { diff --git a/models/actions/runner.go b/models/actions/runner.go index 2c092c2b4a..717b770800 100644 --- a/models/actions/runner.go +++ b/models/actions/runner.go @@ -156,7 +156,7 @@ type FindRunnerOptions struct { WithAvailable bool // not only runners belong to, but also runners can be used } -func (opts FindRunnerOptions) toCond() builder.Cond { +func (opts FindRunnerOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { @@ -181,7 +181,7 @@ func (opts FindRunnerOptions) toCond() builder.Cond { return cond } -func (opts FindRunnerOptions) toOrder() string { +func (opts FindRunnerOptions) ToOrders() string { switch opts.Sort { case "online": return "last_online DESC" @@ -199,22 +199,6 @@ func (opts FindRunnerOptions) toOrder() string { return "last_online DESC" } -func CountRunners(ctx context.Context, opts FindRunnerOptions) (int64, error) { - return db.GetEngine(ctx). - Where(opts.toCond()). - Count(ActionRunner{}) -} - -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 { - sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - return runners, sess.Find(&runners) -} - // GetRunnerByUUID returns a runner via uuid func GetRunnerByUUID(ctx context.Context, uuid string) (*ActionRunner, error) { var runner ActionRunner @@ -263,8 +247,7 @@ func DeleteRunner(ctx context.Context, id int64) error { // CreateRunner creates new runner. func CreateRunner(ctx context.Context, t *ActionRunner) error { - _, err := db.GetEngine(ctx).Insert(t) - return err + return db.Insert(ctx, t) } func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go index ddd9a1321e..b806550b87 100644 --- a/models/actions/schedule_list.go +++ b/models/actions/schedule_list.go @@ -67,7 +67,7 @@ type FindScheduleOptions struct { OwnerID int64 } -func (opts FindScheduleOptions) toConds() builder.Cond { +func (opts FindScheduleOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -79,16 +79,6 @@ func (opts FindScheduleOptions) toConds() builder.Cond { return cond } -func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if !opts.ListAll && opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var schedules ScheduleList - total, err := e.Desc("id").FindAndCount(&schedules) - return schedules, total, err -} - -func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule)) +func (opts FindScheduleOptions) ToOrders() string { + return "`id` DESC" } diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go index 6bf91cf819..e9ae268a6e 100644 --- a/models/actions/schedule_spec_list.go +++ b/models/actions/schedule_spec_list.go @@ -71,7 +71,7 @@ type FindSpecOptions struct { Next int64 } -func (opts FindSpecOptions) toConds() builder.Cond { +func (opts FindSpecOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -84,23 +84,18 @@ func (opts FindSpecOptions) toConds() builder.Cond { return cond } +func (opts FindSpecOptions) ToOrders() string { + return "`id` DESC" +} + func FindSpecs(ctx context.Context, opts FindSpecOptions) (SpecList, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } - var specs SpecList - total, err := e.Desc("id").FindAndCount(&specs) + specs, total, err := db.FindAndCount[ActionScheduleSpec](ctx, opts) if err != nil { return nil, 0, err } - if err := specs.LoadSchedules(ctx); err != nil { + if err := SpecList(specs).LoadSchedules(ctx); err != nil { return nil, 0, err } return specs, total, nil } - -func CountSpecs(ctx context.Context, opts FindSpecOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionScheduleSpec)) -} diff --git a/models/actions/task_list.go b/models/actions/task_list.go index 1f6b16772b..b07d00b8db 100644 --- a/models/actions/task_list.go +++ b/models/actions/task_list.go @@ -62,7 +62,7 @@ type FindTaskOptions struct { IDOrderDesc bool } -func (opts FindTaskOptions) toConds() builder.Cond { +func (opts FindTaskOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -88,18 +88,9 @@ func (opts FindTaskOptions) toConds() builder.Cond { return cond } -func FindTasks(ctx context.Context, opts FindTaskOptions) (TaskList, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.PageSize > 0 && opts.Page >= 1 { - e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) - } +func (opts FindTaskOptions) ToOrders() string { if opts.IDOrderDesc { - e.OrderBy("id DESC") + return "`id` DESC" } - var tasks TaskList - return tasks, e.Find(&tasks) -} - -func CountTasks(ctx context.Context, opts FindTaskOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionTask)) + return "" } diff --git a/models/actions/variable.go b/models/actions/variable.go index e0bb59ccbe..030b7bae92 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -56,7 +56,7 @@ type FindVariablesOpts struct { RepoID int64 } -func (opts *FindVariablesOpts) toConds() builder.Cond { +func (opts FindVariablesOpts) ToConds() builder.Cond { cond := builder.NewCond() if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) @@ -67,15 +67,6 @@ func (opts *FindVariablesOpts) toConds() builder.Cond { return cond } -func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariable, error) { - var variables []*ActionVariable - sess := db.GetEngine(ctx) - if opts.PageSize != 0 { - sess = db.SetSessionPagination(sess, &opts.ListOptions) - } - return variables, sess.Where(opts.toConds()).Find(&variables) -} - func GetVariableByID(ctx context.Context, variableID int64) (*ActionVariable, error) { var variable ActionVariable has, err := db.GetEngine(ctx).Where("id=?", variableID).Get(&variable) diff --git a/models/activities/notification.go b/models/activities/notification.go index 7c794564b6..230bcdd6e8 100644 --- a/models/activities/notification.go +++ b/models/activities/notification.go @@ -22,7 +22,6 @@ import ( "code.gitea.io/gitea/modules/timeutil" "xorm.io/builder" - "xorm.io/xorm" ) type ( @@ -93,7 +92,7 @@ type FindNotificationOptions struct { } // ToCond will convert each condition into a xorm-Cond -func (opts *FindNotificationOptions) ToCond() builder.Cond { +func (opts FindNotificationOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.UserID != 0 { cond = cond.And(builder.Eq{"notification.user_id": opts.UserID}) @@ -105,7 +104,11 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond { cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID}) } if len(opts.Status) > 0 { - cond = cond.And(builder.In("notification.status", opts.Status)) + if len(opts.Status) == 1 { + cond = cond.And(builder.Eq{"notification.status": opts.Status[0]}) + } else { + cond = cond.And(builder.In("notification.status", opts.Status)) + } } if len(opts.Source) > 0 { cond = cond.And(builder.In("notification.source", opts.Source)) @@ -119,24 +122,8 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond { return cond } -// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required -func (opts *FindNotificationOptions) ToSession(ctx context.Context) *xorm.Session { - sess := db.GetEngine(ctx).Where(opts.ToCond()) - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, opts) - } - return sess -} - -// GetNotifications returns all notifications that fit to the given options. -func GetNotifications(ctx context.Context, options *FindNotificationOptions) (nl NotificationList, err error) { - err = options.ToSession(ctx).OrderBy("notification.updated_unix DESC").Find(&nl) - return nl, err -} - -// CountNotifications count all notifications that fit to the given options and ignore pagination. -func CountNotifications(ctx context.Context, opts *FindNotificationOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.ToCond()).Count(&Notification{}) +func (opts FindNotificationOptions) ToOrders() string { + return "notification.updated_unix DESC" } // CreateRepoTransferNotification creates notification for the user a repository was transferred to @@ -192,7 +179,9 @@ func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error { // init var toNotify container.Set[int64] - notifications, err := getNotificationsByIssueID(ctx, issueID) + notifications, err := db.Find[Notification](ctx, FindNotificationOptions{ + IssueID: issueID, + }) if err != nil { return err } @@ -273,23 +262,6 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n return nil } -func getNotificationsByIssueID(ctx context.Context, issueID int64) (notifications []*Notification, err error) { - err = db.GetEngine(ctx). - Where("issue_id = ?", issueID). - Find(¬ifications) - return notifications, err -} - -func notificationExists(notifications []*Notification, issueID, userID int64) bool { - for _, notification := range notifications { - if notification.IssueID == issueID && notification.UserID == userID { - return true - } - } - - return false -} - func createIssueNotification(ctx context.Context, userID int64, issue *issues_model.Issue, commentID, updatedByID int64) error { notification := &Notification{ UserID: userID, @@ -341,35 +313,6 @@ func GetIssueNotification(ctx context.Context, userID, issueID int64) (*Notifica return notification, err } -// NotificationsForUser returns notifications for a given user and status -func NotificationsForUser(ctx context.Context, user *user_model.User, statuses []NotificationStatus, page, perPage int) (notifications NotificationList, err error) { - if len(statuses) == 0 { - return nil, nil - } - - sess := db.GetEngine(ctx). - Where("user_id = ?", user.ID). - In("status", statuses). - OrderBy("updated_unix DESC") - - if page > 0 && perPage > 0 { - sess.Limit(perPage, (page-1)*perPage) - } - - err = sess.Find(¬ifications) - return notifications, err -} - -// CountUnread count unread notifications for a user -func CountUnread(ctx context.Context, userID int64) int64 { - exist, err := db.GetEngine(ctx).Where("user_id = ?", userID).And("status = ?", NotificationStatusUnread).Count(new(Notification)) - if err != nil { - log.Error("countUnread", err) - return 0 - } - return exist -} - // LoadAttributes load Repo Issue User and Comment if not loaded func (n *Notification) LoadAttributes(ctx context.Context) (err error) { if err = n.loadRepo(ctx); err != nil { @@ -481,17 +424,47 @@ func (n *Notification) APIURL() string { return setting.AppURL + "api/v1/notifications/threads/" + strconv.FormatInt(n.ID, 10) } +func notificationExists(notifications []*Notification, issueID, userID int64) bool { + for _, notification := range notifications { + if notification.IssueID == issueID && notification.UserID == userID { + return true + } + } + + return false +} + +// UserIDCount is a simple coalition of UserID and Count +type UserIDCount struct { + UserID int64 + Count int64 +} + +// GetUIDsAndNotificationCounts between the two provided times +func GetUIDsAndNotificationCounts(ctx context.Context, since, until timeutil.TimeStamp) ([]UserIDCount, error) { + sql := `SELECT user_id, count(*) AS count FROM notification ` + + `WHERE user_id IN (SELECT user_id FROM notification WHERE updated_unix >= ? AND ` + + `updated_unix < ?) AND status = ? GROUP BY user_id` + var res []UserIDCount + return res, db.GetEngine(ctx).SQL(sql, since, until, NotificationStatusUnread).Find(&res) +} + // NotificationList contains a list of notifications type NotificationList []*Notification // LoadAttributes load Repo Issue User and Comment if not loaded func (nl NotificationList) LoadAttributes(ctx context.Context) error { - var err error - for i := 0; i < len(nl); i++ { - err = nl[i].LoadAttributes(ctx) - if err != nil && !issues_model.IsErrCommentNotExist(err) { - return err - } + if _, _, err := nl.LoadRepos(ctx); err != nil { + return err + } + if _, err := nl.LoadIssues(ctx); err != nil { + return err + } + if _, err := nl.LoadUsers(ctx); err != nil { + return err + } + if _, err := nl.LoadComments(ctx); err != nil { + return err } return nil } @@ -665,6 +638,68 @@ func (nl NotificationList) getPendingCommentIDs() []int64 { return ids.Values() } +func (nl NotificationList) getUserIDs() []int64 { + ids := make(container.Set[int64], len(nl)) + for _, notification := range nl { + if notification.UserID == 0 || notification.User != nil { + continue + } + ids.Add(notification.UserID) + } + return ids.Values() +} + +// LoadUsers loads users from database +func (nl NotificationList) LoadUsers(ctx context.Context) ([]int, error) { + if len(nl) == 0 { + return []int{}, nil + } + + userIDs := nl.getUserIDs() + users := make(map[int64]*user_model.User, len(userIDs)) + left := len(userIDs) + for left > 0 { + limit := db.DefaultMaxInSize + if left < limit { + limit = left + } + rows, err := db.GetEngine(ctx). + In("id", userIDs[:limit]). + Rows(new(user_model.User)) + if err != nil { + return nil, err + } + + for rows.Next() { + var user user_model.User + err = rows.Scan(&user) + if err != nil { + rows.Close() + return nil, err + } + + users[user.ID] = &user + } + _ = rows.Close() + + left -= limit + userIDs = userIDs[limit:] + } + + failures := []int{} + for i, notification := range nl { + if notification.UserID > 0 && notification.User == nil && users[notification.UserID] != nil { + notification.User = users[notification.UserID] + if notification.User == nil { + log.Error("Notification[%d]: UserID[%d] failed to load", notification.ID, notification.UserID) + failures = append(failures, i) + continue + } + } + } + return failures, nil +} + // LoadComments loads comments from database func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) { if len(nl) == 0 { @@ -717,30 +752,6 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) { return failures, nil } -// GetNotificationCount returns the notification count for user -func GetNotificationCount(ctx context.Context, user *user_model.User, status NotificationStatus) (count int64, err error) { - count, err = db.GetEngine(ctx). - Where("user_id = ?", user.ID). - And("status = ?", status). - Count(&Notification{}) - return count, err -} - -// UserIDCount is a simple coalition of UserID and Count -type UserIDCount struct { - UserID int64 - Count int64 -} - -// GetUIDsAndNotificationCounts between the two provided times -func GetUIDsAndNotificationCounts(ctx context.Context, since, until timeutil.TimeStamp) ([]UserIDCount, error) { - sql := `SELECT user_id, count(*) AS count FROM notification ` + - `WHERE user_id IN (SELECT user_id FROM notification WHERE updated_unix >= ? AND ` + - `updated_unix < ?) AND status = ? GROUP BY user_id` - var res []UserIDCount - return res, db.GetEngine(ctx).SQL(sql, since, until, NotificationStatusUnread).Find(&res) -} - // SetIssueReadBy sets issue to be read by given user. func SetIssueReadBy(ctx context.Context, issueID, userID int64) error { if err := issues_model.UpdateIssueUserByRead(ctx, userID, issueID); err != nil { diff --git a/models/activities/notification_test.go b/models/activities/notification_test.go index b90ce70536..52f0eacba1 100644 --- a/models/activities/notification_test.go +++ b/models/activities/notification_test.go @@ -34,8 +34,13 @@ func TestCreateOrUpdateIssueNotifications(t *testing.T) { func TestNotificationsForUser(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - statuses := []activities_model.NotificationStatus{activities_model.NotificationStatusRead, activities_model.NotificationStatusUnread} - notfs, err := activities_model.NotificationsForUser(db.DefaultContext, user, statuses, 1, 10) + notfs, err := db.Find[activities_model.Notification](db.DefaultContext, activities_model.FindNotificationOptions{ + UserID: user.ID, + Status: []activities_model.NotificationStatus{ + activities_model.NotificationStatusRead, + activities_model.NotificationStatusUnread, + }, + }) assert.NoError(t, err) if assert.Len(t, notfs, 3) { assert.EqualValues(t, 5, notfs[0].ID) @@ -68,11 +73,21 @@ func TestNotification_GetIssue(t *testing.T) { func TestGetNotificationCount(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - cnt, err := activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusRead) + cnt, err := db.Count[activities_model.Notification](db.DefaultContext, activities_model.FindNotificationOptions{ + UserID: user.ID, + Status: []activities_model.NotificationStatus{ + activities_model.NotificationStatusRead, + }, + }) assert.NoError(t, err) assert.EqualValues(t, 0, cnt) - cnt, err = activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusUnread) + cnt, err = db.Count[activities_model.Notification](db.DefaultContext, activities_model.FindNotificationOptions{ + UserID: user.ID, + Status: []activities_model.NotificationStatus{ + activities_model.NotificationStatusUnread, + }, + }) assert.NoError(t, err) assert.EqualValues(t, 1, cnt) } diff --git a/models/activities/statistic.go b/models/activities/statistic.go index e9dab6fc10..fe5f7d0872 100644 --- a/models/activities/statistic.go +++ b/models/activities/statistic.go @@ -52,7 +52,7 @@ type IssueByRepositoryCount struct { func GetStatistic(ctx context.Context) (stats Statistic) { e := db.GetEngine(ctx) stats.Counter.User = user_model.CountUsers(ctx, nil) - stats.Counter.Org, _ = organization.CountOrgs(ctx, organization.FindOrgOptions{IncludePrivate: true}) + stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludePrivate: true}) stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey)) stats.Counter.Repo, _ = repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{}) stats.Counter.Watch, _ = e.Count(new(repo_model.Watch)) @@ -102,7 +102,7 @@ func GetStatistic(ctx context.Context) (stats Statistic) { stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) stats.Counter.Release, _ = e.Count(new(repo_model.Release)) - stats.Counter.AuthSource = auth.CountSources(ctx, auth.FindSourcesOptions{}) + stats.Counter.AuthSource, _ = db.Count[auth.Source](ctx, auth.FindSourcesOptions{}) stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone)) stats.Counter.Label, _ = e.Count(new(issues_model.Label)) diff --git a/models/asymkey/ssh_key.go b/models/asymkey/ssh_key.go index f36738fb3d..552f2ffd69 100644 --- a/models/asymkey/ssh_key.go +++ b/models/asymkey/ssh_key.go @@ -179,45 +179,33 @@ func SearchPublicKeyByContentExact(ctx context.Context, content string) (*Public return key, nil } -// SearchPublicKey returns a list of public keys matching the provided arguments. -func SearchPublicKey(ctx context.Context, uid int64, fingerprint string) ([]*PublicKey, error) { - keys := make([]*PublicKey, 0, 5) +type FindPublicKeyOptions struct { + db.ListOptions + OwnerID int64 + Fingerprint string + KeyTypes []KeyType + NotKeytype KeyType + LoginSourceID int64 +} + +func (opts FindPublicKeyOptions) ToConds() builder.Cond { cond := builder.NewCond() - if uid != 0 { - cond = cond.And(builder.Eq{"owner_id": uid}) + if opts.OwnerID > 0 { + cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) } - if fingerprint != "" { - cond = cond.And(builder.Eq{"fingerprint": fingerprint}) + if opts.Fingerprint != "" { + cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint}) } - return keys, db.GetEngine(ctx).Where(cond).Find(&keys) -} - -// ListPublicKeys returns a list of public keys belongs to given user. -func ListPublicKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*PublicKey, error) { - sess := db.GetEngine(ctx).Where("owner_id = ? AND type != ?", uid, KeyTypePrincipal) - if listOptions.Page != 0 { - sess = db.SetSessionPagination(sess, &listOptions) - - keys := make([]*PublicKey, 0, listOptions.PageSize) - return keys, sess.Find(&keys) + if len(opts.KeyTypes) > 0 { + cond = cond.And(builder.In("type", opts.KeyTypes)) } - - keys := make([]*PublicKey, 0, 5) - return keys, sess.Find(&keys) -} - -// CountPublicKeys count public keys a user has -func CountPublicKeys(ctx context.Context, userID int64) (int64, error) { - sess := db.GetEngine(ctx).Where("owner_id = ? AND type != ?", userID, KeyTypePrincipal) - return sess.Count(&PublicKey{}) -} - -// ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source. -func ListPublicKeysBySource(ctx context.Context, uid, authSourceID int64) ([]*PublicKey, error) { - keys := make([]*PublicKey, 0, 5) - return keys, db.GetEngine(ctx). - Where("owner_id = ? AND login_source_id = ?", uid, authSourceID). - Find(&keys) + if opts.NotKeytype > 0 { + cond = cond.And(builder.Neq{"type": opts.NotKeytype}) + } + if opts.LoginSourceID > 0 { + cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID}) + } + return cond } // UpdatePublicKeyUpdated updates public key use time. @@ -394,7 +382,10 @@ func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.So // Get Public Keys from DB with current LDAP source var giteaKeys []string - keys, err := ListPublicKeysBySource(ctx, usr.ID, s.ID) + keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{ + OwnerID: usr.ID, + LoginSourceID: s.ID, + }) if err != nil { log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err) } diff --git a/models/asymkey/ssh_key_commit_verification.go b/models/asymkey/ssh_key_commit_verification.go index a61f0663b1..27c6df3578 100644 --- a/models/asymkey/ssh_key_commit_verification.go +++ b/models/asymkey/ssh_key_commit_verification.go @@ -21,7 +21,10 @@ import ( func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification { // Now try to associate the signature with the committer, if present if committer.ID != 0 { - keys, err := ListPublicKeys(ctx, committer.ID, db.ListOptions{}) + keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{ + OwnerID: committer.ID, + NotKeytype: KeyTypePrincipal, + }) if err != nil { // Skipping failed to get ssh keys of user log.Error("ListPublicKeys: %v", err) return &CommitVerification{ diff --git a/models/asymkey/ssh_key_deploy.go b/models/asymkey/ssh_key_deploy.go index e347604bcd..8f9f454051 100644 --- a/models/asymkey/ssh_key_deploy.go +++ b/models/asymkey/ssh_key_deploy.go @@ -210,7 +210,7 @@ type ListDeployKeysOptions struct { Fingerprint string } -func (opt ListDeployKeysOptions) toCond() builder.Cond { +func (opt ListDeployKeysOptions) ToConds() builder.Cond { cond := builder.NewCond() if opt.RepoID != 0 { cond = cond.And(builder.Eq{"repo_id": opt.RepoID}) @@ -223,23 +223,3 @@ func (opt ListDeployKeysOptions) toCond() builder.Cond { } return cond } - -// ListDeployKeys returns a list of deploy keys matching the provided arguments. -func ListDeployKeys(ctx context.Context, opts *ListDeployKeysOptions) ([]*DeployKey, error) { - sess := db.GetEngine(ctx).Where(opts.toCond()) - - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, opts) - - keys := make([]*DeployKey, 0, opts.PageSize) - return keys, sess.Find(&keys) - } - - keys := make([]*DeployKey, 0, 5) - return keys, sess.Find(&keys) -} - -// CountDeployKeys returns count deploy keys matching the provided arguments. -func CountDeployKeys(ctx context.Context, opts *ListDeployKeysOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toCond()).Count(&DeployKey{}) -} diff --git a/models/auth/access_token.go b/models/auth/access_token.go index 8abcc622bc..63331b4841 100644 --- a/models/auth/access_token.go +++ b/models/auth/access_token.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/util" lru "github.com/hashicorp/golang-lru/v2" + "xorm.io/builder" ) // ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error. @@ -201,25 +202,18 @@ type ListAccessTokensOptions struct { UserID int64 } -// ListAccessTokens returns a list of access tokens belongs to given user. -func ListAccessTokens(ctx context.Context, opts ListAccessTokensOptions) ([]*AccessToken, error) { - sess := db.GetEngine(ctx).Where("uid=?", opts.UserID) - - if len(opts.Name) != 0 { - sess = sess.Where("name=?", opts.Name) +func (opts ListAccessTokensOptions) ToConds() builder.Cond { + cond := builder.NewCond() + // user id is required, otherwise it will return all result which maybe a possible bug + cond = cond.And(builder.Eq{"uid": opts.UserID}) + if len(opts.Name) > 0 { + cond = cond.And(builder.Eq{"name": opts.Name}) } + return cond +} - sess = sess.Desc("created_unix") - - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, &opts) - - tokens := make([]*AccessToken, 0, opts.PageSize) - return tokens, sess.Find(&tokens) - } - - tokens := make([]*AccessToken, 0, 5) - return tokens, sess.Find(&tokens) +func (opts ListAccessTokensOptions) ToOrders() string { + return "created_unix DESC" } // UpdateAccessToken updates information of access token. @@ -228,15 +222,6 @@ func UpdateAccessToken(ctx context.Context, t *AccessToken) error { return err } -// CountAccessTokens count access tokens belongs to given user by options -func CountAccessTokens(ctx context.Context, opts ListAccessTokensOptions) (int64, error) { - sess := db.GetEngine(ctx).Where("uid=?", opts.UserID) - if len(opts.Name) != 0 { - sess = sess.Where("name=?", opts.Name) - } - return sess.Count(&AccessToken{}) -} - // DeleteAccessTokenByID deletes access token by given ID. func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error { cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{ diff --git a/models/auth/access_token_test.go b/models/auth/access_token_test.go index 72c937ffd6..4360f1a214 100644 --- a/models/auth/access_token_test.go +++ b/models/auth/access_token_test.go @@ -85,7 +85,7 @@ func TestGetAccessTokenBySHA(t *testing.T) { func TestListAccessTokens(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - tokens, err := auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 1}) + tokens, err := db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 1}) assert.NoError(t, err) if assert.Len(t, tokens, 2) { assert.Equal(t, int64(1), tokens[0].UID) @@ -94,14 +94,14 @@ func TestListAccessTokens(t *testing.T) { assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B") } - tokens, err = auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 2}) + tokens, err = db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 2}) assert.NoError(t, err) if assert.Len(t, tokens, 1) { assert.Equal(t, int64(2), tokens[0].UID) assert.Equal(t, "Token A", tokens[0].Name) } - tokens, err = auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 100}) + tokens, err = db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 100}) assert.NoError(t, err) assert.Empty(t, tokens) } diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 76a4e9d835..9d53fffc78 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -5,6 +5,7 @@ package auth import ( "context" + "crypto/sha256" "encoding/base32" "encoding/base64" "fmt" @@ -19,7 +20,6 @@ import ( "code.gitea.io/gitea/modules/util" uuid "github.com/google/uuid" - "github.com/minio/sha256-simd" "golang.org/x/crypto/bcrypt" "xorm.io/builder" "xorm.io/xorm" @@ -243,13 +243,6 @@ func GetOAuth2ApplicationByID(ctx context.Context, id int64) (app *OAuth2Applica return app, nil } -// GetOAuth2ApplicationsByUserID returns all oauth2 applications owned by the user -func GetOAuth2ApplicationsByUserID(ctx context.Context, userID int64) (apps []*OAuth2Application, err error) { - apps = make([]*OAuth2Application, 0) - err = db.GetEngine(ctx).Where("uid = ?", userID).Find(&apps) - return apps, err -} - // CreateOAuth2ApplicationOptions holds options to create an oauth2 application type CreateOAuth2ApplicationOptions struct { Name string @@ -372,25 +365,6 @@ func DeleteOAuth2Application(ctx context.Context, id, userid int64) error { return committer.Commit() } -// ListOAuth2Applications returns a list of oauth2 applications belongs to given user. -func ListOAuth2Applications(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*OAuth2Application, int64, error) { - sess := db.GetEngine(ctx). - Where("uid=?", uid). - Desc("id") - - if listOptions.Page != 0 { - sess = db.SetSessionPagination(sess, &listOptions) - - apps := make([]*OAuth2Application, 0, listOptions.PageSize) - total, err := sess.FindAndCount(&apps) - return apps, total, err - } - - apps := make([]*OAuth2Application, 0, 5) - total, err := sess.FindAndCount(&apps) - return apps, total, err -} - ////////////////////////////////////////////////////// // OAuth2AuthorizationCode is a code to obtain an access token in combination with the client secret once. It has a limited lifetime. diff --git a/models/auth/oauth2_list.go b/models/auth/oauth2_list.go new file mode 100644 index 0000000000..c55f10b3c8 --- /dev/null +++ b/models/auth/oauth2_list.go @@ -0,0 +1,32 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package auth + +import ( + "code.gitea.io/gitea/models/db" + + "xorm.io/builder" +) + +type FindOAuth2ApplicationsOptions struct { + db.ListOptions + // OwnerID is the user id or org id of the owner of the application + OwnerID int64 + // find global applications, if true, then OwnerID will be igonred + IsGlobal bool +} + +func (opts FindOAuth2ApplicationsOptions) ToConds() builder.Cond { + conds := builder.NewCond() + if opts.IsGlobal { + conds = conds.And(builder.Eq{"uid": 0}) + } else if opts.OwnerID != 0 { + conds = conds.And(builder.Eq{"uid": opts.OwnerID}) + } + return conds +} + +func (opts FindOAuth2ApplicationsOptions) ToOrders() string { + return "id DESC" +} diff --git a/models/auth/source.go b/models/auth/source.go index 5f2781c808..5e77afddc3 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -242,6 +242,7 @@ func CreateSource(ctx context.Context, source *Source) error { } type FindSourcesOptions struct { + db.ListOptions IsActive util.OptionalBool LoginType Type } @@ -257,27 +258,22 @@ func (opts FindSourcesOptions) ToConds() builder.Cond { return conds } -// FindSources returns a slice of login sources found in DB according to given conditions. -func FindSources(ctx context.Context, opts FindSourcesOptions) ([]*Source, error) { - auths := make([]*Source, 0, 6) - return auths, db.GetEngine(ctx).Where(opts.ToConds()).Find(&auths) -} - // IsSSPIEnabled returns true if there is at least one activated login // source of type LoginSSPI func IsSSPIEnabled(ctx context.Context) bool { if !db.HasEngine { return false } - sources, err := FindSources(ctx, FindSourcesOptions{ + + exist, err := db.Exists[Source](ctx, FindSourcesOptions{ IsActive: util.OptionalBoolTrue, LoginType: SSPI, }) if err != nil { - log.Error("ActiveSources: %v", err) + log.Error("Active SSPI Sources: %v", err) return false } - return len(sources) > 0 + return exist } // GetSourceByID returns login source by given ID. @@ -346,12 +342,6 @@ func UpdateSource(ctx context.Context, source *Source) error { return err } -// CountSources returns number of login sources. -func CountSources(ctx context.Context, opts FindSourcesOptions) int64 { - count, _ := db.GetEngine(ctx).Where(opts.ToConds()).Count(new(Source)) - return count -} - // ErrSourceNotExist represents a "SourceNotExist" kind of error. type ErrSourceNotExist struct { ID int64 diff --git a/models/db/context.go b/models/db/context.go index 521857fae8..45765ef7d3 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -264,3 +264,8 @@ func inTransaction(ctx context.Context) (*xorm.Session, bool) { return nil, false } } + +func Exists[T any](ctx context.Context, opts FindOptions) (bool, error) { + var bean T + return GetEngine(ctx).Where(opts.ToConds()).Exist(&bean) +} diff --git a/models/db/list.go b/models/db/list.go index 9fb4d0741f..b2f932e89b 100644 --- a/models/db/list.go +++ b/models/db/list.go @@ -14,7 +14,8 @@ import ( const ( // DefaultMaxInSize represents default variables number on IN () in SQL - DefaultMaxInSize = 50 + DefaultMaxInSize = 50 + defaultFindSliceSize = 10 ) // Paginator is the base for different ListOptions types @@ -52,7 +53,12 @@ type ListOptions struct { ListAll bool // if true, then PageSize and Page will not be taken } -var _ Paginator = &ListOptions{} +var ListOptionsAll = ListOptions{ListAll: true} + +var ( + _ Paginator = &ListOptions{} + _ FindOptions = ListOptions{} +) // GetSkipTake returns the skip and take values func (opts *ListOptions) GetSkipTake() (skip, take int) { @@ -67,8 +73,16 @@ func (opts *ListOptions) GetStartEnd() (start, end int) { return start, end } +func (opts ListOptions) GetPage() int { + return opts.Page +} + +func (opts ListOptions) GetPageSize() int { + return opts.PageSize +} + // IsListAll indicates PageSize and Page will be ignored -func (opts *ListOptions) IsListAll() bool { +func (opts ListOptions) IsListAll() bool { return opts.ListAll } @@ -85,6 +99,10 @@ func (opts *ListOptions) SetDefaultValues() { } } +func (opts ListOptions) ToConds() builder.Cond { + return builder.NewCond() +} + // AbsoluteListOptions absolute options to paginate results type AbsoluteListOptions struct { skip int @@ -124,29 +142,63 @@ func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) { // FindOptions represents a find options type FindOptions interface { - Paginator + GetPage() int + GetPageSize() int + IsListAll() bool ToConds() builder.Cond } +type FindOptionsOrder interface { + ToOrders() string +} + // Find represents a common find function which accept an options interface -func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error { +func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) { sess := GetEngine(ctx).Where(opts.ToConds()) - if !opts.IsListAll() { - sess.Limit(opts.GetSkipTake()) + page, pageSize := opts.GetPage(), opts.GetPageSize() + if !opts.IsListAll() && pageSize > 0 && page >= 1 { + sess.Limit(pageSize, (page-1)*pageSize) } - return sess.Find(objects) + if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" { + sess.OrderBy(newOpt.ToOrders()) + } + + findPageSize := defaultFindSliceSize + if pageSize > 0 { + findPageSize = pageSize + } + objects := make([]*T, 0, findPageSize) + if err := sess.Find(&objects); err != nil { + return nil, err + } + return objects, nil } // Count represents a common count function which accept an options interface -func Count[T any](ctx context.Context, opts FindOptions, object T) (int64, error) { - return GetEngine(ctx).Where(opts.ToConds()).Count(object) +func Count[T any](ctx context.Context, opts FindOptions) (int64, error) { + var object T + return GetEngine(ctx).Where(opts.ToConds()).Count(&object) } // FindAndCount represents a common findandcount function which accept an options interface -func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (int64, error) { +func FindAndCount[T any](ctx context.Context, opts FindOptions) ([]*T, int64, error) { sess := GetEngine(ctx).Where(opts.ToConds()) - if !opts.IsListAll() { - sess.Limit(opts.GetSkipTake()) + page, pageSize := opts.GetPage(), opts.GetPageSize() + if !opts.IsListAll() && pageSize > 0 && page >= 1 { + sess.Limit(pageSize, (page-1)*pageSize) } - return sess.FindAndCount(objects) + if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" { + sess.OrderBy(newOpt.ToOrders()) + } + + findPageSize := defaultFindSliceSize + if pageSize > 0 { + findPageSize = pageSize + } + objects := make([]*T, 0, findPageSize) + cnt, err := sess.FindAndCount(&objects) + if err != nil { + return nil, 0, err + } + return objects, cnt, nil } diff --git a/models/db/list_test.go b/models/db/list_test.go index b923dc9c22..45194611f8 100644 --- a/models/db/list_test.go +++ b/models/db/list_test.go @@ -18,11 +18,11 @@ type mockListOptions struct { db.ListOptions } -func (opts *mockListOptions) IsListAll() bool { +func (opts mockListOptions) IsListAll() bool { return true } -func (opts *mockListOptions) ToConds() builder.Cond { +func (opts mockListOptions) ToConds() builder.Cond { return builder.NewCond() } @@ -37,17 +37,16 @@ func TestFind(t *testing.T) { assert.NotEmpty(t, repoUnitCount) opts := mockListOptions{} - var repoUnits []repo_model.RepoUnit - err = db.Find(db.DefaultContext, &opts, &repoUnits) + repoUnits, err := db.Find[repo_model.RepoUnit](db.DefaultContext, opts) assert.NoError(t, err) assert.Len(t, repoUnits, repoUnitCount) - cnt, err := db.Count(db.DefaultContext, &opts, new(repo_model.RepoUnit)) + cnt, err := db.Count[repo_model.RepoUnit](db.DefaultContext, opts) assert.NoError(t, err) assert.EqualValues(t, repoUnitCount, cnt) - repoUnits = make([]repo_model.RepoUnit, 0, 10) - newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits) + repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts) assert.NoError(t, err) assert.EqualValues(t, cnt, newCnt) + assert.Len(t, repoUnits, repoUnitCount) } diff --git a/models/issues/comment.go b/models/issues/comment.go index 7fd07867df..a59fa570af 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -1027,7 +1027,7 @@ type FindCommentsOptions struct { } // ToConds implements FindOptions interface -func (opts *FindCommentsOptions) ToConds() builder.Cond { +func (opts FindCommentsOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID}) diff --git a/models/organization/org.go b/models/organization/org.go index 07091194eb..23a4e2f96a 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -456,7 +456,7 @@ func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder { return builder.Select("org_id").From("org_user").Where(cond) } -func (opts FindOrgOptions) toConds() builder.Cond { +func (opts FindOrgOptions) ToConds() builder.Cond { var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization} if opts.UserID > 0 { cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate))) @@ -467,23 +467,8 @@ func (opts FindOrgOptions) toConds() builder.Cond { return cond } -// FindOrgs returns a list of organizations according given conditions -func FindOrgs(ctx context.Context, opts FindOrgOptions) ([]*Organization, error) { - orgs := make([]*Organization, 0, 10) - sess := db.GetEngine(ctx). - Where(opts.toConds()). - Asc("`user`.name") - if opts.Page > 0 && opts.PageSize > 0 { - sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1)) - } - return orgs, sess.Find(&orgs) -} - -// CountOrgs returns total count organizations according options -func CountOrgs(ctx context.Context, opts FindOrgOptions) (int64, error) { - return db.GetEngine(ctx). - Where(opts.toConds()). - Count(new(Organization)) +func (opts FindOrgOptions) ToOrders() string { + return "`user`.name ASC" } // HasOrgOrUserVisible tells if the given user can see the given org or user diff --git a/models/organization/org_test.go b/models/organization/org_test.go index aa72fc467e..5e40dd4190 100644 --- a/models/organization/org_test.go +++ b/models/organization/org_test.go @@ -131,7 +131,7 @@ func TestCountOrganizations(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{}) assert.NoError(t, err) - cnt, err := organization.CountOrgs(db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true}) + cnt, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{IncludePrivate: true}) assert.NoError(t, err) assert.Equal(t, expected, cnt) } @@ -183,7 +183,7 @@ func TestIsPublicMembership(t *testing.T) { func TestFindOrgs(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - orgs, err := organization.FindOrgs(db.DefaultContext, organization.FindOrgOptions{ + orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: 4, IncludePrivate: true, }) @@ -192,14 +192,14 @@ func TestFindOrgs(t *testing.T) { assert.EqualValues(t, 3, orgs[0].ID) } - orgs, err = organization.FindOrgs(db.DefaultContext, organization.FindOrgOptions{ + orgs, err = db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: 4, IncludePrivate: false, }) assert.NoError(t, err) assert.Len(t, orgs, 0) - total, err := organization.CountOrgs(db.DefaultContext, organization.FindOrgOptions{ + total, err := db.Count[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: 4, IncludePrivate: true, }) diff --git a/models/project/project.go b/models/project/project.go index 3a1bfe1dbd..becfcbea1e 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -192,16 +192,16 @@ func IsTypeValid(p Type) bool { // SearchOptions are options for GetProjects type SearchOptions struct { + db.ListOptions OwnerID int64 RepoID int64 - Page int IsClosed util.OptionalBool OrderBy db.SearchOrderBy Type Type Title string } -func (opts *SearchOptions) toConds() builder.Cond { +func (opts SearchOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID > 0 { cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) @@ -226,9 +226,8 @@ func (opts *SearchOptions) toConds() builder.Cond { return cond } -// CountProjects counts projects -func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Project)) +func (opts SearchOptions) ToOrders() string { + return opts.OrderBy.String() } func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { @@ -244,22 +243,6 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { } } -// FindProjects returns a list of all projects that have been created in the repository -func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { - e := db.GetEngine(ctx).Where(opts.toConds()) - if opts.OrderBy.String() != "" { - e = e.OrderBy(opts.OrderBy.String()) - } - projects := make([]*Project, 0, setting.UI.IssuePagingNum) - - if opts.Page > 0 { - e = e.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum) - } - - count, err := e.FindAndCount(&projects) - return projects, count, err -} - // NewProject creates a new Project func NewProject(ctx context.Context, p *Project) error { if !IsBoardTypeValid(p.BoardType) { diff --git a/models/project/project_test.go b/models/project/project_test.go index 6b5bd5b371..7a37c1faf2 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -34,13 +34,13 @@ func TestIsProjectTypeValid(t *testing.T) { func TestGetProjects(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - projects, _, err := FindProjects(db.DefaultContext, SearchOptions{RepoID: 1}) + projects, err := db.Find[Project](db.DefaultContext, SearchOptions{RepoID: 1}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures assert.Len(t, projects, 1) - projects, _, err = FindProjects(db.DefaultContext, SearchOptions{RepoID: 3}) + projects, err = db.Find[Project](db.DefaultContext, SearchOptions{RepoID: 3}) assert.NoError(t, err) // 1 value for this repo exists in the fixtures @@ -109,7 +109,7 @@ func TestProjectsSort(t *testing.T) { } for _, tt := range tests { - projects, count, err := FindProjects(db.DefaultContext, SearchOptions{ + projects, count, err := db.FindAndCount[Project](db.DefaultContext, SearchOptions{ OrderBy: GetSearchOrderByBySortType(tt.sortType), }) assert.NoError(t, err) diff --git a/models/secret/secret.go b/models/secret/secret.go index 8df46b6c38..41e860d7f6 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -78,7 +78,7 @@ type FindSecretsOptions struct { Name string } -func (opts *FindSecretsOptions) toConds() builder.Cond { +func (opts FindSecretsOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.OwnerID > 0 { cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) @@ -96,22 +96,6 @@ func (opts *FindSecretsOptions) toConds() builder.Cond { return cond } -func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error) { - var secrets []*Secret - sess := db.GetEngine(ctx) - if opts.PageSize != 0 { - sess = db.SetSessionPagination(sess, &opts.ListOptions) - } - return secrets, sess. - Where(opts.toConds()). - Find(&secrets) -} - -// CountSecrets counts the secrets -func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret)) -} - // UpdateSecret changes org or user reop secret. func UpdateSecret(ctx context.Context, secretID int64, data string) error { encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) diff --git a/models/user/external_login_user.go b/models/user/external_login_user.go index 4121c5d89f..0db702f225 100644 --- a/models/user/external_login_user.go +++ b/models/user/external_login_user.go @@ -96,19 +96,6 @@ func GetExternalLogin(ctx context.Context, externalLoginUser *ExternalLoginUser) return db.GetEngine(ctx).Get(externalLoginUser) } -// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource -func ListAccountLinks(ctx context.Context, user *User) ([]*ExternalLoginUser, error) { - externalAccounts := make([]*ExternalLoginUser, 0, 5) - err := db.GetEngine(ctx).Where("user_id=?", user.ID). - Desc("login_source_id"). - Find(&externalAccounts) - if err != nil { - return nil, err - } - - return externalAccounts, nil -} - // LinkExternalToUser link the external user to the user func LinkExternalToUser(ctx context.Context, user *User, externalLoginUser *ExternalLoginUser) error { has, err := db.GetEngine(ctx).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID). @@ -173,28 +160,23 @@ func UpdateExternalUserByExternalID(ctx context.Context, external *ExternalLogin // FindExternalUserOptions represents an options to find external users type FindExternalUserOptions struct { + db.ListOptions Provider string - Limit int - Start int + UserID int64 + OrderBy string } -func (opts FindExternalUserOptions) toConds() builder.Cond { +func (opts FindExternalUserOptions) ToConds() builder.Cond { cond := builder.NewCond() if len(opts.Provider) > 0 { cond = cond.And(builder.Eq{"provider": opts.Provider}) } + if opts.UserID > 0 { + cond = cond.And(builder.Eq{"user_id": opts.UserID}) + } return cond } -// FindExternalUsersByProvider represents external users via provider -func FindExternalUsersByProvider(ctx context.Context, opts FindExternalUserOptions) ([]ExternalLoginUser, error) { - var users []ExternalLoginUser - err := db.GetEngine(ctx).Where(opts.toConds()). - Limit(opts.Limit, opts.Start). - OrderBy("login_source_id ASC, external_id ASC"). - Find(&users) - if err != nil { - return nil, err - } - return users, nil +func (opts FindExternalUserOptions) ToOrders() string { + return opts.OrderBy } diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index b28cea6a9c..408023507a 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -435,7 +435,7 @@ type ListWebhookOptions struct { IsActive util.OptionalBool } -func (opts *ListWebhookOptions) toCond() builder.Cond { +func (opts ListWebhookOptions) ToConds() builder.Cond { cond := builder.NewCond() if opts.RepoID != 0 { cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID}) @@ -449,27 +449,6 @@ func (opts *ListWebhookOptions) toCond() builder.Cond { return cond } -// ListWebhooksByOpts return webhooks based on options -func ListWebhooksByOpts(ctx context.Context, opts *ListWebhookOptions) ([]*Webhook, error) { - sess := db.GetEngine(ctx).Where(opts.toCond()) - - if opts.Page != 0 { - sess = db.SetSessionPagination(sess, opts) - webhooks := make([]*Webhook, 0, opts.PageSize) - err := sess.Find(&webhooks) - return webhooks, err - } - - webhooks := make([]*Webhook, 0, 10) - err := sess.Find(&webhooks) - return webhooks, err -} - -// CountWebhooksByOpts count webhooks based on options and ignore pagination -func CountWebhooksByOpts(ctx context.Context, opts *ListWebhookOptions) (int64, error) { - return db.GetEngine(ctx).Where(opts.toCond()).Count(&Webhook{}) -} - // UpdateWebhook updates information of webhook. func UpdateWebhook(ctx context.Context, w *Webhook) error { _, err := db.GetEngine(ctx).ID(w.ID).AllCols().Update(w) diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index 6a01fdd75f..694fd7a873 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -123,7 +123,7 @@ func TestGetWebhookByOwnerID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(1), hooks[0].ID) @@ -133,7 +133,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{RepoID: 1}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1}) assert.NoError(t, err) if assert.Len(t, hooks, 2) { assert.Equal(t, int64(1), hooks[0].ID) @@ -143,7 +143,7 @@ func TestGetWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) @@ -153,7 +153,7 @@ func TestGetActiveWebhooksByOwnerID(t *testing.T) { func TestGetWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3}) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(3), hooks[0].ID) diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go index c45dc667af..5411237103 100644 --- a/routers/api/actions/artifacts.go +++ b/routers/api/actions/artifacts.go @@ -70,6 +70,7 @@ import ( "strings" "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -314,7 +315,7 @@ func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) { return } - artifacts, err := actions.ListArtifactsByRunID(ctx, runID) + artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{RunID: runID}) if err != nil { log.Error("Error getting artifacts: %v", err) ctx.Error(http.StatusInternalServerError, err.Error()) @@ -376,7 +377,10 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) { return } - artifacts, err := actions.ListArtifactsByRunIDAndArtifactName(ctx, runID, itemPath) + artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{ + RunID: runID, + ArtifactName: itemPath, + }) if err != nil { log.Error("Error getting artifacts: %v", err) ctx.Error(http.StatusInternalServerError, err.Error()) diff --git a/routers/api/actions/artifacts_chunks.go b/routers/api/actions/artifacts_chunks.go index 458d671cff..c7ab70afa9 100644 --- a/routers/api/actions/artifacts_chunks.go +++ b/routers/api/actions/artifacts_chunks.go @@ -13,6 +13,7 @@ import ( "time" "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" ) @@ -86,7 +87,10 @@ func listChunksByRunID(st storage.ObjectStorage, runID int64) (map[int64][]*chun func mergeChunksForRun(ctx *ArtifactContext, st storage.ObjectStorage, runID int64, artifactName string) error { // read all db artifacts by name - artifacts, err := actions.ListArtifactsByRunIDAndName(ctx, runID, artifactName) + artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{ + RunID: runID, + ArtifactName: artifactName, + }) if err != nil { return err } diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index 24432ab6b2..bf913f2c05 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -8,6 +8,7 @@ import ( "fmt" actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" actions_module "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/container" @@ -67,12 +68,12 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s return secrets } - ownerSecrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) + ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) if err != nil { log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) // go on } - repoSecrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID}) + repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID}) if err != nil { log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) // go on @@ -94,13 +95,13 @@ func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map variables := map[string]string{} // Org / User level - ownerVariables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID}) + ownerVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID}) if err != nil { log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err) } // Repo level - repoVariables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID}) + repoVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID}) if err != nil { log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err) } @@ -200,7 +201,7 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str } needs := container.SetOf(task.Job.Needs...) - jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID}) + jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID}) if err != nil { return nil, fmt.Errorf("FindRunJobs: %w", err) } diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go index b22ea8a771..c87da9399f 100644 --- a/routers/api/v1/notify/notifications.go +++ b/routers/api/v1/notify/notifications.go @@ -8,6 +8,7 @@ import ( "strings" activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" @@ -21,7 +22,17 @@ func NewAvailable(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/NotificationCount" - ctx.JSON(http.StatusOK, api.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)}) + + total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, + }) + if err != nil { + ctx.Error(http.StatusUnprocessableEntity, "db.Count[activities_model.Notification]", err) + return + } + + ctx.JSON(http.StatusOK, api.NotificationCount{New: total}) } func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions { diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 0e4efcc640..55ca6ad1fd 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -9,6 +9,7 @@ import ( "time" activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" @@ -108,18 +109,18 @@ func ListRepoNotifications(ctx *context.APIContext) { } opts.RepoID = ctx.Repo.Repository.ID - totalCount, err := activities_model.CountNotifications(ctx, opts) + totalCount, err := db.Count[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - err = nl.LoadAttributes(ctx) + err = activities_model.NotificationList(nl).LoadAttributes(ctx) if err != nil { ctx.InternalServerError(err) return @@ -202,7 +203,7 @@ func ReadRepoNotifications(ctx *context.APIContext) { opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) log.Error("%v", opts.Status) } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index 267b7d8ea8..4abdfb2e92 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -8,6 +8,7 @@ import ( "time" activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/convert" @@ -68,18 +69,18 @@ func ListNotifications(ctx *context.APIContext) { return } - totalCount, err := activities_model.CountNotifications(ctx, opts) + totalCount, err := db.Count[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return } - err = nl.LoadAttributes(ctx) + err = activities_model.NotificationList(nl).LoadAttributes(ctx) if err != nil { ctx.InternalServerError(err) return @@ -147,7 +148,7 @@ func ReadNotifications(ctx *context.APIContext) { statuses := ctx.FormStrings("status-types") opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) } - nl, err := activities_model.GetNotifications(ctx, opts) + nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 5af6125773..ddc74d865b 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -7,6 +7,7 @@ import ( "errors" "net/http" + "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" @@ -48,13 +49,7 @@ func ListActionsSecrets(ctx *context.APIContext) { ListOptions: utils.GetListOptions(ctx), } - count, err := secret_model.CountSecrets(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - secrets, err := secret_model.FindSecrets(ctx, *opts) + secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 6fb8ecd403..d5fac1e5b8 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -30,14 +30,9 @@ func listUserOrgs(ctx *context.APIContext, u *user_model.User) { UserID: u.ID, IncludePrivate: showPrivate, } - orgs, err := organization.FindOrgs(ctx, opts) + orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "FindOrgs", err) - return - } - maxResults, err := organization.CountOrgs(ctx, opts) - if err != nil { - ctx.Error(http.StatusInternalServerError, "CountOrgs", err) + ctx.Error(http.StatusInternalServerError, "db.FindAndCount[organization.Organization]", err) return } diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index 7d0142748a..8859e3ae23 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -7,6 +7,7 @@ package repo import ( "net/http" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" "code.gitea.io/gitea/models/webhook" @@ -58,13 +59,7 @@ func ListHooks(ctx *context.APIContext) { RepoID: ctx.Repo.Repository.ID, } - count, err := webhook.CountWebhooksByOpts(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - hooks, err := webhook.ListWebhooksByOpts(ctx, opts) + hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/repo/key.go b/routers/api/v1/repo/key.go index 47a4d14842..3dc5a60d1c 100644 --- a/routers/api/v1/repo/key.go +++ b/routers/api/v1/repo/key.go @@ -83,20 +83,14 @@ func ListDeployKeys(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - opts := &asymkey_model.ListDeployKeysOptions{ + opts := asymkey_model.ListDeployKeysOptions{ ListOptions: utils.GetListOptions(ctx), RepoID: ctx.Repo.Repository.ID, KeyID: ctx.FormInt64("key_id"), Fingerprint: ctx.FormString("fingerprint"), } - keys, err := asymkey_model.ListDeployKeys(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - count, err := asymkey_model.CountDeployKeys(ctx, opts) + keys, count, err := db.FindAndCount[asymkey_model.DeployKey](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index fef0240b0f..edbc1d17b4 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -12,6 +12,7 @@ import ( "strings" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/context" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" @@ -48,12 +49,7 @@ func ListAccessTokens(ctx *context.APIContext) { opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)} - count, err := auth_model.CountAccessTokens(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - tokens, err := auth_model.ListAccessTokens(ctx, opts) + tokens, count, err := db.FindAndCount[auth_model.AccessToken](ctx, opts) if err != nil { ctx.InternalServerError(err) return @@ -168,7 +164,7 @@ func DeleteAccessToken(ctx *context.APIContext) { tokenID, _ := strconv.ParseInt(token, 0, 64) if tokenID == 0 { - tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{ + tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{ Name: token, UserID: ctx.ContextUser.ID, }) @@ -266,7 +262,10 @@ func ListOauth2Applications(ctx *context.APIContext) { // "200": // "$ref": "#/responses/OAuth2ApplicationList" - apps, total, err := auth_model.ListOAuth2Applications(ctx, ctx.Doer.ID, utils.GetListOptions(ctx)) + apps, total, err := db.FindAndCount[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{ + ListOptions: utils.GetListOptions(ctx), + OwnerID: ctx.Doer.ID, + }) if err != nil { ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err) return diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index fd891699ce..dd185aa7d6 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -8,6 +8,7 @@ import ( "net/http" asymkey_model "code.gitea.io/gitea/models/asymkey" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" @@ -56,25 +57,26 @@ func listPublicKeys(ctx *context.APIContext, user *user_model.User) { username := ctx.Params("username") if fingerprint != "" { + var userID int64 // Unrestricted // Querying not just listing if username != "" { // Restrict to provided uid - keys, err = asymkey_model.SearchPublicKey(ctx, user.ID, fingerprint) - } else { - // Unrestricted - keys, err = asymkey_model.SearchPublicKey(ctx, 0, fingerprint) + userID = user.ID } + keys, err = db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + OwnerID: userID, + Fingerprint: fingerprint, + }) count = len(keys) } else { - total, err2 := asymkey_model.CountPublicKeys(ctx, user.ID) - if err2 != nil { - ctx.InternalServerError(err) - return - } - count = int(total) - + var total int64 // Use ListPublicKeys - keys, err = asymkey_model.ListPublicKeys(ctx, user.ID, utils.GetListOptions(ctx)) + keys, total, err = db.FindAndCount[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + ListOptions: utils.GetListOptions(ctx), + OwnerID: user.ID, + NotKeytype: asymkey_model.KeyTypePrincipal, + }) + count = int(total) } if err != nil { diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index 362f4bfc4d..1207be25ac 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/context" @@ -26,13 +27,7 @@ func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) { OwnerID: owner.ID, } - count, err := webhook.CountWebhooksByOpts(ctx, opts) - if err != nil { - ctx.InternalServerError(err) - return - } - - hooks, err := webhook.ListWebhooksByOpts(ctx, opts) + hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/web/admin/applications.go b/routers/web/admin/applications.go index b26912db48..b6f7bcd2a5 100644 --- a/routers/web/admin/applications.go +++ b/routers/web/admin/applications.go @@ -8,6 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" @@ -33,7 +34,9 @@ func Applications(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings.applications") ctx.Data["PageIsAdminApplications"] = true - apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, 0) + apps, err := db.Find[auth.OAuth2Application](ctx, auth.FindOAuth2ApplicationsOptions{ + IsGlobal: true, + }) if err != nil { ctx.ServerError("GetOAuth2ApplicationsByUserID", err) return diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index 23946d64af..2cf63c646d 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -13,6 +13,7 @@ import ( "strings" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/auth/pam" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -48,13 +49,12 @@ func Authentications(ctx *context.Context) { ctx.Data["PageIsAdminAuthentications"] = true var err error - ctx.Data["Sources"], err = auth.FindSources(ctx, auth.FindSourcesOptions{}) + ctx.Data["Sources"], ctx.Data["Total"], err = db.FindAndCount[auth.Source](ctx, auth.FindSourcesOptions{}) if err != nil { ctx.ServerError("auth.Sources", err) return } - ctx.Data["Total"] = auth.CountSources(ctx, auth.FindSourcesOptions{}) ctx.HTML(http.StatusOK, tplAuths) } @@ -284,7 +284,7 @@ func NewAuthSourcePost(ctx *context.Context) { ctx.RenderWithErr(err.Error(), tplAuthNew, form) return } - existing, err := auth.FindSources(ctx, auth.FindSourcesOptions{LoginType: auth.SSPI}) + existing, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{LoginType: auth.SSPI}) if err != nil || len(existing) > 0 { ctx.Data["Err_Type"] = true ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 58120818b0..44c4fa7512 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -93,7 +93,7 @@ func NewUser(ctx *context.Context) { ctx.Data["login_type"] = "0-0" - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, }) if err != nil { @@ -114,7 +114,7 @@ func NewUserPost(ctx *context.Context) { ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, }) if err != nil { @@ -237,7 +237,7 @@ func prepareUserInfo(ctx *context.Context) *user_model.User { ctx.Data["LoginSource"] = &auth.Source{} } - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{}) + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{}) if err != nil { ctx.ServerError("auth.Sources", err) return nil @@ -296,7 +296,7 @@ func ViewUser(ctx *context.Context) { ctx.Data["Emails"] = emails ctx.Data["EmailsTotal"] = len(emails) - orgs, err := org_model.FindOrgs(ctx, org_model.FindOrgOptions{ + orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{ ListOptions: db.ListOptions{ ListAll: true, }, diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 439fdf644b..03798a712c 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" attachment_model "code.gitea.io/gitea/models/repo" @@ -59,9 +60,12 @@ func Projects(ctx *context.Context) { } else { projectType = project_model.TypeIndividual } - projects, total, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + projects, total, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: setting.UI.IssuePagingNum, + }, OwnerID: ctx.ContextUser.ID, - Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: projectType, @@ -72,7 +76,7 @@ func Projects(ctx *context.Context) { return } - opTotal, err := project_model.CountProjects(ctx, project_model.SearchOptions{ + opTotal, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, IsClosed: util.OptionalBoolOf(!isShowClosed), Type: projectType, diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index fac83b3612..f0d9259d3f 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -215,7 +215,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks" ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc") - ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID}) + ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID}) if err != nil { ctx.ServerError("ListWebhooksByOpts", err) return diff --git a/routers/web/org/setting_oauth2.go b/routers/web/org/setting_oauth2.go index 0045bce4c9..ca4fe09f38 100644 --- a/routers/web/org/setting_oauth2.go +++ b/routers/web/org/setting_oauth2.go @@ -8,6 +8,7 @@ import ( "net/http" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" @@ -35,7 +36,9 @@ func Applications(ctx *context.Context) { ctx.Data["PageIsOrgSettings"] = true ctx.Data["PageIsSettingsApplications"] = true - apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, ctx.Org.Organization.ID) + apps, err := db.Find[auth.OAuth2Application](ctx, auth.FindOAuth2ApplicationsOptions{ + OwnerID: ctx.Org.Organization.ID, + }) if err != nil { ctx.ServerError("GetOAuth2ApplicationsByUserID", err) return diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index 6284d21463..3b10f0b957 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -75,11 +75,10 @@ func List(ctx *context.Context) { } // Get all runner labels - opts := actions_model.FindRunnerOptions{ + runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{ RepoID: ctx.Repo.Repository.ID, WithAvailable: true, - } - runners, err := actions_model.FindRunners(ctx, opts) + }) if err != nil { ctx.ServerError("FindRunners", err) return @@ -169,7 +168,7 @@ func List(ctx *context.Context) { opts.Status = []actions_model.Status{actions_model.Status(status)} } - runs, total, err := actions_model.FindRuns(ctx, opts) + runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return @@ -179,7 +178,7 @@ func List(ctx *context.Context) { run.Repo = ctx.Repo.Repository } - if err := runs.LoadTriggerUser(ctx); err != nil { + if err := actions_model.RunList(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 2c69b13616..1cdae32a32 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -512,7 +512,7 @@ func ArtifactsView(ctx *context_module.Context) { } for _, art := range artifacts { status := "completed" - if art.Status == int64(actions_model.ArtifactStatusExpired) { + if art.Status == actions_model.ArtifactStatusExpired { status = "expired" } artifactsResponse.Artifacts = append(artifactsResponse.Artifacts, &ArtifactsViewItem{ @@ -538,7 +538,10 @@ func ArtifactsDownloadView(ctx *context_module.Context) { return } - artifacts, err := actions_model.ListArtifactsByRunIDAndName(ctx, run.ID, artifactName) + artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{ + RunID: run.ID, + ArtifactName: artifactName, + }) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 94300da868..fad4a10de8 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -569,21 +569,21 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { repoOwnerType = project_model.TypeOrganization } var err error - projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeRepository, + projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + RepoID: repo.ID, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeRepository, }) if err != nil { ctx.ServerError("GetProjects", err) return } - projects2, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: repoOwnerType, + projects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + OwnerID: repo.OwnerID, + IsClosed: util.OptionalBoolFalse, + Type: repoOwnerType, }) if err != nil { ctx.ServerError("GetProjects", err) @@ -592,21 +592,21 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.Data["OpenProjects"] = append(projects, projects2...) - projects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeRepository, + projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + RepoID: repo.ID, + IsClosed: util.OptionalBoolTrue, + Type: project_model.TypeRepository, }) if err != nil { ctx.ServerError("GetProjects", err) return } - projects2, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: repoOwnerType, + projects2, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptionsAll, + OwnerID: repo.OwnerID, + IsClosed: util.OptionalBoolTrue, + Type: repoOwnerType, }) if err != nil { ctx.ServerError("GetProjects", err) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 6417024f8b..199a065245 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -10,6 +10,7 @@ import ( "net/url" "strings" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/perm" project_model "code.gitea.io/gitea/models/project" @@ -71,9 +72,12 @@ func Projects(ctx *context.Context) { total = repo.NumClosedProjects } - projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ + ListOptions: db.ListOptions{ + PageSize: setting.UI.IssuePagingNum, + Page: page, + }, RepoID: repo.ID, - Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), OrderBy: project_model.GetSearchOrderByBySortType(sortType), Type: project_model.TypeRepository, diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go index 579743ef3c..3d4420006c 100644 --- a/routers/web/repo/setting/deploy_key.go +++ b/routers/web/repo/setting/deploy_key.go @@ -22,7 +22,7 @@ func DeployKeys(ctx *context.Context) { ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled - keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) + keys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("ListDeployKeys", err) return @@ -39,7 +39,7 @@ func DeployKeysPost(ctx *context.Context) { ctx.Data["PageIsSettingsKeys"] = true ctx.Data["DisableSSH"] = setting.SSH.Disabled - keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) + keys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("ListDeployKeys", err) return diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index ea5abb0579..5100bf782f 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -12,6 +12,7 @@ import ( "path" "strings" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" user_model "code.gitea.io/gitea/models/user" @@ -46,7 +47,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks" ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.com/usage/webhooks") - ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID}) + ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { ctx.ServerError("GetWebhooksByRepoID", err) return diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go index 1da4ddf14f..ae9a376724 100644 --- a/routers/web/shared/actions/runners.go +++ b/routers/web/shared/actions/runners.go @@ -17,18 +17,13 @@ import ( // RunnersList prepares data for runners list func RunnersList(ctx *context.Context, opts actions_model.FindRunnerOptions) { - count, err := actions_model.CountRunners(ctx, opts) + runners, count, err := db.FindAndCount[actions_model.ActionRunner](ctx, opts) if err != nil { ctx.ServerError("CountRunners", err) return } - runners, err := actions_model.FindRunners(ctx, opts) - if err != nil { - ctx.ServerError("FindRunners", err) - return - } - if err := runners.LoadAttributes(ctx); err != nil { + if err := actions_model.RunnerList(runners).LoadAttributes(ctx); err != nil { ctx.ServerError("LoadAttributes", err) return } @@ -89,18 +84,13 @@ func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int RunnerID: runner.ID, } - count, err := actions_model.CountTasks(ctx, opts) + tasks, count, err := db.FindAndCount[actions_model.ActionTask](ctx, opts) if err != nil { ctx.ServerError("CountTasks", err) return } - tasks, err := actions_model.FindTasks(ctx, opts) - if err != nil { - ctx.ServerError("FindTasks", err) - return - } - if err = tasks.LoadAttributes(ctx); err != nil { + if err = actions_model.TaskList(tasks).LoadAttributes(ctx); err != nil { ctx.ServerError("TasksLoadAttributes", err) return } diff --git a/routers/web/shared/actions/variables.go b/routers/web/shared/actions/variables.go index 341c18f589..07a0575207 100644 --- a/routers/web/shared/actions/variables.go +++ b/routers/web/shared/actions/variables.go @@ -18,7 +18,7 @@ import ( ) func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) { - variables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{ + variables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{ OwnerID: ownerID, RepoID: repoID, }) diff --git a/routers/web/shared/secrets/secrets.go b/routers/web/shared/secrets/secrets.go index 875cb0cfec..c805da734a 100644 --- a/routers/web/shared/secrets/secrets.go +++ b/routers/web/shared/secrets/secrets.go @@ -4,6 +4,7 @@ package secrets import ( + "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" @@ -14,7 +15,7 @@ import ( ) func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { - secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID}) + secrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID}) if err != nil { ctx.ServerError("FindSecrets", err) return diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 998a0c896b..411d499eb4 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -60,7 +60,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { } showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) - orgs, err := organization.FindOrgs(ctx, organization.FindOrgOptions{ + orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{ UserID: ctx.ContextUser.ID, IncludePrivate: showPrivate, }) @@ -141,7 +141,7 @@ func LoadHeaderCount(ctx *context.Context) error { } else { projectType = project_model.TypeIndividual } - projectCount, err := project_model.CountProjects(ctx, project_model.SearchOptions{ + projectCount, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, IsClosed: util.OptionalBoolOf(false), Type: projectType, diff --git a/routers/web/user/home.go b/routers/web/user/home.go index db3778d9e1..b4fb25dfe0 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -759,7 +759,9 @@ func loadRepoByIDs(ctx *context.Context, ctxUser *user_model.User, issueCountByR // ShowSSHKeys output all the ssh keys of user by uid func ShowSSHKeys(ctx *context.Context) { - keys, err := asymkey_model.ListPublicKeys(ctx, ctx.ContextUser.ID, db.ListOptions{}) + keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + OwnerID: ctx.ContextUser.ID, + }) if err != nil { ctx.ServerError("ListPublicKeys", err) return diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index 579287ffac..26f77cfc3a 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -42,7 +42,10 @@ func GetNotificationCount(ctx *context.Context) { } ctx.Data["NotificationUnreadCount"] = func() int64 { - count, err := activities_model.GetNotificationCount(ctx, ctx.Doer, activities_model.NotificationStatusUnread) + count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, + }) if err != nil { if err != goctx.Canceled { log.Error("Unable to GetNotificationCount for user:%-v: %v", ctx.Doer, err) @@ -89,7 +92,10 @@ func getNotifications(ctx *context.Context) { status = activities_model.NotificationStatusUnread } - total, err := activities_model.GetNotificationCount(ctx, ctx.Doer, status) + total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{status}, + }) if err != nil { ctx.ServerError("ErrGetNotificationCount", err) return @@ -103,12 +109,21 @@ func getNotifications(ctx *context.Context) { } statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned} - notifications, err := activities_model.NotificationsForUser(ctx, ctx.Doer, statuses, page, perPage) + nls, err := db.Find[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + ListOptions: db.ListOptions{ + PageSize: perPage, + Page: page, + }, + UserID: ctx.Doer.ID, + Status: statuses, + }) if err != nil { - ctx.ServerError("ErrNotificationsForUser", err) + ctx.ServerError("db.Find[activities_model.Notification]", err) return } + notifications := activities_model.NotificationList(nls) + failCount := 0 repos, failures, err := notifications.LoadRepos(ctx) @@ -409,5 +424,15 @@ func NotificationWatching(ctx *context.Context) { // NewAvailable returns the notification counts func NewAvailable(ctx *context.Context) { - ctx.JSON(http.StatusOK, structs.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)}) + total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{ + UserID: ctx.Doer.ID, + Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread}, + }) + if err != nil { + log.Error("db.Count[activities_model.Notification]", err) + ctx.JSON(http.StatusOK, structs.NotificationCount{New: 0}) + return + } + + ctx.JSON(http.StatusOK, structs.NotificationCount{New: total}) } diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index ee44d48dce..69a93dbf03 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -8,6 +8,7 @@ import ( "net/http" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" @@ -88,7 +89,7 @@ func DeleteApplication(ctx *context.Context) { func loadApplicationsData(ctx *context.Context) { ctx.Data["AccessTokenScopePublicOnly"] = auth_model.AccessTokenScopePublicOnly - tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) + tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) if err != nil { ctx.ServerError("ListAccessTokens", err) return @@ -97,7 +98,9 @@ func loadApplicationsData(ctx *context.Context) { ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin if setting.OAuth2.Enable { - ctx.Data["Applications"], err = auth_model.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID) + ctx.Data["Applications"], err = db.Find[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{ + OwnerID: ctx.Doer.ID, + }) if err != nil { ctx.ServerError("GetOAuth2ApplicationsByUserID", err) return diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 440885bb07..0dfb506fa9 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -260,7 +260,10 @@ func DeleteKey(ctx *context.Context) { } func loadKeysData(ctx *context.Context) { - keys, err := asymkey_model.ListPublicKeys(ctx, ctx.Doer.ID, db.ListOptions{}) + keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + OwnerID: ctx.Doer.ID, + NotKeytype: asymkey_model.KeyTypePrincipal, + }) if err != nil { ctx.ServerError("ListPublicKeys", err) return diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index 2390b0746c..d8331fef43 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -214,16 +214,12 @@ func Organization(ctx *context.Context) { opts.Page = 1 } - orgs, err := organization.FindOrgs(ctx, opts) + orgs, total, err := db.FindAndCount[organization.Organization](ctx, opts) if err != nil { ctx.ServerError("FindOrgs", err) return } - total, err := organization.CountOrgs(ctx, opts) - if err != nil { - ctx.ServerError("CountOrgs", err) - return - } + ctx.Data["Orgs"] = orgs pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) pager.SetDefaultParams(ctx) diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index ec269776e2..3647d606ee 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -9,6 +9,7 @@ import ( "sort" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -68,14 +69,17 @@ func loadSecurityData(ctx *context.Context) { } ctx.Data["WebAuthnCredentials"] = credentials - tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) + tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) if err != nil { ctx.ServerError("ListAccessTokens", err) return } ctx.Data["Tokens"] = tokens - accountLinks, err := user_model.ListAccountLinks(ctx, ctx.Doer) + accountLinks, err := db.Find[user_model.ExternalLoginUser](ctx, user_model.FindExternalUserOptions{ + UserID: ctx.Doer.ID, + OrderBy: "login_source_id DESC", + }) if err != nil { ctx.ServerError("ListAccountLinks", err) return @@ -107,7 +111,7 @@ func loadSecurityData(ctx *context.Context) { } ctx.Data["AccountLinks"] = sources - authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{ + authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{ IsActive: util.OptionalBoolNone, LoginType: auth_model.OAuth2, }) diff --git a/routers/web/user/setting/webhooks.go b/routers/web/user/setting/webhooks.go index 50cebc2a3d..679b72e501 100644 --- a/routers/web/user/setting/webhooks.go +++ b/routers/web/user/setting/webhooks.go @@ -6,6 +6,7 @@ package setting import ( "net/http" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -24,7 +25,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["BaseLinkNew"] = setting.AppSubURL + "/user/settings/hooks" ctx.Data["Description"] = ctx.Tr("settings.hooks.desc") - ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Doer.ID}) + ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{OwnerID: ctx.Doer.ID}) if err != nil { ctx.ServerError("ListWebhooksByOpts", err) return diff --git a/services/actions/clear_tasks.go b/services/actions/clear_tasks.go index 7c7043c42f..67373782d5 100644 --- a/services/actions/clear_tasks.go +++ b/services/actions/clear_tasks.go @@ -33,7 +33,7 @@ func StopEndlessTasks(ctx context.Context) error { } func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error { - tasks, err := actions_model.FindTasks(ctx, opts) + tasks, err := db.Find[actions_model.ActionTask](ctx, opts) if err != nil { return fmt.Errorf("find tasks: %w", err) } @@ -74,7 +74,7 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error { // CancelAbandonedJobs cancels the jobs which have waiting status, but haven't been picked by a runner for a long time func CancelAbandonedJobs(ctx context.Context) error { - jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{ + jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{ Statuses: []actions_model.Status{actions_model.StatusWaiting, actions_model.StatusBlocked}, UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.AbandonedJobTimeout).Unix()), }) diff --git a/services/actions/job_emitter.go b/services/actions/job_emitter.go index f7ec615364..fe39312386 100644 --- a/services/actions/job_emitter.go +++ b/services/actions/job_emitter.go @@ -44,7 +44,7 @@ func jobEmitterQueueHandler(items ...*jobUpdate) []*jobUpdate { } func checkJobsOfRun(ctx context.Context, runID int64) error { - jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: runID}) + jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: runID}) if err != nil { return err } diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 1c08cec007..175b8a4118 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -11,6 +11,7 @@ import ( "strings" actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" packages_model "code.gitea.io/gitea/models/packages" access_model "code.gitea.io/gitea/models/perm/access" @@ -298,7 +299,7 @@ func handleWorkflows( continue } - alljobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: run.ID}) + alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID}) if err != nil { log.Error("FindRunJobs: %v", err) continue @@ -377,7 +378,7 @@ func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun, repo *rep } // don't need approval if the user has been approved before - if count, err := actions_model.CountRuns(ctx, actions_model.FindRunOptions{ + if count, err := db.Count[actions_model.ActionRun](ctx, actions_model.FindRunOptions{ RepoID: repo.ID, TriggerUserID: user.ID, Approved: true, @@ -408,7 +409,7 @@ func handleSchedules( return nil } - if count, err := actions_model.CountSchedules(ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}); err != nil { + if count, err := db.Count[actions_model.ActionSchedule](ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}); err != nil { log.Error("CountSchedules: %v", err) return err } else if count > 0 { diff --git a/services/asymkey/ssh_key_test.go b/services/asymkey/ssh_key_test.go index 3a39a9a1db..fbd5d13ab2 100644 --- a/services/asymkey/ssh_key_test.go +++ b/services/asymkey/ssh_key_test.go @@ -67,7 +67,10 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib for i, kase := range testCases { s.ID = int64(i) + 20 asymkey_model.AddPublicKeysBySource(db.DefaultContext, user, s, []string{kase.keyString}) - keys, err := asymkey_model.ListPublicKeysBySource(db.DefaultContext, user.ID, s.ID) + keys, err := db.Find[asymkey_model.PublicKey](db.DefaultContext, asymkey_model.FindPublicKeyOptions{ + OwnerID: user.ID, + LoginSourceID: s.ID, + }) assert.NoError(t, err) if err != nil { continue diff --git a/services/auth/httpsign.go b/services/auth/httpsign.go index d5c8ea33aa..b604349f80 100644 --- a/services/auth/httpsign.go +++ b/services/auth/httpsign.go @@ -12,6 +12,7 @@ import ( "strings" asymkey_model "code.gitea.io/gitea/models/asymkey" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -92,7 +93,9 @@ func VerifyPubKey(r *http.Request) (*asymkey_model.PublicKey, error) { keyID := verifier.KeyId() - publicKeys, err := asymkey_model.SearchPublicKey(r.Context(), 0, keyID) + publicKeys, err := db.Find[asymkey_model.PublicKey](r.Context(), asymkey_model.FindPublicKeyOptions{ + Fingerprint: keyID, + }) if err != nil { return nil, err } diff --git a/services/auth/signin.go b/services/auth/signin.go index 2e53453681..fafe3ef3c6 100644 --- a/services/auth/signin.go +++ b/services/auth/signin.go @@ -86,7 +86,7 @@ func UserSignIn(ctx context.Context, username, password string) (*user_model.Use } } - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, }) if err != nil { diff --git a/services/auth/source/oauth2/init.go b/services/auth/source/oauth2/init.go index 0ebbdaebd4..3ad6e307f1 100644 --- a/services/auth/source/oauth2/init.go +++ b/services/auth/source/oauth2/init.go @@ -10,6 +10,7 @@ import ( "sync" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -64,7 +65,7 @@ func ResetOAuth2(ctx context.Context) error { // initOAuth2Sources is used to load and register all active OAuth2 providers func initOAuth2Sources(ctx context.Context) error { - authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, LoginType: auth.OAuth2, }) diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index 3b45b252f7..f4edb507f2 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -13,6 +13,7 @@ import ( "sort" "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -107,7 +108,7 @@ func CreateProviderFromSource(source *auth.Source) (Provider, error) { // GetOAuth2Providers returns the list of configured OAuth2 providers func GetOAuth2Providers(ctx context.Context, isActive util.OptionalBool) ([]Provider, error) { - authSources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + authSources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: isActive, LoginType: auth.OAuth2, }) diff --git a/services/auth/sspi.go b/services/auth/sspi.go index bc8ec948f2..57ba0462c5 100644 --- a/services/auth/sspi.go +++ b/services/auth/sspi.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/avatars" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" gitea_context "code.gitea.io/gitea/modules/context" @@ -130,7 +131,7 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, // getConfig retrieves the SSPI configuration from login sources func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) { - sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{ + sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ IsActive: util.OptionalBoolTrue, LoginType: auth.SSPI, }) diff --git a/services/auth/sync.go b/services/auth/sync.go index 11a59d41ae..7562ac812b 100644 --- a/services/auth/sync.go +++ b/services/auth/sync.go @@ -15,7 +15,7 @@ import ( func SyncExternalUsers(ctx context.Context, updateExisting bool) error { log.Trace("Doing: SyncExternalUsers") - ls, err := auth.FindSources(ctx, auth.FindSourcesOptions{}) + ls, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{}) if err != nil { log.Error("SyncExternalUsers: %v", err) return err diff --git a/services/migrations/update.go b/services/migrations/update.go index d466832363..4a49206f82 100644 --- a/services/migrations/update.go +++ b/services/migrations/update.go @@ -36,8 +36,7 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ } const batchSize = 100 - var start int - for { + for page := 0; ; page++ { select { case <-ctx.Done(): log.Warn("UpdateMigrationPosterIDByGitService(%s) cancelled", tp.Name()) @@ -45,10 +44,13 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ default: } - users, err := user_model.FindExternalUsersByProvider(ctx, user_model.FindExternalUserOptions{ + users, err := db.Find[user_model.ExternalLoginUser](ctx, user_model.FindExternalUserOptions{ + ListOptions: db.ListOptions{ + PageSize: batchSize, + Page: page, + }, Provider: provider, - Start: start, - Limit: batchSize, + OrderBy: "login_source_id ASC, external_id ASC", }) if err != nil { return err @@ -70,7 +72,6 @@ func updateMigrationPosterIDByGitService(ctx context.Context, tp structs.GitServ if len(users) < batchSize { break } - start += len(users) } return nil } diff --git a/services/pull/review.go b/services/pull/review.go index 3f5644b0cd..e48f380154 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -49,16 +49,16 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis return nil } issueIDs := prs.GetIssueIDs() - var codeComments []*issues_model.Comment - if err := db.Find(ctx, &issues_model.FindCommentsOptions{ + codeComments, err := db.Find[issues_model.Comment](ctx, issues_model.FindCommentsOptions{ ListOptions: db.ListOptions{ ListAll: true, }, Type: issues_model.CommentTypeCode, Invalidated: util.OptionalBoolFalse, IssueIDs: issueIDs, - }, &codeComments); err != nil { + }) + if err != nil { return fmt.Errorf("find code comments: %v", err) } for _, comment := range codeComments { diff --git a/services/repository/delete.go b/services/repository/delete.go index 861dfa2dcd..08d6800ee7 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -54,13 +54,13 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } // Query the action tasks of this repo, they will be needed after they have been deleted to remove the logs - tasks, err := actions_model.FindTasks(ctx, actions_model.FindTaskOptions{RepoID: repoID}) + tasks, err := db.Find[actions_model.ActionTask](ctx, actions_model.FindTaskOptions{RepoID: repoID}) if err != nil { return fmt.Errorf("find actions tasks of repo %v: %w", repoID, err) } // Query the artifacts of this repo, they will be needed after they have been deleted to remove artifacts files in ObjectStorage - artifacts, err := actions_model.ListArtifactsByRepoID(ctx, repoID) + artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{RepoID: repoID}) if err != nil { return fmt.Errorf("list actions artifacts of repo %v: %w", repoID, err) } @@ -75,7 +75,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID } // Delete Deploy Keys - deployKeys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: repoID}) + deployKeys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: repoID}) if err != nil { return fmt.Errorf("listDeployKeys: %w", err) } diff --git a/services/repository/hooks.go b/services/repository/hooks.go index 8506fa3413..7b82f36b43 100644 --- a/services/repository/hooks.go +++ b/services/repository/hooks.go @@ -85,7 +85,7 @@ func GenerateGitHooks(ctx context.Context, templateRepo, generateRepo *repo_mode // GenerateWebhooks generates webhooks from a template repository func GenerateWebhooks(ctx context.Context, templateRepo, generateRepo *repo_model.Repository) error { - templateWebhooks, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: templateRepo.ID}) + templateWebhooks, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{RepoID: templateRepo.ID}) if err != nil { return err } diff --git a/services/secrets/secrets.go b/services/secrets/secrets.go index 1c4772d6bf..97e15ba6c7 100644 --- a/services/secrets/secrets.go +++ b/services/secrets/secrets.go @@ -15,7 +15,7 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data return nil, false, err } - s, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{ + s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, Name: name, @@ -40,7 +40,7 @@ func CreateOrUpdateSecret(ctx context.Context, ownerID, repoID int64, name, data } func DeleteSecretByID(ctx context.Context, ownerID, repoID, secretID int64) error { - s, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{ + s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, SecretID: secretID, @@ -60,7 +60,7 @@ func DeleteSecretByName(ctx context.Context, ownerID, repoID int64, name string) return err } - s, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{ + s, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{ OwnerID: ownerID, RepoID: repoID, Name: name, diff --git a/services/user/user.go b/services/user/user.go index 4a4908fe8e..932e359c9f 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -172,7 +172,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error { // An alternative option here would be write a function which would delete all organizations but it seems // but such a function would likely get out of date for { - orgs, err := organization.FindOrgs(ctx, organization.FindOrgOptions{ + orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{ ListOptions: db.ListOptions{ PageSize: repo_model.RepositoryListDefaultPageSize, Page: 1, diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index 9d5dab85f7..ac18da3525 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" webhook_model "code.gitea.io/gitea/models/webhook" @@ -222,7 +223,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu var ws []*webhook_model.Webhook if source.Repository != nil { - repoHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{ + repoHooks, err := db.Find[webhook_model.Webhook](ctx, webhook_model.ListWebhookOptions{ RepoID: source.Repository.ID, IsActive: util.OptionalBoolTrue, }) @@ -236,7 +237,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu // append additional webhooks of a user or organization if owner != nil { - ownerHooks, err := webhook_model.ListWebhooksByOpts(ctx, &webhook_model.ListWebhookOptions{ + ownerHooks, err := db.Find[webhook_model.Webhook](ctx, webhook_model.ListWebhookOptions{ OwnerID: owner.ID, IsActive: util.OptionalBoolTrue, }) diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go index 9bb9e7b3c7..1148b3ad39 100644 --- a/tests/integration/auth_ldap_test.go +++ b/tests/integration/auth_ldap_test.go @@ -414,7 +414,7 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ Name: gitLDAPUser.UserName, }) - usersOrgs, err := organization.FindOrgs(db.DefaultContext, organization.FindOrgOptions{ + usersOrgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: user.ID, IncludePrivate: true, }) diff --git a/tests/integration/org_count_test.go b/tests/integration/org_count_test.go index d1aa41963e..6386f53f05 100644 --- a/tests/integration/org_count_test.go +++ b/tests/integration/org_count_test.go @@ -118,7 +118,7 @@ func doCheckOrgCounts(username string, orgCounts map[string]int, strict bool, ca Name: username, }) - orgs, err := organization.FindOrgs(db.DefaultContext, organization.FindOrgOptions{ + orgs, err := db.Find[organization.Organization](db.DefaultContext, organization.FindOrgOptions{ UserID: user.ID, IncludePrivate: true, }) From 6c7744370f1c5e524377a15ea26abd58a1598639 Mon Sep 17 00:00:00 2001 From: Nanguan Lin <70063547+lng2020@users.noreply.github.com> Date: Fri, 24 Nov 2023 20:25:01 +0800 Subject: [PATCH 15/19] Remove workaround in disk-clean.yml (#28195) According to https://github.com/jlumbroso/free-disk-space/issues/17, the issue has been fixed in the main branch. --- .github/workflows/disk-clean.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/disk-clean.yml b/.github/workflows/disk-clean.yml index 78cd251354..8abe8891c7 100644 --- a/.github/workflows/disk-clean.yml +++ b/.github/workflows/disk-clean.yml @@ -8,21 +8,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - # FIXME: https://github.com/jlumbroso/free-disk-space/issues/17 - - name: same as 'large-packages' but without 'google-cloud-sdk' - shell: bash - run: | - sudo apt-get update - sudo apt-get remove -y '^dotnet-.*' || true - sudo apt-get remove -y '^llvm-.*' || true - sudo apt-get remove -y 'php.*' || true - sudo apt-get remove -y '^mongodb-.*' || true - sudo apt-get remove -y '^mysql-.*' || true - sudo apt-get remove -y azure-cli google-chrome-stable firefox powershell mono-devel libgl1-mesa-dri || true - sudo apt-get autoremove -y - sudo apt-get clean - env: - DEBIAN_FRONTEND: noninteractive - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: From ad9aac39752c733ae0694b8299c0a525c08d14c9 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 24 Nov 2023 22:57:09 +0900 Subject: [PATCH 16/19] Fix some incorrect links in docs (#28191) https://gitea.com/gitea/gitea-docusaurus/actions/runs/661/jobs/0#jobstep-9-39 I noticed that there are many warning logs in building docs. It is causing 404 in docs.gitea.com now, so we need to fix it. And there are also some other problems in v1.19 which can not be done in this PR. ps: Are there any good methods to test this in local? --- docs/content/administration/email-setup.zh-cn.md | 2 +- docs/content/help/faq.en-us.md | 1 - docs/content/help/faq.zh-cn.md | 1 - docs/content/usage/actions/act-runner.en-us.md | 2 +- docs/content/usage/actions/act-runner.zh-cn.md | 2 +- docs/content/usage/authentication.en-us.md | 2 +- docs/content/usage/authentication.zh-cn.md | 2 +- 7 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/content/administration/email-setup.zh-cn.md b/docs/content/administration/email-setup.zh-cn.md index 0a7ac3378f..1e7009b8dd 100644 --- a/docs/content/administration/email-setup.zh-cn.md +++ b/docs/content/administration/email-setup.zh-cn.md @@ -55,7 +55,7 @@ PASSWD = `password` 要发送测试邮件以验证设置,请转到 Gitea > 站点管理 > 配置 > SMTP 邮件配置。 -有关所有选项的完整列表,请查看[配置速查表](doc/administration/config-cheat-sheet.zh-cn.md)。 +有关所有选项的完整列表,请查看[配置速查表](doc/administration/config-cheat-sheet.md)。 请注意:只有在使用 TLS 或 `HOST=localhost` 加密 SMTP 服务器通信时才支持身份验证。TLS 加密可以通过以下方式进行: diff --git a/docs/content/help/faq.en-us.md b/docs/content/help/faq.en-us.md index 0116b81e75..93b3434f47 100644 --- a/docs/content/help/faq.en-us.md +++ b/docs/content/help/faq.en-us.md @@ -39,7 +39,6 @@ If a bug fix is targeted on 1.20.1 but 1.20.1 is not released yet, you can get t To migrate from Gogs to Gitea: -- [Gogs version 0.9.146 or less](installation/upgrade-from-gogs.md) - [Gogs version 0.11.46.0418](https://github.com/go-gitea/gitea/issues/4286) To migrate from GitHub to Gitea, you can use Gitea's built-in migration form. diff --git a/docs/content/help/faq.zh-cn.md b/docs/content/help/faq.zh-cn.md index b71d38269a..096ac05f48 100644 --- a/docs/content/help/faq.zh-cn.md +++ b/docs/content/help/faq.zh-cn.md @@ -41,7 +41,6 @@ menu: 要从Gogs迁移到Gitea: -- [Gogs版本0.9.146或更低](installation/upgrade-from-gogs.md) - [Gogs版本0.11.46.0418](https://github.com/go-gitea/gitea/issues/4286) 要从GitHub迁移到Gitea,您可以使用Gitea内置的迁移表单。 diff --git a/docs/content/usage/actions/act-runner.en-us.md b/docs/content/usage/actions/act-runner.en-us.md index 6e99beb870..9e030b0c35 100644 --- a/docs/content/usage/actions/act-runner.en-us.md +++ b/docs/content/usage/actions/act-runner.en-us.md @@ -114,7 +114,7 @@ If you cannot see the settings page, please make sure that you have the right pe The format of the registration token is a random string `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx`. -A registration token can also be obtained from the gitea [command-line interface](../../administration/command-line.en-us.md#actions-generate-runner-token): +A registration token can also be obtained from the gitea [command-line interface](../../administration/command-line.md#actions-generate-runner-token): ``` gitea --config /etc/gitea/app.ini actions generate-runner-token diff --git a/docs/content/usage/actions/act-runner.zh-cn.md b/docs/content/usage/actions/act-runner.zh-cn.md index 39d5c238c7..2e8bf515ae 100644 --- a/docs/content/usage/actions/act-runner.zh-cn.md +++ b/docs/content/usage/actions/act-runner.zh-cn.md @@ -113,7 +113,7 @@ Runner级别决定了从哪里获取注册令牌。 注册令牌的格式是一个随机字符串 `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx`。 -注册令牌也可以通过 Gitea 的 [命令行](../../administration/command-line.en-us.md#actions-generate-runner-token) 获得: +注册令牌也可以通过 Gitea 的 [命令行](../../administration/command-line.md#actions-generate-runner-token) 获得: ### 注册Runner diff --git a/docs/content/usage/authentication.en-us.md b/docs/content/usage/authentication.en-us.md index 6e4ede0be6..6a6de08d6c 100644 --- a/docs/content/usage/authentication.en-us.md +++ b/docs/content/usage/authentication.en-us.md @@ -198,7 +198,7 @@ administrative user. field is set to `mail.com`, then Gitea will expect the `user email` field for an authenticated GIT instance to be `gituser@mail.com`.[^2] -**Note**: PAM support is added via [build-time flags](installation/install-from-source.md#build), +**Note**: PAM support is added via [build-time flags](installation/from-source.md#build), and the official binaries provided do not have this enabled. PAM requires that the necessary libpam dynamic library be available and the necessary PAM development headers be accessible to the compiler. diff --git a/docs/content/usage/authentication.zh-cn.md b/docs/content/usage/authentication.zh-cn.md index e465bc3f23..d1cfeeb800 100644 --- a/docs/content/usage/authentication.zh-cn.md +++ b/docs/content/usage/authentication.zh-cn.md @@ -162,7 +162,7 @@ PAM提供了一种机制,通过对用户进行PAM认证来自动将其添加 - PAM电子邮件域:用户认证时要附加的电子邮件后缀。例如,如果登录系统期望一个名为gituse的用户, 并且将此字段设置为mail.com,那么Gitea在验证一个GIT实例的用户时将期望user emai字段为gituser@mail.com[^2]。 -**Note**: PAM 支持通过[build-time flags](installation/install-from-source.md#build)添加, +**Note**: PAM 支持通过[build-time flags](installation/from-source.md#build)添加, 而官方提供的二进制文件通常不会默认启用此功能。PAM需要确保系统上有必要的libpam动态库,并且编译器可以访问必要的PAM开发头文件。 [^1]: 例如,在Debian "Bullseye"上使用标准Linux登录,可以使用`common-session-noninteractive`。这个值对于其他版本的Debian, From 2ac2796a4c15ba924a7d446c00c4822cd00ed1ba Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 24 Nov 2023 22:21:46 +0800 Subject: [PATCH 17/19] Refactor graceful manager to use shared code (#28073) Make "windows" and "unix" share as much code as possible. No logic change. --- modules/graceful/manager_common.go | 104 ++++++++++++++++++++++++++++ modules/graceful/manager_unix.go | 102 ++------------------------- modules/graceful/manager_windows.go | 87 +++-------------------- 3 files changed, 119 insertions(+), 174 deletions(-) create mode 100644 modules/graceful/manager_common.go diff --git a/modules/graceful/manager_common.go b/modules/graceful/manager_common.go new file mode 100644 index 0000000000..aaf008670c --- /dev/null +++ b/modules/graceful/manager_common.go @@ -0,0 +1,104 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package graceful + +import ( + "context" + "runtime/pprof" + "sync" + "time" +) + +type systemdNotifyMsg string + +const ( + readyMsg systemdNotifyMsg = "READY=1" + stoppingMsg systemdNotifyMsg = "STOPPING=1" + reloadingMsg systemdNotifyMsg = "RELOADING=1" + watchdogMsg systemdNotifyMsg = "WATCHDOG=1" +) + +func statusMsg(msg string) systemdNotifyMsg { + return systemdNotifyMsg("STATUS=" + msg) +} + +// Manager manages the graceful shutdown process +type Manager struct { + ctx context.Context + isChild bool + forked bool + lock sync.RWMutex + state state + shutdownCtx context.Context + hammerCtx context.Context + terminateCtx context.Context + managerCtx context.Context + shutdownCtxCancel context.CancelFunc + hammerCtxCancel context.CancelFunc + terminateCtxCancel context.CancelFunc + managerCtxCancel context.CancelFunc + runningServerWaitGroup sync.WaitGroup + createServerWaitGroup sync.WaitGroup + terminateWaitGroup sync.WaitGroup + shutdownRequested chan struct{} + + toRunAtShutdown []func() + toRunAtTerminate []func() +} + +func newGracefulManager(ctx context.Context) *Manager { + manager := &Manager{ctx: ctx, shutdownRequested: make(chan struct{})} + manager.createServerWaitGroup.Add(numberOfServersToCreate) + manager.prepare(ctx) + manager.start() + return manager +} + +func (g *Manager) prepare(ctx context.Context) { + g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx) + g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx) + g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx) + g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx) + + g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) + g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) + g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) + g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) + + if !g.setStateTransition(stateInit, stateRunning) { + panic("invalid graceful manager state: transition from init to running failed") + } +} + +// DoImmediateHammer causes an immediate hammer +func (g *Manager) DoImmediateHammer() { + g.notify(statusMsg("Sending immediate hammer")) + g.doHammerTime(0 * time.Second) +} + +// DoGracefulShutdown causes a graceful shutdown +func (g *Manager) DoGracefulShutdown() { + g.lock.Lock() + select { + case <-g.shutdownRequested: + default: + close(g.shutdownRequested) + } + forked := g.forked + g.lock.Unlock() + + if !forked { + g.notify(stoppingMsg) + } else { + g.notify(statusMsg("Shutting down after fork")) + } + g.doShutdown() +} + +// RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die. +// Any call to RegisterServer must be matched by a call to ServerDone +func (g *Manager) RegisterServer() { + KillParent() + g.runningServerWaitGroup.Add(1) +} diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go index bdf23a4fde..f4af4993d9 100644 --- a/modules/graceful/manager_unix.go +++ b/modules/graceful/manager_unix.go @@ -12,7 +12,6 @@ import ( "os/signal" "runtime/pprof" "strconv" - "sync" "syscall" "time" @@ -22,51 +21,6 @@ import ( "code.gitea.io/gitea/modules/setting" ) -// Manager manages the graceful shutdown process -type Manager struct { - isChild bool - forked bool - lock *sync.RWMutex - state state - shutdownCtx context.Context - hammerCtx context.Context - terminateCtx context.Context - managerCtx context.Context - shutdownCtxCancel context.CancelFunc - hammerCtxCancel context.CancelFunc - terminateCtxCancel context.CancelFunc - managerCtxCancel context.CancelFunc - runningServerWaitGroup sync.WaitGroup - createServerWaitGroup sync.WaitGroup - terminateWaitGroup sync.WaitGroup - - toRunAtShutdown []func() - toRunAtTerminate []func() -} - -func newGracefulManager(ctx context.Context) *Manager { - manager := &Manager{ - isChild: len(os.Getenv(listenFDsEnv)) > 0 && os.Getppid() > 1, - lock: &sync.RWMutex{}, - } - manager.createServerWaitGroup.Add(numberOfServersToCreate) - manager.start(ctx) - return manager -} - -type systemdNotifyMsg string - -const ( - readyMsg systemdNotifyMsg = "READY=1" - stoppingMsg systemdNotifyMsg = "STOPPING=1" - reloadingMsg systemdNotifyMsg = "RELOADING=1" - watchdogMsg systemdNotifyMsg = "WATCHDOG=1" -) - -func statusMsg(msg string) systemdNotifyMsg { - return systemdNotifyMsg("STATUS=" + msg) -} - func pidMsg() systemdNotifyMsg { return systemdNotifyMsg("MAINPID=" + strconv.Itoa(os.Getpid())) } @@ -89,27 +43,13 @@ func (g *Manager) notify(msg systemdNotifyMsg) { } } -func (g *Manager) start(ctx context.Context) { - // Make contexts - g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx) - g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx) - g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx) - g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx) - - // Next add pprof labels to these contexts - g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) - g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) - g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) - g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) - +func (g *Manager) start() { // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager pprof.SetGoroutineLabels(g.managerCtx) - defer pprof.SetGoroutineLabels(ctx) + defer pprof.SetGoroutineLabels(g.ctx) + + g.isChild = len(os.Getenv(listenFDsEnv)) > 0 && os.Getppid() > 1 - // Set the running state & handle signals - if !g.setStateTransition(stateInit, stateRunning) { - panic("invalid graceful manager state: transition from init to running failed") - } g.notify(statusMsg("Starting Gitea")) g.notify(pidMsg()) go g.handleSignals(g.managerCtx) @@ -118,11 +58,9 @@ func (g *Manager) start(ctx context.Context) { startupDone := make(chan struct{}) go func() { defer close(startupDone) - // Wait till we're done getting all of the listeners and then close - // the unused ones + // Wait till we're done getting all the listeners and then close the unused ones g.createServerWaitGroup.Wait() - // Ignore the error here there's not much we can do with it - // They're logged in the CloseProvidedListeners function + // Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function _ = CloseProvidedListeners() g.notify(readyMsg) }() @@ -133,7 +71,7 @@ func (g *Manager) start(ctx context.Context) { return case <-g.IsShutdown(): func() { - // When waitgroup counter goes negative it will panic - we don't care about this so we can just ignore it. + // When WaitGroup counter goes negative it will panic - we don't care about this so we can just ignore it. defer func() { _ = recover() }() @@ -255,29 +193,3 @@ func (g *Manager) DoGracefulRestart() { g.doShutdown() } } - -// DoImmediateHammer causes an immediate hammer -func (g *Manager) DoImmediateHammer() { - g.notify(statusMsg("Sending immediate hammer")) - g.doHammerTime(0 * time.Second) -} - -// DoGracefulShutdown causes a graceful shutdown -func (g *Manager) DoGracefulShutdown() { - g.lock.Lock() - if !g.forked { - g.lock.Unlock() - g.notify(stoppingMsg) - } else { - g.lock.Unlock() - g.notify(statusMsg("Shutting down after fork")) - } - g.doShutdown() -} - -// RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die. -// Any call to RegisterServer must be matched by a call to ServerDone -func (g *Manager) RegisterServer() { - KillParent() - g.runningServerWaitGroup.Add(1) -} diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go index c2ea5383cc..0248dcb24d 100644 --- a/modules/graceful/manager_windows.go +++ b/modules/graceful/manager_windows.go @@ -7,11 +7,9 @@ package graceful import ( - "context" "os" "runtime/pprof" "strconv" - "sync" "time" "code.gitea.io/gitea/modules/log" @@ -30,64 +28,11 @@ const ( acceptHammerCode = svc.Accepted(hammerCode) ) -// Manager manages the graceful shutdown process -type Manager struct { - ctx context.Context - isChild bool - lock *sync.RWMutex - state state - shutdownCtx context.Context - hammerCtx context.Context - terminateCtx context.Context - managerCtx context.Context - shutdownCtxCancel context.CancelFunc - hammerCtxCancel context.CancelFunc - terminateCtxCancel context.CancelFunc - managerCtxCancel context.CancelFunc - runningServerWaitGroup sync.WaitGroup - createServerWaitGroup sync.WaitGroup - terminateWaitGroup sync.WaitGroup - shutdownRequested chan struct{} - - toRunAtShutdown []func() - toRunAtTerminate []func() -} - -func newGracefulManager(ctx context.Context) *Manager { - manager := &Manager{ - isChild: false, - lock: &sync.RWMutex{}, - ctx: ctx, - } - manager.createServerWaitGroup.Add(numberOfServersToCreate) - manager.start() - return manager -} - func (g *Manager) start() { - // Make contexts - g.terminateCtx, g.terminateCtxCancel = context.WithCancel(g.ctx) - g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(g.ctx) - g.hammerCtx, g.hammerCtxCancel = context.WithCancel(g.ctx) - g.managerCtx, g.managerCtxCancel = context.WithCancel(g.ctx) - - // Next add pprof labels to these contexts - g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) - g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) - g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) - g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) - // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager pprof.SetGoroutineLabels(g.managerCtx) defer pprof.SetGoroutineLabels(g.ctx) - // Make channels - g.shutdownRequested = make(chan struct{}) - - // Set the running state - if !g.setStateTransition(stateInit, stateRunning) { - panic("invalid graceful manager state: transition from init to running failed") - } if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip { log.Trace("Skipping SVC check as SKIP_MINWINSVC is set") return @@ -201,30 +146,6 @@ hammerLoop: return false, 0 } -// DoImmediateHammer causes an immediate hammer -func (g *Manager) DoImmediateHammer() { - g.doHammerTime(0 * time.Second) -} - -// DoGracefulShutdown causes a graceful shutdown -func (g *Manager) DoGracefulShutdown() { - g.lock.Lock() - select { - case <-g.shutdownRequested: - g.lock.Unlock() - default: - close(g.shutdownRequested) - g.lock.Unlock() - g.doShutdown() - } -} - -// RegisterServer registers the running of a listening server. -// Any call to RegisterServer must be matched by a call to ServerDone -func (g *Manager) RegisterServer() { - g.runningServerWaitGroup.Add(1) -} - func (g *Manager) awaitServer(limit time.Duration) bool { c := make(chan struct{}) go func() { @@ -249,3 +170,11 @@ func (g *Manager) awaitServer(limit time.Duration) bool { } } } + +func (g *Manager) notify(msg systemdNotifyMsg) { + // Windows doesn't use systemd to notify +} + +func KillParent() { + // Windows doesn't need to "kill parent" because there is no graceful restart +} From eacce14546874dbeeda1f3f71dca89e0fc5226e6 Mon Sep 17 00:00:00 2001 From: pitpalme Date: Fri, 24 Nov 2023 15:46:19 +0100 Subject: [PATCH 18/19] Fix delete-orphaned-repos (#28200) gitea doctor failed at checking and fixing 'delete-orphaned-repos', because table name 'user' needs quoting to be correctly recognized by at least PostgreSQL. fixes #28199 --- modules/doctor/repository.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/doctor/repository.go b/modules/doctor/repository.go index d69ba2048b..b3a03aa422 100644 --- a/modules/doctor/repository.go +++ b/modules/doctor/repository.go @@ -26,7 +26,7 @@ func handleDeleteOrphanedRepos(ctx context.Context, logger log.Logger, autofix b // countOrphanedRepos count repository where user of owner_id do not exist func countOrphanedRepos(ctx context.Context) (int64, error) { - return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=user.id") + return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=`user`.id") } // deleteOrphanedRepos delete repository where user of owner_id do not exist @@ -43,7 +43,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) { default: var ids []int64 if err := e.Table("`repository`"). - Join("LEFT", "`user`", "repository.owner_id=user.id"). + Join("LEFT", "`user`", "repository.owner_id=`user`.id"). Where(builder.IsNull{"`user`.id"}). Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil { return deleted, err From 80217cacfc3fcf0ffa0dc203843c11e318f85d19 Mon Sep 17 00:00:00 2001 From: CodeShakingSheep <19874562+CodeShakingSheep@users.noreply.github.com> Date: Sat, 25 Nov 2023 03:16:29 +0100 Subject: [PATCH 19/19] Docs: Replace deprecated IS_TLS_ENABLED mailer setting in email setup (#28205) In the [docs for email setup](https://docs.gitea.com/administration/email-setup) `mailer.IS_TLS_ENABLED` is mentioned which was replaced by `mailer.PROTOCOL` in release 1.18.0 according to https://blog.gitea.com/release-of-1.18.0/ . This change wasn't reflected in the docs for email setup. I just replaced the deprecated mailer setting. --- docs/content/administration/email-setup.en-us.md | 2 +- docs/content/administration/email-setup.zh-cn.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/administration/email-setup.en-us.md b/docs/content/administration/email-setup.en-us.md index 645a7a3f43..f9621e6075 100644 --- a/docs/content/administration/email-setup.en-us.md +++ b/docs/content/administration/email-setup.en-us.md @@ -61,7 +61,7 @@ Please note: authentication is only supported when the SMTP server communication - STARTTLS (also known as Opportunistic TLS) via port 587. Initial connection is done over cleartext, but then be upgraded over TLS if the server supports it. - SMTPS connection (SMTP over TLS) via the default port 465. Connection to the server use TLS from the beginning. -- Forced SMTPS connection with `IS_TLS_ENABLED=true`. (These are both known as Implicit TLS.) +- Forced SMTPS connection with `PROTOCOL=smtps`. (These are both known as Implicit TLS.) This is due to protections imposed by the Go internal libraries against STRIPTLS attacks. Note that Implicit TLS is recommended by [RFC8314](https://tools.ietf.org/html/rfc8314#section-3) since 2018. diff --git a/docs/content/administration/email-setup.zh-cn.md b/docs/content/administration/email-setup.zh-cn.md index 1e7009b8dd..cbcf8015b5 100644 --- a/docs/content/administration/email-setup.zh-cn.md +++ b/docs/content/administration/email-setup.zh-cn.md @@ -61,7 +61,7 @@ PASSWD = `password` - 通过端口 587 的 STARTTLS(也称为 Opportunistic TLS)。初始连接是明文的,但如果服务器支持,则可以升级为 TLS。 - 通过默认端口 465 的 SMTPS 连接。连接到服务器从一开始就使用 TLS。 -- 使用 `IS_TLS_ENABLED=true` 进行强制的 SMTPS 连接。(这两种方式都被称为 Implicit TLS) +- 使用 `PROTOCOL=smtps` 进行强制的 SMTPS 连接。(这两种方式都被称为 Implicit TLS) 这是由于 Go 内部库对 STRIPTLS 攻击的保护机制。 请注意,自2018年起,[RFC8314](https://tools.ietf.org/html/rfc8314#section-3) 推荐使用 Implicit TLS。