Merge remote-tracking branch 'upstream/main' into feat/api-project-boards

This commit is contained in:
Dinesh Salunke
2023-11-25 12:56:11 +05:30
125 changed files with 873 additions and 938 deletions

View File

@@ -8,21 +8,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - 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) - name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main uses: jlumbroso/free-disk-space@main
with: with:

View File

@@ -10,7 +10,7 @@
<img src="https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main"> <img src="https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main">
</a> </a>
<a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea"> <a href="https://discord.gg/Gitea" title="Join the Discord chat at https://discord.gg/Gitea">
<img src="https://img.shields.io/discord/322538954119184384.svg"> <img src="https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2">
</a> </a>
<a href="https://app.codecov.io/gh/go-gitea/gitea" title="Codecov"> <a href="https://app.codecov.io/gh/go-gitea/gitea" title="Codecov">
<img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg"> <img src="https://codecov.io/gh/go-gitea/gitea/branch/main/graph/badge.svg">

View File

@@ -9,6 +9,7 @@ import (
"text/tabwriter" "text/tabwriter"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
auth_service "code.gitea.io/gitea/services/auth" auth_service "code.gitea.io/gitea/services/auth"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@@ -62,7 +63,7 @@ func runListAuth(c *cli.Context) error {
return err 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 { if err != nil {
return err return err
} }

View File

@@ -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. - 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. - 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. 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. Note that Implicit TLS is recommended by [RFC8314](https://tools.ietf.org/html/rfc8314#section-3) since 2018.

View File

@@ -55,13 +55,13 @@ PASSWD = `password`
要发送测试邮件以验证设置,请转到 Gitea > 站点管理 > 配置 > SMTP 邮件配置。 要发送测试邮件以验证设置,请转到 Gitea > 站点管理 > 配置 > SMTP 邮件配置。
有关所有选项的完整列表,请查看[配置速查表](doc/administration/config-cheat-sheet.zh-cn.md)。 有关所有选项的完整列表,请查看[配置速查表](doc/administration/config-cheat-sheet.md)。
请注意:只有在使用 TLS 或 `HOST=localhost` 加密 SMTP 服务器通信时才支持身份验证。TLS 加密可以通过以下方式进行: 请注意:只有在使用 TLS 或 `HOST=localhost` 加密 SMTP 服务器通信时才支持身份验证。TLS 加密可以通过以下方式进行:
- 通过端口 587 的 STARTTLS也称为 Opportunistic TLS。初始连接是明文的但如果服务器支持则可以升级为 TLS。 - 通过端口 587 的 STARTTLS也称为 Opportunistic TLS。初始连接是明文的但如果服务器支持则可以升级为 TLS。
- 通过默认端口 465 的 SMTPS 连接。连接到服务器从一开始就使用 TLS。 - 通过默认端口 465 的 SMTPS 连接。连接到服务器从一开始就使用 TLS。
- 使用 `IS_TLS_ENABLED=true` 进行强制的 SMTPS 连接。(这两种方式都被称为 Implicit TLS - 使用 `PROTOCOL=smtps` 进行强制的 SMTPS 连接。(这两种方式都被称为 Implicit TLS
这是由于 Go 内部库对 STRIPTLS 攻击的保护机制。 这是由于 Go 内部库对 STRIPTLS 攻击的保护机制。
请注意自2018年起[RFC8314](https://tools.ietf.org/html/rfc8314#section-3) 推荐使用 Implicit TLS。 请注意自2018年起[RFC8314](https://tools.ietf.org/html/rfc8314#section-3) 推荐使用 Implicit TLS。

View File

@@ -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: 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) - [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. To migrate from GitHub to Gitea, you can use Gitea's built-in migration form.

View File

@@ -41,7 +41,6 @@ menu:
要从Gogs迁移到Gitea 要从Gogs迁移到Gitea
- [Gogs版本0.9.146或更低](installation/upgrade-from-gogs.md)
- [Gogs版本0.11.46.0418](https://github.com/go-gitea/gitea/issues/4286) - [Gogs版本0.11.46.0418](https://github.com/go-gitea/gitea/issues/4286)
要从GitHub迁移到Gitea您可以使用Gitea内置的迁移表单。 要从GitHub迁移到Gitea您可以使用Gitea内置的迁移表单。

View File

@@ -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`. 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 gitea --config /etc/gitea/app.ini actions generate-runner-token

View File

@@ -113,7 +113,7 @@ Runner级别决定了从哪里获取注册令牌。
注册令牌的格式是一个随机字符串 `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx` 注册令牌的格式是一个随机字符串 `D0gvfu2iHfUjNqCYVljVyRV14fISpJxxxxxxxxxx`
注册令牌也可以通过 Gitea 的 [命令行](../../administration/command-line.en-us.md#actions-generate-runner-token) 获得: 注册令牌也可以通过 Gitea 的 [命令行](../../administration/command-line.md#actions-generate-runner-token) 获得:
### 注册Runner ### 注册Runner

View File

@@ -23,7 +23,7 @@ First of all, you need a Gitea instance.
You can follow the [documentation](installation/from-package.md) to set up a new instance or upgrade your existing one. You can follow the [documentation](installation/from-package.md) to set up a new instance or upgrade your existing one.
It doesn't matter how you install or run Gitea, as long as its version is 1.19.0 or higher. It doesn't matter how you install or run Gitea, as long as its version is 1.19.0 or higher.
Actions are disabled by default, so you need to add the following to the configuration file to enable it: Since 1.21.0, Actions are enabled by default. If you are using versions before 1.21.0, you need to add the following to the configuration file to enable it:
```ini ```ini
[actions] [actions]

View File

@@ -23,7 +23,7 @@ menu:
您可以按照[文档](installation/from-package.md) 来设置一个新实例或升级现有实例。 您可以按照[文档](installation/from-package.md) 来设置一个新实例或升级现有实例。
无论您如何安装或运行Gitea只要版本号是1.19.0或更高即可。 无论您如何安装或运行Gitea只要版本号是1.19.0或更高即可。
默认情况下Actions是禁用的,因此您需要将以下内容添加到配置文件中以启用它: 从1.21.0开始,默认情况下Actions是启用的。如果您正在使用1.21.0之前的版本,您需要将以下内容添加到配置文件中以启用它:
```ini ```ini
[actions] [actions]

View File

@@ -198,7 +198,7 @@ administrative user.
field is set to `mail.com`, then Gitea will expect the `user email` field 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] 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 and the official binaries provided do not have this enabled. PAM requires that
the necessary libpam dynamic library be available and the necessary PAM the necessary libpam dynamic library be available and the necessary PAM
development headers be accessible to the compiler. development headers be accessible to the compiler.

View File

@@ -162,7 +162,7 @@ PAM提供了一种机制通过对用户进行PAM认证来自动将其添加
- PAM电子邮件域:用户认证时要附加的电子邮件后缀。例如如果登录系统期望一个名为gituse的用户 - PAM电子邮件域:用户认证时要附加的电子邮件后缀。例如如果登录系统期望一个名为gituse的用户
并且将此字段设置为mail.com那么Gitea在验证一个GIT实例的用户时将期望user emai字段为gituser@mail.com[^2]。 并且将此字段设置为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开发头文件。 而官方提供的二进制文件通常不会默认启用此功能。PAM需要确保系统上有必要的libpam动态库并且编译器可以访问必要的PAM开发头文件。
[^1]: 例如在Debian "Bullseye"上使用标准Linux登录可以使用`common-session-noninteractive`。这个值对于其他版本的Debian [^1]: 例如在Debian "Bullseye"上使用标准Linux登录可以使用`common-session-noninteractive`。这个值对于其他版本的Debian

View File

@@ -14,6 +14,8 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
) )
// ArtifactStatus is the status of an artifact, uploading, expired or need-delete // 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 return err
} }
// ListArtifactsByRunID returns all artifacts of a run type FindArtifactsOptions struct {
func ListArtifactsByRunID(ctx context.Context, runID int64) ([]*ActionArtifact, error) { db.ListOptions
arts := make([]*ActionArtifact, 0, 10) RepoID int64
return arts, db.GetEngine(ctx).Where("run_id=?", runID).Find(&arts) RunID int64
ArtifactName string
Status int
} }
// ListArtifactsByRunIDAndArtifactName returns an artifacts of a run by artifact name func (opts FindArtifactsOptions) ToConds() builder.Cond {
func ListArtifactsByRunIDAndArtifactName(ctx context.Context, runID int64, artifactName string) ([]*ActionArtifact, error) { cond := builder.NewCond()
arts := make([]*ActionArtifact, 0, 10) if opts.RepoID > 0 {
return arts, db.GetEngine(ctx).Where("run_id=? AND artifact_name=?", runID, artifactName).Find(&arts) 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 return cond
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)
} }
// ActionArtifactMeta is the meta data of an artifact // ActionArtifactMeta is the meta data of an artifact
type ActionArtifactMeta struct { type ActionArtifactMeta struct {
ArtifactName string ArtifactName string
FileSize int64 FileSize int64
Status int64 Status ArtifactStatus
} }
// ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run // ListUploadedArtifactsMeta returns all uploaded artifacts meta of a run
@@ -143,18 +153,6 @@ func ListUploadedArtifactsMeta(ctx context.Context, runID int64) ([]*ActionArtif
Find(&arts) 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 // ListNeedExpiredArtifacts returns all need expired artifacts but not deleted
func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) {
arts := make([]*ActionArtifact, 0, 10) arts := make([]*ActionArtifact, 0, 10)

View File

@@ -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. // CancelRunningJobs cancels all running and waiting jobs associated with a specific workflow.
func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string) error { 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'. // 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, RepoID: repoID,
Ref: ref, Ref: ref,
WorkflowID: workflowID, 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. // Iterate over each found run and cancel its associated jobs.
for _, run := range runs { for _, run := range runs {
// Find all jobs associated with the current run. // Find all jobs associated with the current run.
jobs, _, err := FindRunJobs(ctx, FindRunJobOptions{ jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{
RunID: run.ID, RunID: run.ID,
}) })
if err != nil { if err != nil {

View File

@@ -61,7 +61,7 @@ type FindRunJobOptions struct {
UpdatedBefore timeutil.TimeStamp UpdatedBefore timeutil.TimeStamp
} }
func (opts FindRunJobOptions) toConds() builder.Cond { func (opts FindRunJobOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RunID > 0 { if opts.RunID > 0 {
cond = cond.And(builder.Eq{"run_id": opts.RunID}) cond = cond.And(builder.Eq{"run_id": opts.RunID})
@@ -83,17 +83,3 @@ func (opts FindRunJobOptions) toConds() builder.Cond {
} }
return 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))
}

View File

@@ -75,7 +75,7 @@ type FindRunOptions struct {
Status []Status Status []Status
} }
func (opts FindRunOptions) toConds() builder.Cond { func (opts FindRunOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -101,18 +101,8 @@ func (opts FindRunOptions) toConds() builder.Cond {
return cond return cond
} }
func FindRuns(ctx context.Context, opts FindRunOptions) (RunList, int64, error) { func (opts FindRunOptions) ToOrders() string {
e := db.GetEngine(ctx).Where(opts.toConds()) return "`id` DESC"
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))
} }
type StatusInfo struct { type StatusInfo struct {

View File

@@ -156,7 +156,7 @@ type FindRunnerOptions struct {
WithAvailable bool // not only runners belong to, but also runners can be used 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() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
@@ -181,7 +181,7 @@ func (opts FindRunnerOptions) toCond() builder.Cond {
return cond return cond
} }
func (opts FindRunnerOptions) toOrder() string { func (opts FindRunnerOptions) ToOrders() string {
switch opts.Sort { switch opts.Sort {
case "online": case "online":
return "last_online DESC" return "last_online DESC"
@@ -199,22 +199,6 @@ func (opts FindRunnerOptions) toOrder() string {
return "last_online DESC" 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 // GetRunnerByUUID returns a runner via uuid
func GetRunnerByUUID(ctx context.Context, uuid string) (*ActionRunner, error) { func GetRunnerByUUID(ctx context.Context, uuid string) (*ActionRunner, error) {
var runner ActionRunner var runner ActionRunner
@@ -263,8 +247,7 @@ func DeleteRunner(ctx context.Context, id int64) error {
// CreateRunner creates new runner. // CreateRunner creates new runner.
func CreateRunner(ctx context.Context, t *ActionRunner) error { func CreateRunner(ctx context.Context, t *ActionRunner) error {
_, err := db.GetEngine(ctx).Insert(t) return db.Insert(ctx, t)
return err
} }
func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {

View File

@@ -67,7 +67,7 @@ type FindScheduleOptions struct {
OwnerID int64 OwnerID int64
} }
func (opts FindScheduleOptions) toConds() builder.Cond { func (opts FindScheduleOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -79,16 +79,6 @@ func (opts FindScheduleOptions) toConds() builder.Cond {
return cond return cond
} }
func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) { func (opts FindScheduleOptions) ToOrders() string {
e := db.GetEngine(ctx).Where(opts.toConds()) return "`id` DESC"
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))
} }

View File

@@ -71,7 +71,7 @@ type FindSpecOptions struct {
Next int64 Next int64
} }
func (opts FindSpecOptions) toConds() builder.Cond { func (opts FindSpecOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -84,23 +84,18 @@ func (opts FindSpecOptions) toConds() builder.Cond {
return cond return cond
} }
func (opts FindSpecOptions) ToOrders() string {
return "`id` DESC"
}
func FindSpecs(ctx context.Context, opts FindSpecOptions) (SpecList, int64, error) { func FindSpecs(ctx context.Context, opts FindSpecOptions) (SpecList, int64, error) {
e := db.GetEngine(ctx).Where(opts.toConds()) specs, total, err := db.FindAndCount[ActionScheduleSpec](ctx, opts)
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)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
if err := specs.LoadSchedules(ctx); err != nil { if err := SpecList(specs).LoadSchedules(ctx); err != nil {
return nil, 0, err return nil, 0, err
} }
return specs, total, nil return specs, total, nil
} }
func CountSpecs(ctx context.Context, opts FindSpecOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionScheduleSpec))
}

View File

@@ -62,7 +62,7 @@ type FindTaskOptions struct {
IDOrderDesc bool IDOrderDesc bool
} }
func (opts FindTaskOptions) toConds() builder.Cond { func (opts FindTaskOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -88,18 +88,9 @@ func (opts FindTaskOptions) toConds() builder.Cond {
return cond return cond
} }
func FindTasks(ctx context.Context, opts FindTaskOptions) (TaskList, error) { func (opts FindTaskOptions) ToOrders() string {
e := db.GetEngine(ctx).Where(opts.toConds())
if opts.PageSize > 0 && opts.Page >= 1 {
e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
if opts.IDOrderDesc { if opts.IDOrderDesc {
e.OrderBy("id DESC") return "`id` DESC"
} }
var tasks TaskList return ""
return tasks, e.Find(&tasks)
}
func CountTasks(ctx context.Context, opts FindTaskOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionTask))
} }

View File

@@ -20,6 +20,10 @@ type ActionTaskOutput struct {
OutputValue string `xorm:"MEDIUMTEXT"` OutputValue string `xorm:"MEDIUMTEXT"`
} }
func init() {
db.RegisterModel(new(ActionTaskOutput))
}
// FindTaskOutputByTaskID returns the outputs of the task. // FindTaskOutputByTaskID returns the outputs of the task.
func FindTaskOutputByTaskID(ctx context.Context, taskID int64) ([]*ActionTaskOutput, error) { func FindTaskOutputByTaskID(ctx context.Context, taskID int64) ([]*ActionTaskOutput, error) {
var outputs []*ActionTaskOutput var outputs []*ActionTaskOutput

View File

@@ -56,7 +56,7 @@ type FindVariablesOpts struct {
RepoID int64 RepoID int64
} }
func (opts *FindVariablesOpts) toConds() builder.Cond { func (opts FindVariablesOpts) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.OwnerID > 0 { if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
@@ -67,15 +67,6 @@ func (opts *FindVariablesOpts) toConds() builder.Cond {
return 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) { func GetVariableByID(ctx context.Context, variableID int64) (*ActionVariable, error) {
var variable ActionVariable var variable ActionVariable
has, err := db.GetEngine(ctx).Where("id=?", variableID).Get(&variable) has, err := db.GetEngine(ctx).Where("id=?", variableID).Get(&variable)

View File

@@ -22,7 +22,6 @@ import (
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm"
) )
type ( type (
@@ -93,7 +92,7 @@ type FindNotificationOptions struct {
} }
// ToCond will convert each condition into a xorm-Cond // ToCond will convert each condition into a xorm-Cond
func (opts *FindNotificationOptions) ToCond() builder.Cond { func (opts FindNotificationOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.UserID != 0 { if opts.UserID != 0 {
cond = cond.And(builder.Eq{"notification.user_id": opts.UserID}) cond = cond.And(builder.Eq{"notification.user_id": opts.UserID})
@@ -105,8 +104,12 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID}) cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID})
} }
if len(opts.Status) > 0 { if len(opts.Status) > 0 {
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)) cond = cond.And(builder.In("notification.status", opts.Status))
} }
}
if len(opts.Source) > 0 { if len(opts.Source) > 0 {
cond = cond.And(builder.In("notification.source", opts.Source)) cond = cond.And(builder.In("notification.source", opts.Source))
} }
@@ -119,24 +122,8 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
return 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) ToOrders() string {
func (opts *FindNotificationOptions) ToSession(ctx context.Context) *xorm.Session { return "notification.updated_unix DESC"
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{})
} }
// CreateRepoTransferNotification creates notification for the user a repository was transferred to // 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 { func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
// init // init
var toNotify container.Set[int64] var toNotify container.Set[int64]
notifications, err := getNotificationsByIssueID(ctx, issueID) notifications, err := db.Find[Notification](ctx, FindNotificationOptions{
IssueID: issueID,
})
if err != nil { if err != nil {
return err return err
} }
@@ -273,23 +262,6 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
return nil return nil
} }
func getNotificationsByIssueID(ctx context.Context, issueID int64) (notifications []*Notification, err error) {
err = db.GetEngine(ctx).
Where("issue_id = ?", issueID).
Find(&notifications)
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 { func createIssueNotification(ctx context.Context, userID int64, issue *issues_model.Issue, commentID, updatedByID int64) error {
notification := &Notification{ notification := &Notification{
UserID: userID, UserID: userID,
@@ -341,35 +313,6 @@ func GetIssueNotification(ctx context.Context, userID, issueID int64) (*Notifica
return notification, err 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(&notifications)
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 // LoadAttributes load Repo Issue User and Comment if not loaded
func (n *Notification) LoadAttributes(ctx context.Context) (err error) { func (n *Notification) LoadAttributes(ctx context.Context) (err error) {
if err = n.loadRepo(ctx); err != nil { 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) 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 // NotificationList contains a list of notifications
type NotificationList []*Notification type NotificationList []*Notification
// LoadAttributes load Repo Issue User and Comment if not loaded // LoadAttributes load Repo Issue User and Comment if not loaded
func (nl NotificationList) LoadAttributes(ctx context.Context) error { func (nl NotificationList) LoadAttributes(ctx context.Context) error {
var err error if _, _, err := nl.LoadRepos(ctx); err != nil {
for i := 0; i < len(nl); i++ {
err = nl[i].LoadAttributes(ctx)
if err != nil && !issues_model.IsErrCommentNotExist(err) {
return err 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 return nil
} }
@@ -665,6 +638,68 @@ func (nl NotificationList) getPendingCommentIDs() []int64 {
return ids.Values() 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 // LoadComments loads comments from database
func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) { func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
if len(nl) == 0 { if len(nl) == 0 {
@@ -717,30 +752,6 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
return failures, nil 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. // SetIssueReadBy sets issue to be read by given user.
func SetIssueReadBy(ctx context.Context, issueID, userID int64) error { func SetIssueReadBy(ctx context.Context, issueID, userID int64) error {
if err := issues_model.UpdateIssueUserByRead(ctx, userID, issueID); err != nil { if err := issues_model.UpdateIssueUserByRead(ctx, userID, issueID); err != nil {

View File

@@ -34,8 +34,13 @@ func TestCreateOrUpdateIssueNotifications(t *testing.T) {
func TestNotificationsForUser(t *testing.T) { func TestNotificationsForUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
statuses := []activities_model.NotificationStatus{activities_model.NotificationStatusRead, activities_model.NotificationStatusUnread} notfs, err := db.Find[activities_model.Notification](db.DefaultContext, activities_model.FindNotificationOptions{
notfs, err := activities_model.NotificationsForUser(db.DefaultContext, user, statuses, 1, 10) UserID: user.ID,
Status: []activities_model.NotificationStatus{
activities_model.NotificationStatusRead,
activities_model.NotificationStatusUnread,
},
})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, notfs, 3) { if assert.Len(t, notfs, 3) {
assert.EqualValues(t, 5, notfs[0].ID) assert.EqualValues(t, 5, notfs[0].ID)
@@ -68,11 +73,21 @@ func TestNotification_GetIssue(t *testing.T) {
func TestGetNotificationCount(t *testing.T) { func TestGetNotificationCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) 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.NoError(t, err)
assert.EqualValues(t, 0, cnt) 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.NoError(t, err)
assert.EqualValues(t, 1, cnt) assert.EqualValues(t, 1, cnt)
} }

View File

@@ -52,7 +52,7 @@ type IssueByRepositoryCount struct {
func GetStatistic(ctx context.Context) (stats Statistic) { func GetStatistic(ctx context.Context) (stats Statistic) {
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
stats.Counter.User = user_model.CountUsers(ctx, nil) 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.PublicKey, _ = e.Count(new(asymkey_model.PublicKey))
stats.Counter.Repo, _ = repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{}) stats.Counter.Repo, _ = repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{})
stats.Counter.Watch, _ = e.Count(new(repo_model.Watch)) 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.Follow, _ = e.Count(new(user_model.Follow))
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
stats.Counter.Release, _ = e.Count(new(repo_model.Release)) 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.Webhook, _ = e.Count(new(webhook.Webhook))
stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone)) stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone))
stats.Counter.Label, _ = e.Count(new(issues_model.Label)) stats.Counter.Label, _ = e.Count(new(issues_model.Label))

View File

@@ -179,45 +179,33 @@ func SearchPublicKeyByContentExact(ctx context.Context, content string) (*Public
return key, nil return key, nil
} }
// SearchPublicKey returns a list of public keys matching the provided arguments. type FindPublicKeyOptions struct {
func SearchPublicKey(ctx context.Context, uid int64, fingerprint string) ([]*PublicKey, error) { db.ListOptions
keys := make([]*PublicKey, 0, 5) OwnerID int64
Fingerprint string
KeyTypes []KeyType
NotKeytype KeyType
LoginSourceID int64
}
func (opts FindPublicKeyOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if uid != 0 { if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": uid}) cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
} }
if fingerprint != "" { if opts.Fingerprint != "" {
cond = cond.And(builder.Eq{"fingerprint": fingerprint}) cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint})
} }
return keys, db.GetEngine(ctx).Where(cond).Find(&keys) if len(opts.KeyTypes) > 0 {
} cond = cond.And(builder.In("type", opts.KeyTypes))
// 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 opts.NotKeytype > 0 {
keys := make([]*PublicKey, 0, 5) cond = cond.And(builder.Neq{"type": opts.NotKeytype})
return keys, sess.Find(&keys) }
} if opts.LoginSourceID > 0 {
cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID})
// CountPublicKeys count public keys a user has }
func CountPublicKeys(ctx context.Context, userID int64) (int64, error) { return cond
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)
} }
// UpdatePublicKeyUpdated updates public key use time. // 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 // Get Public Keys from DB with current LDAP source
var giteaKeys []string 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 { if err != nil {
log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err) log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err)
} }

View File

@@ -21,7 +21,10 @@ import (
func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification { func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification {
// Now try to associate the signature with the committer, if present // Now try to associate the signature with the committer, if present
if committer.ID != 0 { 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 if err != nil { // Skipping failed to get ssh keys of user
log.Error("ListPublicKeys: %v", err) log.Error("ListPublicKeys: %v", err)
return &CommitVerification{ return &CommitVerification{

View File

@@ -210,7 +210,7 @@ type ListDeployKeysOptions struct {
Fingerprint string Fingerprint string
} }
func (opt ListDeployKeysOptions) toCond() builder.Cond { func (opt ListDeployKeysOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opt.RepoID != 0 { if opt.RepoID != 0 {
cond = cond.And(builder.Eq{"repo_id": opt.RepoID}) cond = cond.And(builder.Eq{"repo_id": opt.RepoID})
@@ -223,23 +223,3 @@ func (opt ListDeployKeysOptions) toCond() builder.Cond {
} }
return 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{})
}

View File

@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
lru "github.com/hashicorp/golang-lru/v2" lru "github.com/hashicorp/golang-lru/v2"
"xorm.io/builder"
) )
// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error. // ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
@@ -201,25 +202,18 @@ type ListAccessTokensOptions struct {
UserID int64 UserID int64
} }
// ListAccessTokens returns a list of access tokens belongs to given user. func (opts ListAccessTokensOptions) ToConds() builder.Cond {
func ListAccessTokens(ctx context.Context, opts ListAccessTokensOptions) ([]*AccessToken, error) { cond := builder.NewCond()
sess := db.GetEngine(ctx).Where("uid=?", opts.UserID) // 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 { if len(opts.Name) > 0 {
sess = sess.Where("name=?", opts.Name) cond = cond.And(builder.Eq{"name": opts.Name})
} }
return cond
}
sess = sess.Desc("created_unix") func (opts ListAccessTokensOptions) ToOrders() string {
return "created_unix DESC"
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)
} }
// UpdateAccessToken updates information of access token. // UpdateAccessToken updates information of access token.
@@ -228,15 +222,6 @@ func UpdateAccessToken(ctx context.Context, t *AccessToken) error {
return err 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. // DeleteAccessTokenByID deletes access token by given ID.
func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error { func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error {
cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{ cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{

View File

@@ -85,7 +85,7 @@ func TestGetAccessTokenBySHA(t *testing.T) {
func TestListAccessTokens(t *testing.T) { func TestListAccessTokens(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) assert.NoError(t, err)
if assert.Len(t, tokens, 2) { if assert.Len(t, tokens, 2) {
assert.Equal(t, int64(1), tokens[0].UID) 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") 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) assert.NoError(t, err)
if assert.Len(t, tokens, 1) { if assert.Len(t, tokens, 1) {
assert.Equal(t, int64(2), tokens[0].UID) assert.Equal(t, int64(2), tokens[0].UID)
assert.Equal(t, "Token A", tokens[0].Name) 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.NoError(t, err)
assert.Empty(t, tokens) assert.Empty(t, tokens)
} }

View File

@@ -5,6 +5,7 @@ package auth
import ( import (
"context" "context"
"crypto/sha256"
"encoding/base32" "encoding/base32"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
@@ -19,7 +20,6 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
uuid "github.com/google/uuid" uuid "github.com/google/uuid"
"github.com/minio/sha256-simd"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm" "xorm.io/xorm"
@@ -243,13 +243,6 @@ func GetOAuth2ApplicationByID(ctx context.Context, id int64) (app *OAuth2Applica
return app, nil 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 // CreateOAuth2ApplicationOptions holds options to create an oauth2 application
type CreateOAuth2ApplicationOptions struct { type CreateOAuth2ApplicationOptions struct {
Name string Name string
@@ -372,25 +365,6 @@ func DeleteOAuth2Application(ctx context.Context, id, userid int64) error {
return committer.Commit() 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. // OAuth2AuthorizationCode is a code to obtain an access token in combination with the client secret once. It has a limited lifetime.

View File

@@ -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"
}

View File

@@ -242,6 +242,7 @@ func CreateSource(ctx context.Context, source *Source) error {
} }
type FindSourcesOptions struct { type FindSourcesOptions struct {
db.ListOptions
IsActive util.OptionalBool IsActive util.OptionalBool
LoginType Type LoginType Type
} }
@@ -257,27 +258,22 @@ func (opts FindSourcesOptions) ToConds() builder.Cond {
return conds 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 // IsSSPIEnabled returns true if there is at least one activated login
// source of type LoginSSPI // source of type LoginSSPI
func IsSSPIEnabled(ctx context.Context) bool { func IsSSPIEnabled(ctx context.Context) bool {
if !db.HasEngine { if !db.HasEngine {
return false return false
} }
sources, err := FindSources(ctx, FindSourcesOptions{
exist, err := db.Exists[Source](ctx, FindSourcesOptions{
IsActive: util.OptionalBoolTrue, IsActive: util.OptionalBoolTrue,
LoginType: SSPI, LoginType: SSPI,
}) })
if err != nil { if err != nil {
log.Error("ActiveSources: %v", err) log.Error("Active SSPI Sources: %v", err)
return false return false
} }
return len(sources) > 0 return exist
} }
// GetSourceByID returns login source by given ID. // GetSourceByID returns login source by given ID.
@@ -346,12 +342,6 @@ func UpdateSource(ctx context.Context, source *Source) error {
return err 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. // ErrSourceNotExist represents a "SourceNotExist" kind of error.
type ErrSourceNotExist struct { type ErrSourceNotExist struct {
ID int64 ID int64

View File

@@ -264,3 +264,8 @@ func inTransaction(ctx context.Context) (*xorm.Session, bool) {
return nil, false 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)
}

View File

@@ -15,6 +15,7 @@ import (
const ( const (
// DefaultMaxInSize represents default variables number on IN () in SQL // DefaultMaxInSize represents default variables number on IN () in SQL
DefaultMaxInSize = 50 DefaultMaxInSize = 50
defaultFindSliceSize = 10
) )
// Paginator is the base for different ListOptions types // 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 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 // GetSkipTake returns the skip and take values
func (opts *ListOptions) GetSkipTake() (skip, take int) { func (opts *ListOptions) GetSkipTake() (skip, take int) {
@@ -67,8 +73,16 @@ func (opts *ListOptions) GetStartEnd() (start, end int) {
return start, end 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 // IsListAll indicates PageSize and Page will be ignored
func (opts *ListOptions) IsListAll() bool { func (opts ListOptions) IsListAll() bool {
return opts.ListAll 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 // AbsoluteListOptions absolute options to paginate results
type AbsoluteListOptions struct { type AbsoluteListOptions struct {
skip int skip int
@@ -124,29 +142,63 @@ func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) {
// FindOptions represents a find options // FindOptions represents a find options
type FindOptions interface { type FindOptions interface {
Paginator GetPage() int
GetPageSize() int
IsListAll() bool
ToConds() builder.Cond ToConds() builder.Cond
} }
type FindOptionsOrder interface {
ToOrders() string
}
// Find represents a common find function which accept an options interface // 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()) sess := GetEngine(ctx).Where(opts.ToConds())
if !opts.IsListAll() { page, pageSize := opts.GetPage(), opts.GetPageSize()
sess.Limit(opts.GetSkipTake()) 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 // Count represents a common count function which accept an options interface
func Count[T any](ctx context.Context, opts FindOptions, object T) (int64, error) { func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
return GetEngine(ctx).Where(opts.ToConds()).Count(object) var object T
return GetEngine(ctx).Where(opts.ToConds()).Count(&object)
} }
// FindAndCount represents a common findandcount function which accept an options interface // 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()) sess := GetEngine(ctx).Where(opts.ToConds())
if !opts.IsListAll() { page, pageSize := opts.GetPage(), opts.GetPageSize()
sess.Limit(opts.GetSkipTake()) 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
} }

View File

@@ -18,11 +18,11 @@ type mockListOptions struct {
db.ListOptions db.ListOptions
} }
func (opts *mockListOptions) IsListAll() bool { func (opts mockListOptions) IsListAll() bool {
return true return true
} }
func (opts *mockListOptions) ToConds() builder.Cond { func (opts mockListOptions) ToConds() builder.Cond {
return builder.NewCond() return builder.NewCond()
} }
@@ -37,17 +37,16 @@ func TestFind(t *testing.T) {
assert.NotEmpty(t, repoUnitCount) assert.NotEmpty(t, repoUnitCount)
opts := mockListOptions{} opts := mockListOptions{}
var repoUnits []repo_model.RepoUnit repoUnits, err := db.Find[repo_model.RepoUnit](db.DefaultContext, opts)
err = db.Find(db.DefaultContext, &opts, &repoUnits)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, repoUnits, repoUnitCount) 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.NoError(t, err)
assert.EqualValues(t, repoUnitCount, cnt) assert.EqualValues(t, repoUnitCount, cnt)
repoUnits = make([]repo_model.RepoUnit, 0, 10) repoUnits, newCnt, err := db.FindAndCount[repo_model.RepoUnit](db.DefaultContext, opts)
newCnt, err := db.FindAndCount(db.DefaultContext, &opts, &repoUnits)
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, cnt, newCnt) assert.EqualValues(t, cnt, newCnt)
assert.Len(t, repoUnits, repoUnitCount)
} }

View File

@@ -1027,7 +1027,7 @@ type FindCommentsOptions struct {
} }
// ToConds implements FindOptions interface // ToConds implements FindOptions interface
func (opts *FindCommentsOptions) ToConds() builder.Cond { func (opts FindCommentsOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"issue.repo_id": opts.RepoID})

View File

@@ -456,7 +456,7 @@ func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
return builder.Select("org_id").From("org_user").Where(cond) 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} var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
if opts.UserID > 0 { if opts.UserID > 0 {
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate))) cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
@@ -467,23 +467,8 @@ func (opts FindOrgOptions) toConds() builder.Cond {
return cond return cond
} }
// FindOrgs returns a list of organizations according given conditions func (opts FindOrgOptions) ToOrders() string {
func FindOrgs(ctx context.Context, opts FindOrgOptions) ([]*Organization, error) { return "`user`.name ASC"
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))
} }
// HasOrgOrUserVisible tells if the given user can see the given org or user // HasOrgOrUserVisible tells if the given user can see the given org or user

View File

@@ -131,7 +131,7 @@ func TestCountOrganizations(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{}) expected, err := db.GetEngine(db.DefaultContext).Where("type=?", user_model.UserTypeOrganization).Count(&organization.Organization{})
assert.NoError(t, err) 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.NoError(t, err)
assert.Equal(t, expected, cnt) assert.Equal(t, expected, cnt)
} }
@@ -183,7 +183,7 @@ func TestIsPublicMembership(t *testing.T) {
func TestFindOrgs(t *testing.T) { func TestFindOrgs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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, UserID: 4,
IncludePrivate: true, IncludePrivate: true,
}) })
@@ -192,14 +192,14 @@ func TestFindOrgs(t *testing.T) {
assert.EqualValues(t, 3, orgs[0].ID) 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, UserID: 4,
IncludePrivate: false, IncludePrivate: false,
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, orgs, 0) 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, UserID: 4,
IncludePrivate: true, IncludePrivate: true,
}) })

View File

@@ -203,16 +203,16 @@ func IsTypeValid(p Type) bool {
// SearchOptions are options for GetProjects // SearchOptions are options for GetProjects
type SearchOptions struct { type SearchOptions struct {
db.ListOptions
OwnerID int64 OwnerID int64
RepoID int64 RepoID int64
Page int
IsClosed util.OptionalBool IsClosed util.OptionalBool
OrderBy db.SearchOrderBy OrderBy db.SearchOrderBy
Type Type Type Type
Title string Title string
} }
func (opts *SearchOptions) toConds() builder.Cond { func (opts SearchOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID > 0 { if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -237,9 +237,8 @@ func (opts *SearchOptions) toConds() builder.Cond {
return cond return cond
} }
// CountProjects counts projects func (opts SearchOptions) ToOrders() string {
func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { return opts.OrderBy.String()
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Project))
} }
func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy { func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy {
@@ -255,22 +254,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 // NewProject creates a new Project
func NewProject(ctx context.Context, p *Project) error { func NewProject(ctx context.Context, p *Project) error {
if !IsBoardTypeValid(p.BoardType) { if !IsBoardTypeValid(p.BoardType) {

View File

@@ -34,13 +34,13 @@ func TestIsProjectTypeValid(t *testing.T) {
func TestGetProjects(t *testing.T) { func TestGetProjects(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) assert.NoError(t, err)
// 1 value for this repo exists in the fixtures // 1 value for this repo exists in the fixtures
assert.Len(t, projects, 1) 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) assert.NoError(t, err)
// 1 value for this repo exists in the fixtures // 1 value for this repo exists in the fixtures
@@ -109,7 +109,7 @@ func TestProjectsSort(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
projects, count, err := FindProjects(db.DefaultContext, SearchOptions{ projects, count, err := db.FindAndCount[Project](db.DefaultContext, SearchOptions{
OrderBy: GetSearchOrderByBySortType(tt.sortType), OrderBy: GetSearchOrderByBySortType(tt.sortType),
}) })
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -584,9 +584,9 @@ func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
}, repo.Description) }, repo.Description)
if err != nil { if err != nil {
log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err) 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. // CloneLink represents different types of clone URLs of repository.

View File

@@ -78,7 +78,7 @@ type FindSecretsOptions struct {
Name string Name string
} }
func (opts *FindSecretsOptions) toConds() builder.Cond { func (opts FindSecretsOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.OwnerID > 0 { if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
@@ -96,22 +96,6 @@ func (opts *FindSecretsOptions) toConds() builder.Cond {
return 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. // UpdateSecret changes org or user reop secret.
func UpdateSecret(ctx context.Context, secretID int64, data string) error { func UpdateSecret(ctx context.Context, secretID int64, data string) error {
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)

View File

@@ -96,19 +96,6 @@ func GetExternalLogin(ctx context.Context, externalLoginUser *ExternalLoginUser)
return db.GetEngine(ctx).Get(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 // LinkExternalToUser link the external user to the user
func LinkExternalToUser(ctx context.Context, user *User, externalLoginUser *ExternalLoginUser) error { 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). 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 // FindExternalUserOptions represents an options to find external users
type FindExternalUserOptions struct { type FindExternalUserOptions struct {
db.ListOptions
Provider string Provider string
Limit int UserID int64
Start int OrderBy string
} }
func (opts FindExternalUserOptions) toConds() builder.Cond { func (opts FindExternalUserOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if len(opts.Provider) > 0 { if len(opts.Provider) > 0 {
cond = cond.And(builder.Eq{"provider": opts.Provider}) cond = cond.And(builder.Eq{"provider": opts.Provider})
} }
if opts.UserID > 0 {
cond = cond.And(builder.Eq{"user_id": opts.UserID})
}
return cond return cond
} }
// FindExternalUsersByProvider represents external users via provider func (opts FindExternalUserOptions) ToOrders() string {
func FindExternalUsersByProvider(ctx context.Context, opts FindExternalUserOptions) ([]ExternalLoginUser, error) { return opts.OrderBy
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
} }

View File

@@ -435,7 +435,7 @@ type ListWebhookOptions struct {
IsActive util.OptionalBool IsActive util.OptionalBool
} }
func (opts *ListWebhookOptions) toCond() builder.Cond { func (opts ListWebhookOptions) ToConds() builder.Cond {
cond := builder.NewCond() cond := builder.NewCond()
if opts.RepoID != 0 { if opts.RepoID != 0 {
cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID}) cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID})
@@ -449,27 +449,6 @@ func (opts *ListWebhookOptions) toCond() builder.Cond {
return 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. // UpdateWebhook updates information of webhook.
func UpdateWebhook(ctx context.Context, w *Webhook) error { func UpdateWebhook(ctx context.Context, w *Webhook) error {
_, err := db.GetEngine(ctx).ID(w.ID).AllCols().Update(w) _, err := db.GetEngine(ctx).ID(w.ID).AllCols().Update(w)

View File

@@ -123,7 +123,7 @@ func TestGetWebhookByOwnerID(t *testing.T) {
func TestGetActiveWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) assert.NoError(t, err)
if assert.Len(t, hooks, 1) { if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(1), hooks[0].ID) assert.Equal(t, int64(1), hooks[0].ID)
@@ -133,7 +133,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) {
func TestGetWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) assert.NoError(t, err)
if assert.Len(t, hooks, 2) { if assert.Len(t, hooks, 2) {
assert.Equal(t, int64(1), hooks[0].ID) assert.Equal(t, int64(1), hooks[0].ID)
@@ -143,7 +143,7 @@ func TestGetWebhooksByRepoID(t *testing.T) {
func TestGetActiveWebhooksByOwnerID(t *testing.T) { func TestGetActiveWebhooksByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) assert.NoError(t, err)
if assert.Len(t, hooks, 1) { if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(3), hooks[0].ID) assert.Equal(t, int64(3), hooks[0].ID)
@@ -153,7 +153,7 @@ func TestGetActiveWebhooksByOwnerID(t *testing.T) {
func TestGetWebhooksByOwnerID(t *testing.T) { func TestGetWebhooksByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) 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) assert.NoError(t, err)
if assert.Len(t, hooks, 1) { if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(3), hooks[0].ID) assert.Equal(t, int64(3), hooks[0].ID)

View File

@@ -563,6 +563,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode) ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues) ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests) ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
ctx.Data["CanWriteActions"] = ctx.Repo.CanWrite(unit_model.TypeActions)
canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository) canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository)
if err != nil { if err != nil {

View File

@@ -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 // countOrphanedRepos count repository where user of owner_id do not exist
func countOrphanedRepos(ctx context.Context) (int64, error) { 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 // deleteOrphanedRepos delete repository where user of owner_id do not exist
@@ -43,7 +43,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) {
default: default:
var ids []int64 var ids []int64
if err := e.Table("`repository`"). 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"}). Where(builder.IsNull{"`user`.id"}).
Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil { Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
return deleted, err return deleted, err

View File

@@ -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)
}

View File

@@ -12,7 +12,6 @@ import (
"os/signal" "os/signal"
"runtime/pprof" "runtime/pprof"
"strconv" "strconv"
"sync"
"syscall" "syscall"
"time" "time"
@@ -22,51 +21,6 @@ import (
"code.gitea.io/gitea/modules/setting" "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 { func pidMsg() systemdNotifyMsg {
return systemdNotifyMsg("MAINPID=" + strconv.Itoa(os.Getpid())) return systemdNotifyMsg("MAINPID=" + strconv.Itoa(os.Getpid()))
} }
@@ -89,27 +43,13 @@ func (g *Manager) notify(msg systemdNotifyMsg) {
} }
} }
func (g *Manager) start(ctx context.Context) { func (g *Manager) start() {
// 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"))
// Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager
pprof.SetGoroutineLabels(g.managerCtx) 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(statusMsg("Starting Gitea"))
g.notify(pidMsg()) g.notify(pidMsg())
go g.handleSignals(g.managerCtx) go g.handleSignals(g.managerCtx)
@@ -118,11 +58,9 @@ func (g *Manager) start(ctx context.Context) {
startupDone := make(chan struct{}) startupDone := make(chan struct{})
go func() { go func() {
defer close(startupDone) defer close(startupDone)
// Wait till we're done getting all of the listeners and then close // Wait till we're done getting all the listeners and then close the unused ones
// the unused ones
g.createServerWaitGroup.Wait() g.createServerWaitGroup.Wait()
// Ignore the error here there's not much we can do with it // Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function
// They're logged in the CloseProvidedListeners function
_ = CloseProvidedListeners() _ = CloseProvidedListeners()
g.notify(readyMsg) g.notify(readyMsg)
}() }()
@@ -133,7 +71,7 @@ func (g *Manager) start(ctx context.Context) {
return return
case <-g.IsShutdown(): case <-g.IsShutdown():
func() { 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() { defer func() {
_ = recover() _ = recover()
}() }()
@@ -255,29 +193,3 @@ func (g *Manager) DoGracefulRestart() {
g.doShutdown() 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)
}

View File

@@ -7,11 +7,9 @@
package graceful package graceful
import ( import (
"context"
"os" "os"
"runtime/pprof" "runtime/pprof"
"strconv" "strconv"
"sync"
"time" "time"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@@ -30,64 +28,11 @@ const (
acceptHammerCode = svc.Accepted(hammerCode) 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() { 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 // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager
pprof.SetGoroutineLabels(g.managerCtx) pprof.SetGoroutineLabels(g.managerCtx)
defer pprof.SetGoroutineLabels(g.ctx) 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 { if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
log.Trace("Skipping SVC check as SKIP_MINWINSVC is set") log.Trace("Skipping SVC check as SKIP_MINWINSVC is set")
return return
@@ -201,30 +146,6 @@ hammerLoop:
return false, 0 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 { func (g *Manager) awaitServer(limit time.Duration) bool {
c := make(chan struct{}) c := make(chan struct{})
go func() { 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
}

View File

@@ -19,6 +19,7 @@ import (
// any modification to the underlying policies once it's been created. // any modification to the underlying policies once it's been created.
type Sanitizer struct { type Sanitizer struct {
defaultPolicy *bluemonday.Policy defaultPolicy *bluemonday.Policy
descriptionPolicy *bluemonday.Policy
rendererPolicies map[string]*bluemonday.Policy rendererPolicies map[string]*bluemonday.Policy
init sync.Once init sync.Once
} }
@@ -41,6 +42,7 @@ func NewSanitizer() {
func InitializeSanitizer() { func InitializeSanitizer() {
sanitizer.rendererPolicies = map[string]*bluemonday.Policy{} sanitizer.rendererPolicies = map[string]*bluemonday.Policy{}
sanitizer.defaultPolicy = createDefaultPolicy() sanitizer.defaultPolicy = createDefaultPolicy()
sanitizer.descriptionPolicy = createRepoDescriptionPolicy()
for name, renderer := range renderers { for name, renderer := range renderers {
sanitizerRules := renderer.SanitizerRules() sanitizerRules := renderer.SanitizerRules()
@@ -161,6 +163,27 @@ func createDefaultPolicy() *bluemonday.Policy {
return 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) { func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) {
for _, rule := range rules { for _, rule := range rules {
if rule.AllowDataURIImages { 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. // Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string { func Sanitize(s string) string {
NewSanitizer() NewSanitizer()

View File

@@ -73,6 +73,28 @@ func Test_Sanitizer(t *testing.T) {
} }
} }
func TestDescriptionSanitizer(t *testing.T) {
NewSanitizer()
testCases := []string{
`<h1>Title</h1>`, `Title`,
`<img src='img.png' alt='image'>`, ``,
`<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`, `<span class="emoji" aria-label="thumbs up">THUMBS UP</span>`,
`<span style="color: red">Hello World</span>`, `<span>Hello World</span>`,
`<br>`, ``,
`<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>`, `<a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a>`,
`<mark>Important!</mark>`, `Important!`,
`<details>Click me! <summary>Nothing to see here.</summary></details>`, `Click me! Nothing to see here.`,
`<input type="hidden">`, ``,
`<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`, `<b>I</b> have a <i>strong</i> <strong>opinion</strong> about <em>this</em>.`,
`Provides alternative <code>wg(8)</code> tool`, `Provides alternative <code>wg(8)</code> tool`,
}
for i := 0; i < len(testCases); i += 2 {
assert.Equal(t, testCases[i+1], SanitizeDescription(testCases[i]))
}
}
func TestSanitizeNonEscape(t *testing.T) { func TestSanitizeNonEscape(t *testing.T) {
descStr := "<scrİpt>&lt;script&gt;alert(document.domain)&lt;/script&gt;</scrİpt>" descStr := "<scrİpt>&lt;script&gt;alert(document.domain)&lt;/script&gt;</scrİpt>"

View File

@@ -136,6 +136,10 @@ func (r *Route) Get(pattern string, h ...any) {
r.Methods("GET", pattern, h...) r.Methods("GET", pattern, h...)
} }
func (r *Route) Options(pattern string, h ...any) {
r.Methods("OPTIONS", pattern, h...)
}
// GetOptions delegate get and options method // GetOptions delegate get and options method
func (r *Route) GetOptions(pattern string, h ...any) { func (r *Route) GetOptions(pattern string, h ...any) {
r.Methods("GET,OPTIONS", pattern, h...) r.Methods("GET,OPTIONS", pattern, h...)

View File

@@ -3529,7 +3529,11 @@ runs.status = Status
runs.actors_no_select = All actors runs.actors_no_select = All actors
runs.status_no_select = All status runs.status_no_select = All status
runs.no_results = No results matched. runs.no_results = No results matched.
runs.no_workflows = There are no workflows yet.
runs.no_workflows.quick_start = Don't know how to start with Gitea Action? See <a target="_blank" rel="noopener noreferrer" href="%s">the quick start guide</a>.
runs.no_workflows.documentation = For more information on the Gitea Action, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
runs.no_runs = The workflow has no runs yet. runs.no_runs = The workflow has no runs yet.
runs.empty_commit_message = (empty commit message)
workflow.disable = Disable Workflow workflow.disable = Disable Workflow
workflow.disable_success = Workflow '%s' disabled successfully. workflow.disable_success = Workflow '%s' disabled successfully.

View File

@@ -70,6 +70,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@@ -314,7 +315,7 @@ func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) {
return return
} }
artifacts, err := actions.ListArtifactsByRunID(ctx, runID) artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{RunID: runID})
if err != nil { if err != nil {
log.Error("Error getting artifacts: %v", err) log.Error("Error getting artifacts: %v", err)
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.Error(http.StatusInternalServerError, err.Error())
@@ -376,7 +377,10 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) {
return return
} }
artifacts, err := actions.ListArtifactsByRunIDAndArtifactName(ctx, runID, itemPath) artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{
RunID: runID,
ArtifactName: itemPath,
})
if err != nil { if err != nil {
log.Error("Error getting artifacts: %v", err) log.Error("Error getting artifacts: %v", err)
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.Error(http.StatusInternalServerError, err.Error())

View File

@@ -13,6 +13,7 @@ import (
"time" "time"
"code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage" "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 { func mergeChunksForRun(ctx *ArtifactContext, st storage.ObjectStorage, runID int64, artifactName string) error {
// read all db artifacts by name // 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 { if err != nil {
return err return err
} }

View File

@@ -8,6 +8,7 @@ import (
"fmt" "fmt"
actions_model "code.gitea.io/gitea/models/actions" actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret" secret_model "code.gitea.io/gitea/models/secret"
actions_module "code.gitea.io/gitea/modules/actions" actions_module "code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
@@ -67,12 +68,12 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s
return secrets 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 { if err != nil {
log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err)
// go on // 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 { if err != nil {
log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err)
// go on // go on
@@ -94,13 +95,13 @@ func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map
variables := map[string]string{} variables := map[string]string{}
// Org / User level // 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 { if err != nil {
log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err) log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err)
} }
// Repo level // 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 { if err != nil {
log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err) 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...) 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 { if err != nil {
return nil, fmt.Errorf("FindRunJobs: %w", err) return nil, fmt.Errorf("FindRunJobs: %w", err)
} }

View File

@@ -2,7 +2,7 @@
// Copyright 2016 The Gitea Authors. All rights reserved. // Copyright 2016 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Package v1 Gitea API. // Package v1 Gitea API
// //
// This documentation describes the Gitea API. // This documentation describes the Gitea API.
// //

View File

@@ -8,6 +8,7 @@ import (
"strings" "strings"
activities_model "code.gitea.io/gitea/models/activities" 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/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/routers/api/v1/utils"
@@ -21,7 +22,17 @@ func NewAvailable(ctx *context.APIContext) {
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/NotificationCount" // "$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 { func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {

View File

@@ -9,6 +9,7 @@ import (
"time" "time"
activities_model "code.gitea.io/gitea/models/activities" 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/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
@@ -108,18 +109,18 @@ func ListRepoNotifications(ctx *context.APIContext) {
} }
opts.RepoID = ctx.Repo.Repository.ID 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 { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }
nl, err := activities_model.GetNotifications(ctx, opts) nl, err := db.Find[activities_model.Notification](ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }
err = nl.LoadAttributes(ctx) err = activities_model.NotificationList(nl).LoadAttributes(ctx)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
@@ -202,7 +203,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
log.Error("%v", opts.Status) log.Error("%v", opts.Status)
} }
nl, err := activities_model.GetNotifications(ctx, opts) nl, err := db.Find[activities_model.Notification](ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
activities_model "code.gitea.io/gitea/models/activities" 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/context"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/convert"
@@ -68,18 +69,18 @@ func ListNotifications(ctx *context.APIContext) {
return return
} }
totalCount, err := activities_model.CountNotifications(ctx, opts) totalCount, err := db.Count[activities_model.Notification](ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }
nl, err := activities_model.GetNotifications(ctx, opts) nl, err := db.Find[activities_model.Notification](ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
} }
err = nl.LoadAttributes(ctx) err = activities_model.NotificationList(nl).LoadAttributes(ctx)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
@@ -147,7 +148,7 @@ func ReadNotifications(ctx *context.APIContext) {
statuses := ctx.FormStrings("status-types") statuses := ctx.FormStrings("status-types")
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) 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 { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return

View File

@@ -7,6 +7,7 @@ import (
"errors" "errors"
"net/http" "net/http"
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret" secret_model "code.gitea.io/gitea/models/secret"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
@@ -48,13 +49,7 @@ func ListActionsSecrets(ctx *context.APIContext) {
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
} }
count, err := secret_model.CountSecrets(ctx, opts) secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
}
secrets, err := secret_model.FindSecrets(ctx, *opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return

View File

@@ -30,14 +30,9 @@ func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
UserID: u.ID, UserID: u.ID,
IncludePrivate: showPrivate, IncludePrivate: showPrivate,
} }
orgs, err := organization.FindOrgs(ctx, opts) orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "FindOrgs", err) ctx.Error(http.StatusInternalServerError, "db.FindAndCount[organization.Organization]", err)
return
}
maxResults, err := organization.CountOrgs(ctx, opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CountOrgs", err)
return return
} }

View File

@@ -7,6 +7,7 @@ package repo
import ( import (
"net/http" "net/http"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
"code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/models/webhook"
@@ -58,13 +59,7 @@ func ListHooks(ctx *context.APIContext) {
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
} }
count, err := webhook.CountWebhooksByOpts(ctx, opts) hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
}
hooks, err := webhook.ListWebhooksByOpts(ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return

View File

@@ -83,20 +83,14 @@ func ListDeployKeys(ctx *context.APIContext) {
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
opts := &asymkey_model.ListDeployKeysOptions{ opts := asymkey_model.ListDeployKeysOptions{
ListOptions: utils.GetListOptions(ctx), ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
KeyID: ctx.FormInt64("key_id"), KeyID: ctx.FormInt64("key_id"),
Fingerprint: ctx.FormString("fingerprint"), Fingerprint: ctx.FormString("fingerprint"),
} }
keys, err := asymkey_model.ListDeployKeys(ctx, opts) keys, count, err := db.FindAndCount[asymkey_model.DeployKey](ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
}
count, err := asymkey_model.CountDeployKeys(ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return

View File

@@ -12,6 +12,7 @@ import (
"strings" "strings"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web" "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)} opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)}
count, err := auth_model.CountAccessTokens(ctx, opts) tokens, count, err := db.FindAndCount[auth_model.AccessToken](ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
}
tokens, err := auth_model.ListAccessTokens(ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return
@@ -168,7 +164,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
tokenID, _ := strconv.ParseInt(token, 0, 64) tokenID, _ := strconv.ParseInt(token, 0, 64)
if tokenID == 0 { 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, Name: token,
UserID: ctx.ContextUser.ID, UserID: ctx.ContextUser.ID,
}) })
@@ -266,7 +262,10 @@ func ListOauth2Applications(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/OAuth2ApplicationList" // "$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 { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err) ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err)
return return

View File

@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -56,25 +57,26 @@ func listPublicKeys(ctx *context.APIContext, user *user_model.User) {
username := ctx.Params("username") username := ctx.Params("username")
if fingerprint != "" { if fingerprint != "" {
var userID int64 // Unrestricted
// Querying not just listing // Querying not just listing
if username != "" { if username != "" {
// Restrict to provided uid // Restrict to provided uid
keys, err = asymkey_model.SearchPublicKey(ctx, user.ID, fingerprint) userID = user.ID
} else {
// Unrestricted
keys, err = asymkey_model.SearchPublicKey(ctx, 0, fingerprint)
} }
keys, err = db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
OwnerID: userID,
Fingerprint: fingerprint,
})
count = len(keys) count = len(keys)
} else { } else {
total, err2 := asymkey_model.CountPublicKeys(ctx, user.ID) var total int64
if err2 != nil {
ctx.InternalServerError(err)
return
}
count = int(total)
// Use ListPublicKeys // 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 { if err != nil {

View File

@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -26,13 +27,7 @@ func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) {
OwnerID: owner.ID, OwnerID: owner.ID,
} }
count, err := webhook.CountWebhooksByOpts(ctx, opts) hooks, count, err := db.FindAndCount[webhook.Webhook](ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
}
hooks, err := webhook.ListWebhooksByOpts(ctx, opts)
if err != nil { if err != nil {
ctx.InternalServerError(err) ctx.InternalServerError(err)
return return

View File

@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@@ -33,7 +34,9 @@ func Applications(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings.applications") ctx.Data["Title"] = ctx.Tr("settings.applications")
ctx.Data["PageIsAdminApplications"] = true ctx.Data["PageIsAdminApplications"] = true
apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, 0) apps, err := db.Find[auth.OAuth2Application](ctx, auth.FindOAuth2ApplicationsOptions{
IsGlobal: true,
})
if err != nil { if err != nil {
ctx.ServerError("GetOAuth2ApplicationsByUserID", err) ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
return return

View File

@@ -13,6 +13,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/auth/pam" "code.gitea.io/gitea/modules/auth/pam"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -48,13 +49,12 @@ func Authentications(ctx *context.Context) {
ctx.Data["PageIsAdminAuthentications"] = true ctx.Data["PageIsAdminAuthentications"] = true
var err error 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 { if err != nil {
ctx.ServerError("auth.Sources", err) ctx.ServerError("auth.Sources", err)
return return
} }
ctx.Data["Total"] = auth.CountSources(ctx, auth.FindSourcesOptions{})
ctx.HTML(http.StatusOK, tplAuths) ctx.HTML(http.StatusOK, tplAuths)
} }
@@ -284,7 +284,7 @@ func NewAuthSourcePost(ctx *context.Context) {
ctx.RenderWithErr(err.Error(), tplAuthNew, form) ctx.RenderWithErr(err.Error(), tplAuthNew, form)
return 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 { if err != nil || len(existing) > 0 {
ctx.Data["Err_Type"] = true ctx.Data["Err_Type"] = true
ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form)

View File

@@ -93,7 +93,7 @@ func NewUser(ctx *context.Context) {
ctx.Data["login_type"] = "0-0" 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, IsActive: util.OptionalBoolTrue,
}) })
if err != nil { if err != nil {
@@ -114,7 +114,7 @@ func NewUserPost(ctx *context.Context) {
ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode ctx.Data["DefaultUserVisibilityMode"] = setting.Service.DefaultUserVisibilityMode
ctx.Data["AllowedUserVisibilityModes"] = setting.Service.AllowedUserVisibilityModesSlice.ToVisibleTypeSlice() 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, IsActive: util.OptionalBoolTrue,
}) })
if err != nil { if err != nil {
@@ -237,7 +237,7 @@ func prepareUserInfo(ctx *context.Context) *user_model.User {
ctx.Data["LoginSource"] = &auth.Source{} ctx.Data["LoginSource"] = &auth.Source{}
} }
sources, err := auth.FindSources(ctx, auth.FindSourcesOptions{}) sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{})
if err != nil { if err != nil {
ctx.ServerError("auth.Sources", err) ctx.ServerError("auth.Sources", err)
return nil return nil
@@ -296,7 +296,7 @@ func ViewUser(ctx *context.Context) {
ctx.Data["Emails"] = emails ctx.Data["Emails"] = emails
ctx.Data["EmailsTotal"] = len(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{ ListOptions: db.ListOptions{
ListAll: true, ListAll: true,
}, },

View File

@@ -33,6 +33,10 @@ func DummyOK(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
func DummyBadRequest(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusBadRequest)
}
func RobotsTxt(w http.ResponseWriter, req *http.Request) { func RobotsTxt(w http.ResponseWriter, req *http.Request) {
robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt") robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt")
if ok, _ := util.IsExist(robotsTxt); !ok { if ok, _ := util.IsExist(robotsTxt); !ok {

View File

@@ -11,6 +11,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
project_model "code.gitea.io/gitea/models/project" project_model "code.gitea.io/gitea/models/project"
attachment_model "code.gitea.io/gitea/models/repo" attachment_model "code.gitea.io/gitea/models/repo"
@@ -59,9 +60,12 @@ func Projects(ctx *context.Context) {
} else { } else {
projectType = project_model.TypeIndividual 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{
OwnerID: ctx.ContextUser.ID, ListOptions: db.ListOptions{
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum,
},
OwnerID: ctx.ContextUser.ID,
IsClosed: util.OptionalBoolOf(isShowClosed), IsClosed: util.OptionalBoolOf(isShowClosed),
OrderBy: project_model.GetSearchOrderByBySortType(sortType), OrderBy: project_model.GetSearchOrderByBySortType(sortType),
Type: projectType, Type: projectType,
@@ -72,7 +76,7 @@ func Projects(ctx *context.Context) {
return 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, OwnerID: ctx.ContextUser.ID,
IsClosed: util.OptionalBoolOf(!isShowClosed), IsClosed: util.OptionalBoolOf(!isShowClosed),
Type: projectType, Type: projectType,

View File

@@ -215,7 +215,7 @@ func Webhooks(ctx *context.Context) {
ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks" ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc") 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 { if err != nil {
ctx.ServerError("ListWebhooksByOpts", err) ctx.ServerError("ListWebhooksByOpts", err)
return return

View File

@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@@ -35,7 +36,9 @@ func Applications(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsApplications"] = 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 { if err != nil {
ctx.ServerError("GetOAuth2ApplicationsByUserID", err) ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
return return

View File

@@ -75,11 +75,10 @@ func List(ctx *context.Context) {
} }
// Get all runner labels // Get all runner labels
opts := actions_model.FindRunnerOptions{ runners, err := db.Find[actions_model.ActionRunner](ctx, actions_model.FindRunnerOptions{
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
WithAvailable: true, WithAvailable: true,
} })
runners, err := actions_model.FindRunners(ctx, opts)
if err != nil { if err != nil {
ctx.ServerError("FindRunners", err) ctx.ServerError("FindRunners", err)
return return
@@ -169,7 +168,7 @@ func List(ctx *context.Context) {
opts.Status = []actions_model.Status{actions_model.Status(status)} 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 { if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.Error(http.StatusInternalServerError, err.Error())
return return
@@ -179,7 +178,7 @@ func List(ctx *context.Context) {
run.Repo = ctx.Repo.Repository 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()) ctx.Error(http.StatusInternalServerError, err.Error())
return return
} }

View File

@@ -512,7 +512,7 @@ func ArtifactsView(ctx *context_module.Context) {
} }
for _, art := range artifacts { for _, art := range artifacts {
status := "completed" status := "completed"
if art.Status == int64(actions_model.ArtifactStatusExpired) { if art.Status == actions_model.ArtifactStatusExpired {
status = "expired" status = "expired"
} }
artifactsResponse.Artifacts = append(artifactsResponse.Artifacts, &ArtifactsViewItem{ artifactsResponse.Artifacts = append(artifactsResponse.Artifacts, &ArtifactsViewItem{
@@ -538,7 +538,10 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
return 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 { if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.Error(http.StatusInternalServerError, err.Error())
return return

View File

@@ -287,7 +287,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
Operation: operation, Operation: operation,
FromTreePath: ctx.Repo.TreePath, FromTreePath: ctx.Repo.TreePath,
TreePath: form.TreePath, TreePath: form.TreePath,
ContentReader: strings.NewReader(form.Content), ContentReader: strings.NewReader(strings.ReplaceAll(form.Content, "\r", "")),
}, },
}, },
Signoff: form.Signoff, Signoff: form.Signoff,

View File

@@ -569,9 +569,9 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
repoOwnerType = project_model.TypeOrganization repoOwnerType = project_model.TypeOrganization
} }
var err error var err error
projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
RepoID: repo.ID, RepoID: repo.ID,
Page: -1,
IsClosed: util.OptionalBoolFalse, IsClosed: util.OptionalBoolFalse,
Type: project_model.TypeRepository, Type: project_model.TypeRepository,
}) })
@@ -579,9 +579,9 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
ctx.ServerError("GetProjects", err) ctx.ServerError("GetProjects", err)
return return
} }
projects2, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ projects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
OwnerID: repo.OwnerID, OwnerID: repo.OwnerID,
Page: -1,
IsClosed: util.OptionalBoolFalse, IsClosed: util.OptionalBoolFalse,
Type: repoOwnerType, Type: repoOwnerType,
}) })
@@ -592,9 +592,9 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
ctx.Data["OpenProjects"] = append(projects, projects2...) ctx.Data["OpenProjects"] = append(projects, projects2...)
projects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
RepoID: repo.ID, RepoID: repo.ID,
Page: -1,
IsClosed: util.OptionalBoolTrue, IsClosed: util.OptionalBoolTrue,
Type: project_model.TypeRepository, Type: project_model.TypeRepository,
}) })
@@ -602,9 +602,9 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
ctx.ServerError("GetProjects", err) ctx.ServerError("GetProjects", err)
return return
} }
projects2, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ projects2, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptionsAll,
OwnerID: repo.OwnerID, OwnerID: repo.OwnerID,
Page: -1,
IsClosed: util.OptionalBoolTrue, IsClosed: util.OptionalBoolTrue,
Type: repoOwnerType, Type: repoOwnerType,
}) })

View File

@@ -10,6 +10,7 @@ import (
"net/url" "net/url"
"strings" "strings"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
project_model "code.gitea.io/gitea/models/project" project_model "code.gitea.io/gitea/models/project"
@@ -71,9 +72,12 @@ func Projects(ctx *context.Context) {
total = repo.NumClosedProjects 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{
RepoID: repo.ID, ListOptions: db.ListOptions{
PageSize: setting.UI.IssuePagingNum,
Page: page, Page: page,
},
RepoID: repo.ID,
IsClosed: util.OptionalBoolOf(isShowClosed), IsClosed: util.OptionalBoolOf(isShowClosed),
OrderBy: project_model.GetSearchOrderByBySortType(sortType), OrderBy: project_model.GetSearchOrderByBySortType(sortType),
Type: project_model.TypeRepository, Type: project_model.TypeRepository,

View File

@@ -22,7 +22,7 @@ func DeployKeys(ctx *context.Context) {
ctx.Data["PageIsSettingsKeys"] = true ctx.Data["PageIsSettingsKeys"] = true
ctx.Data["DisableSSH"] = setting.SSH.Disabled 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 { if err != nil {
ctx.ServerError("ListDeployKeys", err) ctx.ServerError("ListDeployKeys", err)
return return
@@ -39,7 +39,7 @@ func DeployKeysPost(ctx *context.Context) {
ctx.Data["PageIsSettingsKeys"] = true ctx.Data["PageIsSettingsKeys"] = true
ctx.Data["DisableSSH"] = setting.SSH.Disabled 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 { if err != nil {
ctx.ServerError("ListDeployKeys", err) ctx.ServerError("ListDeployKeys", err)
return return

View File

@@ -12,6 +12,7 @@ import (
"path" "path"
"strings" "strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
user_model "code.gitea.io/gitea/models/user" 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["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.com/usage/webhooks") 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 { if err != nil {
ctx.ServerError("GetWebhooksByRepoID", err) ctx.ServerError("GetWebhooksByRepoID", err)
return return

View File

@@ -17,18 +17,13 @@ import (
// RunnersList prepares data for runners list // RunnersList prepares data for runners list
func RunnersList(ctx *context.Context, opts actions_model.FindRunnerOptions) { 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 { if err != nil {
ctx.ServerError("CountRunners", err) ctx.ServerError("CountRunners", err)
return return
} }
runners, err := actions_model.FindRunners(ctx, opts) if err := actions_model.RunnerList(runners).LoadAttributes(ctx); err != nil {
if err != nil {
ctx.ServerError("FindRunners", err)
return
}
if err := runners.LoadAttributes(ctx); err != nil {
ctx.ServerError("LoadAttributes", err) ctx.ServerError("LoadAttributes", err)
return return
} }
@@ -89,18 +84,13 @@ func RunnerDetails(ctx *context.Context, page int, runnerID, ownerID, repoID int
RunnerID: runner.ID, RunnerID: runner.ID,
} }
count, err := actions_model.CountTasks(ctx, opts) tasks, count, err := db.FindAndCount[actions_model.ActionTask](ctx, opts)
if err != nil { if err != nil {
ctx.ServerError("CountTasks", err) ctx.ServerError("CountTasks", err)
return return
} }
tasks, err := actions_model.FindTasks(ctx, opts) if err = actions_model.TaskList(tasks).LoadAttributes(ctx); err != nil {
if err != nil {
ctx.ServerError("FindTasks", err)
return
}
if err = tasks.LoadAttributes(ctx); err != nil {
ctx.ServerError("TasksLoadAttributes", err) ctx.ServerError("TasksLoadAttributes", err)
return return
} }

View File

@@ -18,7 +18,7 @@ import (
) )
func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) { 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, OwnerID: ownerID,
RepoID: repoID, RepoID: repoID,
}) })

View File

@@ -4,6 +4,7 @@
package secrets package secrets
import ( import (
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret" secret_model "code.gitea.io/gitea/models/secret"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@@ -14,7 +15,7 @@ import (
) )
func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) { 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 { if err != nil {
ctx.ServerError("FindSecrets", err) ctx.ServerError("FindSecrets", err)
return return

View File

@@ -60,7 +60,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
} }
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) 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, UserID: ctx.ContextUser.ID,
IncludePrivate: showPrivate, IncludePrivate: showPrivate,
}) })
@@ -141,7 +141,7 @@ func LoadHeaderCount(ctx *context.Context) error {
} else { } else {
projectType = project_model.TypeIndividual 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, OwnerID: ctx.ContextUser.ID,
IsClosed: util.OptionalBoolOf(false), IsClosed: util.OptionalBoolOf(false),
Type: projectType, Type: projectType,

View File

@@ -759,7 +759,9 @@ func loadRepoByIDs(ctx *context.Context, ctxUser *user_model.User, issueCountByR
// ShowSSHKeys output all the ssh keys of user by uid // ShowSSHKeys output all the ssh keys of user by uid
func ShowSSHKeys(ctx *context.Context) { 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 { if err != nil {
ctx.ServerError("ListPublicKeys", err) ctx.ServerError("ListPublicKeys", err)
return return

View File

@@ -42,7 +42,10 @@ func GetNotificationCount(ctx *context.Context) {
} }
ctx.Data["NotificationUnreadCount"] = func() int64 { 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 != nil {
if err != goctx.Canceled { if err != goctx.Canceled {
log.Error("Unable to GetNotificationCount for user:%-v: %v", ctx.Doer, err) 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 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 { if err != nil {
ctx.ServerError("ErrGetNotificationCount", err) ctx.ServerError("ErrGetNotificationCount", err)
return return
@@ -103,12 +109,21 @@ func getNotifications(ctx *context.Context) {
} }
statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned} 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 { if err != nil {
ctx.ServerError("ErrNotificationsForUser", err) ctx.ServerError("db.Find[activities_model.Notification]", err)
return return
} }
notifications := activities_model.NotificationList(nls)
failCount := 0 failCount := 0
repos, failures, err := notifications.LoadRepos(ctx) repos, failures, err := notifications.LoadRepos(ctx)
@@ -409,5 +424,15 @@ func NotificationWatching(ctx *context.Context) {
// NewAvailable returns the notification counts // NewAvailable returns the notification counts
func NewAvailable(ctx *context.Context) { 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})
} }

View File

@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
auth_model "code.gitea.io/gitea/models/auth" 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/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@@ -88,7 +89,7 @@ func DeleteApplication(ctx *context.Context) {
func loadApplicationsData(ctx *context.Context) { func loadApplicationsData(ctx *context.Context) {
ctx.Data["AccessTokenScopePublicOnly"] = auth_model.AccessTokenScopePublicOnly 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 { if err != nil {
ctx.ServerError("ListAccessTokens", err) ctx.ServerError("ListAccessTokens", err)
return return
@@ -97,7 +98,9 @@ func loadApplicationsData(ctx *context.Context) {
ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin
if setting.OAuth2.Enable { 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 { if err != nil {
ctx.ServerError("GetOAuth2ApplicationsByUserID", err) ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
return return

View File

@@ -260,7 +260,10 @@ func DeleteKey(ctx *context.Context) {
} }
func loadKeysData(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 { if err != nil {
ctx.ServerError("ListPublicKeys", err) ctx.ServerError("ListPublicKeys", err)
return return

View File

@@ -214,16 +214,12 @@ func Organization(ctx *context.Context) {
opts.Page = 1 opts.Page = 1
} }
orgs, err := organization.FindOrgs(ctx, opts) orgs, total, err := db.FindAndCount[organization.Organization](ctx, opts)
if err != nil { if err != nil {
ctx.ServerError("FindOrgs", err) ctx.ServerError("FindOrgs", err)
return return
} }
total, err := organization.CountOrgs(ctx, opts)
if err != nil {
ctx.ServerError("CountOrgs", err)
return
}
ctx.Data["Orgs"] = orgs ctx.Data["Orgs"] = orgs
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)

View File

@@ -9,6 +9,7 @@ import (
"sort" "sort"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -68,14 +69,17 @@ func loadSecurityData(ctx *context.Context) {
} }
ctx.Data["WebAuthnCredentials"] = credentials 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 { if err != nil {
ctx.ServerError("ListAccessTokens", err) ctx.ServerError("ListAccessTokens", err)
return return
} }
ctx.Data["Tokens"] = tokens 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 { if err != nil {
ctx.ServerError("ListAccountLinks", err) ctx.ServerError("ListAccountLinks", err)
return return
@@ -107,7 +111,7 @@ func loadSecurityData(ctx *context.Context) {
} }
ctx.Data["AccountLinks"] = sources 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, IsActive: util.OptionalBoolNone,
LoginType: auth_model.OAuth2, LoginType: auth_model.OAuth2,
}) })

View File

@@ -6,6 +6,7 @@ package setting
import ( import (
"net/http" "net/http"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "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["BaseLinkNew"] = setting.AppSubURL + "/user/settings/hooks"
ctx.Data["Description"] = ctx.Tr("settings.hooks.desc") 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 { if err != nil {
ctx.ServerError("ListWebhooksByOpts", err) ctx.ServerError("ListWebhooksByOpts", err)
return return

View File

@@ -533,8 +533,10 @@ func registerRoutes(m *web.Route) {
m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth)
}, ignSignInAndCsrf, reqSignIn) }, ignSignInAndCsrf, reqSignIn)
m.Get("/login/oauth/userinfo", ignSignInAndCsrf, auth.InfoOAuth) m.Get("/login/oauth/userinfo", ignSignInAndCsrf, auth.InfoOAuth)
m.Options("/login/oauth/access_token", CorsHandler(), misc.DummyBadRequest)
m.Post("/login/oauth/access_token", CorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) m.Post("/login/oauth/access_token", CorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth)
m.Get("/login/oauth/keys", ignSignInAndCsrf, auth.OIDCKeys) m.Get("/login/oauth/keys", ignSignInAndCsrf, auth.OIDCKeys)
m.Options("/login/oauth/introspect", CorsHandler(), misc.DummyBadRequest)
m.Post("/login/oauth/introspect", CorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) m.Post("/login/oauth/introspect", CorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth)
m.Group("/user/settings", func() { m.Group("/user/settings", func() {

View File

@@ -33,7 +33,7 @@ func StopEndlessTasks(ctx context.Context) error {
} }
func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) 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 { if err != nil {
return fmt.Errorf("find tasks: %w", err) 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 // 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 { 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}, Statuses: []actions_model.Status{actions_model.StatusWaiting, actions_model.StatusBlocked},
UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.AbandonedJobTimeout).Unix()), UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.AbandonedJobTimeout).Unix()),
}) })

View File

@@ -44,7 +44,7 @@ func jobEmitterQueueHandler(items ...*jobUpdate) []*jobUpdate {
} }
func checkJobsOfRun(ctx context.Context, runID int64) error { 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 { if err != nil {
return err return err
} }

View File

@@ -11,6 +11,7 @@ import (
"strings" "strings"
actions_model "code.gitea.io/gitea/models/actions" actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
access_model "code.gitea.io/gitea/models/perm/access" access_model "code.gitea.io/gitea/models/perm/access"
@@ -298,7 +299,7 @@ func handleWorkflows(
continue 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 { if err != nil {
log.Error("FindRunJobs: %v", err) log.Error("FindRunJobs: %v", err)
continue 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 // 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, RepoID: repo.ID,
TriggerUserID: user.ID, TriggerUserID: user.ID,
Approved: true, Approved: true,
@@ -408,7 +409,7 @@ func handleSchedules(
return nil 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) log.Error("CountSchedules: %v", err)
return err return err
} else if count > 0 { } else if count > 0 {

View File

@@ -67,7 +67,10 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib
for i, kase := range testCases { for i, kase := range testCases {
s.ID = int64(i) + 20 s.ID = int64(i) + 20
asymkey_model.AddPublicKeysBySource(db.DefaultContext, user, s, []string{kase.keyString}) 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) assert.NoError(t, err)
if err != nil { if err != nil {
continue continue

Some files were not shown because too many files have changed in this diff Show More