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
steps:
- uses: actions/checkout@v4
# FIXME: https://github.com/jlumbroso/free-disk-space/issues/17
- name: same as 'large-packages' but without 'google-cloud-sdk'
shell: bash
run: |
sudo apt-get update
sudo apt-get remove -y '^dotnet-.*' || true
sudo apt-get remove -y '^llvm-.*' || true
sudo apt-get remove -y 'php.*' || true
sudo apt-get remove -y '^mongodb-.*' || true
sudo apt-get remove -y '^mysql-.*' || true
sudo apt-get remove -y azure-cli google-chrome-stable firefox powershell mono-devel libgl1-mesa-dri || true
sudo apt-get autoremove -y
sudo apt-get clean
env:
DEBIAN_FRONTEND: noninteractive
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:

View File

@@ -10,7 +10,7 @@
<img src="https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main">
</a>
<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 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">

View File

@@ -9,6 +9,7 @@ import (
"text/tabwriter"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
auth_service "code.gitea.io/gitea/services/auth"
"github.com/urfave/cli/v2"
@@ -62,7 +63,7 @@ func runListAuth(c *cli.Context) error {
return err
}
authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{})
authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{})
if err != nil {
return err
}

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.
- SMTPS connection (SMTP over TLS) via the default port 465. Connection to the server use TLS from the beginning.
- Forced SMTPS connection with `IS_TLS_ENABLED=true`. (These are both known as Implicit TLS.)
- Forced SMTPS connection with `PROTOCOL=smtps`. (These are both known as Implicit TLS.)
This is due to protections imposed by the Go internal libraries against STRIPTLS attacks.
Note that Implicit TLS is recommended by [RFC8314](https://tools.ietf.org/html/rfc8314#section-3) since 2018.

View File

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

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:
- [Gogs version 0.9.146 or less](installation/upgrade-from-gogs.md)
- [Gogs version 0.11.46.0418](https://github.com/go-gitea/gitea/issues/4286)
To migrate from GitHub to Gitea, you can use Gitea's built-in migration form.

View File

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

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`.
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

View File

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

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.
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
[actions]

View File

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

View File

@@ -198,7 +198,7 @@ administrative user.
field is set to `mail.com`, then Gitea will expect the `user email` field
for an authenticated GIT instance to be `gituser@mail.com`.[^2]
**Note**: PAM support is added via [build-time flags](installation/install-from-source.md#build),
**Note**: PAM support is added via [build-time flags](installation/from-source.md#build),
and the official binaries provided do not have this enabled. PAM requires that
the necessary libpam dynamic library be available and the necessary PAM
development headers be accessible to the compiler.

View File

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

View File

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

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.
func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string) error {
// Find all runs in the specified repository, reference, and workflow with statuses 'Running' or 'Waiting'.
runs, total, err := FindRuns(ctx, FindRunOptions{
runs, total, err := db.FindAndCount[ActionRun](ctx, FindRunOptions{
RepoID: repoID,
Ref: ref,
WorkflowID: workflowID,
@@ -188,7 +188,7 @@ func CancelRunningJobs(ctx context.Context, repoID int64, ref, workflowID string
// Iterate over each found run and cancel its associated jobs.
for _, run := range runs {
// Find all jobs associated with the current run.
jobs, _, err := FindRunJobs(ctx, FindRunJobOptions{
jobs, err := db.Find[ActionRunJob](ctx, FindRunJobOptions{
RunID: run.ID,
})
if err != nil {

View File

@@ -61,7 +61,7 @@ type FindRunJobOptions struct {
UpdatedBefore timeutil.TimeStamp
}
func (opts FindRunJobOptions) toConds() builder.Cond {
func (opts FindRunJobOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RunID > 0 {
cond = cond.And(builder.Eq{"run_id": opts.RunID})
@@ -83,17 +83,3 @@ func (opts FindRunJobOptions) toConds() builder.Cond {
}
return cond
}
func FindRunJobs(ctx context.Context, opts FindRunJobOptions) (ActionJobList, int64, error) {
e := db.GetEngine(ctx).Where(opts.toConds())
if opts.PageSize > 0 && opts.Page >= 1 {
e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
var tasks ActionJobList
total, err := e.FindAndCount(&tasks)
return tasks, total, err
}
func CountRunJobs(ctx context.Context, opts FindRunJobOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionRunJob))
}

View File

@@ -75,7 +75,7 @@ type FindRunOptions struct {
Status []Status
}
func (opts FindRunOptions) toConds() builder.Cond {
func (opts FindRunOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -101,18 +101,8 @@ func (opts FindRunOptions) toConds() builder.Cond {
return cond
}
func FindRuns(ctx context.Context, opts FindRunOptions) (RunList, int64, error) {
e := db.GetEngine(ctx).Where(opts.toConds())
if opts.PageSize > 0 && opts.Page >= 1 {
e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
var runs RunList
total, err := e.Desc("id").FindAndCount(&runs)
return runs, total, err
}
func CountRuns(ctx context.Context, opts FindRunOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionRun))
func (opts FindRunOptions) ToOrders() string {
return "`id` DESC"
}
type StatusInfo struct {

View File

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

View File

@@ -67,7 +67,7 @@ type FindScheduleOptions struct {
OwnerID int64
}
func (opts FindScheduleOptions) toConds() builder.Cond {
func (opts FindScheduleOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
@@ -79,16 +79,6 @@ func (opts FindScheduleOptions) toConds() builder.Cond {
return cond
}
func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) {
e := db.GetEngine(ctx).Where(opts.toConds())
if !opts.ListAll && opts.PageSize > 0 && opts.Page >= 1 {
e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
var schedules ScheduleList
total, err := e.Desc("id").FindAndCount(&schedules)
return schedules, total, err
}
func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule))
func (opts FindScheduleOptions) ToOrders() string {
return "`id` DESC"
}

View File

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

View File

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

View File

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

View File

@@ -56,7 +56,7 @@ type FindVariablesOpts struct {
RepoID int64
}
func (opts *FindVariablesOpts) toConds() builder.Cond {
func (opts FindVariablesOpts) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
@@ -67,15 +67,6 @@ func (opts *FindVariablesOpts) toConds() builder.Cond {
return cond
}
func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariable, error) {
var variables []*ActionVariable
sess := db.GetEngine(ctx)
if opts.PageSize != 0 {
sess = db.SetSessionPagination(sess, &opts.ListOptions)
}
return variables, sess.Where(opts.toConds()).Find(&variables)
}
func GetVariableByID(ctx context.Context, variableID int64) (*ActionVariable, error) {
var variable ActionVariable
has, err := db.GetEngine(ctx).Where("id=?", variableID).Get(&variable)

View File

@@ -22,7 +22,6 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
"xorm.io/xorm"
)
type (
@@ -93,7 +92,7 @@ type FindNotificationOptions struct {
}
// ToCond will convert each condition into a xorm-Cond
func (opts *FindNotificationOptions) ToCond() builder.Cond {
func (opts FindNotificationOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.UserID != 0 {
cond = cond.And(builder.Eq{"notification.user_id": opts.UserID})
@@ -105,8 +104,12 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID})
}
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))
}
}
if len(opts.Source) > 0 {
cond = cond.And(builder.In("notification.source", opts.Source))
}
@@ -119,24 +122,8 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
return cond
}
// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required
func (opts *FindNotificationOptions) ToSession(ctx context.Context) *xorm.Session {
sess := db.GetEngine(ctx).Where(opts.ToCond())
if opts.Page != 0 {
sess = db.SetSessionPagination(sess, opts)
}
return sess
}
// GetNotifications returns all notifications that fit to the given options.
func GetNotifications(ctx context.Context, options *FindNotificationOptions) (nl NotificationList, err error) {
err = options.ToSession(ctx).OrderBy("notification.updated_unix DESC").Find(&nl)
return nl, err
}
// CountNotifications count all notifications that fit to the given options and ignore pagination.
func CountNotifications(ctx context.Context, opts *FindNotificationOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.ToCond()).Count(&Notification{})
func (opts FindNotificationOptions) ToOrders() string {
return "notification.updated_unix DESC"
}
// CreateRepoTransferNotification creates notification for the user a repository was transferred to
@@ -192,7 +179,9 @@ func CreateOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
// init
var toNotify container.Set[int64]
notifications, err := getNotificationsByIssueID(ctx, issueID)
notifications, err := db.Find[Notification](ctx, FindNotificationOptions{
IssueID: issueID,
})
if err != nil {
return err
}
@@ -273,23 +262,6 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
return nil
}
func getNotificationsByIssueID(ctx context.Context, issueID int64) (notifications []*Notification, err error) {
err = db.GetEngine(ctx).
Where("issue_id = ?", issueID).
Find(&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 {
notification := &Notification{
UserID: userID,
@@ -341,35 +313,6 @@ func GetIssueNotification(ctx context.Context, userID, issueID int64) (*Notifica
return notification, err
}
// NotificationsForUser returns notifications for a given user and status
func NotificationsForUser(ctx context.Context, user *user_model.User, statuses []NotificationStatus, page, perPage int) (notifications NotificationList, err error) {
if len(statuses) == 0 {
return nil, nil
}
sess := db.GetEngine(ctx).
Where("user_id = ?", user.ID).
In("status", statuses).
OrderBy("updated_unix DESC")
if page > 0 && perPage > 0 {
sess.Limit(perPage, (page-1)*perPage)
}
err = sess.Find(&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
func (n *Notification) LoadAttributes(ctx context.Context) (err error) {
if err = n.loadRepo(ctx); err != nil {
@@ -481,17 +424,47 @@ func (n *Notification) APIURL() string {
return setting.AppURL + "api/v1/notifications/threads/" + strconv.FormatInt(n.ID, 10)
}
func notificationExists(notifications []*Notification, issueID, userID int64) bool {
for _, notification := range notifications {
if notification.IssueID == issueID && notification.UserID == userID {
return true
}
}
return false
}
// UserIDCount is a simple coalition of UserID and Count
type UserIDCount struct {
UserID int64
Count int64
}
// GetUIDsAndNotificationCounts between the two provided times
func GetUIDsAndNotificationCounts(ctx context.Context, since, until timeutil.TimeStamp) ([]UserIDCount, error) {
sql := `SELECT user_id, count(*) AS count FROM notification ` +
`WHERE user_id IN (SELECT user_id FROM notification WHERE updated_unix >= ? AND ` +
`updated_unix < ?) AND status = ? GROUP BY user_id`
var res []UserIDCount
return res, db.GetEngine(ctx).SQL(sql, since, until, NotificationStatusUnread).Find(&res)
}
// NotificationList contains a list of notifications
type NotificationList []*Notification
// LoadAttributes load Repo Issue User and Comment if not loaded
func (nl NotificationList) LoadAttributes(ctx context.Context) error {
var err error
for i := 0; i < len(nl); i++ {
err = nl[i].LoadAttributes(ctx)
if err != nil && !issues_model.IsErrCommentNotExist(err) {
if _, _, err := nl.LoadRepos(ctx); err != nil {
return err
}
if _, err := nl.LoadIssues(ctx); err != nil {
return err
}
if _, err := nl.LoadUsers(ctx); err != nil {
return err
}
if _, err := nl.LoadComments(ctx); err != nil {
return err
}
return nil
}
@@ -665,6 +638,68 @@ func (nl NotificationList) getPendingCommentIDs() []int64 {
return ids.Values()
}
func (nl NotificationList) getUserIDs() []int64 {
ids := make(container.Set[int64], len(nl))
for _, notification := range nl {
if notification.UserID == 0 || notification.User != nil {
continue
}
ids.Add(notification.UserID)
}
return ids.Values()
}
// LoadUsers loads users from database
func (nl NotificationList) LoadUsers(ctx context.Context) ([]int, error) {
if len(nl) == 0 {
return []int{}, nil
}
userIDs := nl.getUserIDs()
users := make(map[int64]*user_model.User, len(userIDs))
left := len(userIDs)
for left > 0 {
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
rows, err := db.GetEngine(ctx).
In("id", userIDs[:limit]).
Rows(new(user_model.User))
if err != nil {
return nil, err
}
for rows.Next() {
var user user_model.User
err = rows.Scan(&user)
if err != nil {
rows.Close()
return nil, err
}
users[user.ID] = &user
}
_ = rows.Close()
left -= limit
userIDs = userIDs[limit:]
}
failures := []int{}
for i, notification := range nl {
if notification.UserID > 0 && notification.User == nil && users[notification.UserID] != nil {
notification.User = users[notification.UserID]
if notification.User == nil {
log.Error("Notification[%d]: UserID[%d] failed to load", notification.ID, notification.UserID)
failures = append(failures, i)
continue
}
}
}
return failures, nil
}
// LoadComments loads comments from database
func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
if len(nl) == 0 {
@@ -717,30 +752,6 @@ func (nl NotificationList) LoadComments(ctx context.Context) ([]int, error) {
return failures, nil
}
// GetNotificationCount returns the notification count for user
func GetNotificationCount(ctx context.Context, user *user_model.User, status NotificationStatus) (count int64, err error) {
count, err = db.GetEngine(ctx).
Where("user_id = ?", user.ID).
And("status = ?", status).
Count(&Notification{})
return count, err
}
// UserIDCount is a simple coalition of UserID and Count
type UserIDCount struct {
UserID int64
Count int64
}
// GetUIDsAndNotificationCounts between the two provided times
func GetUIDsAndNotificationCounts(ctx context.Context, since, until timeutil.TimeStamp) ([]UserIDCount, error) {
sql := `SELECT user_id, count(*) AS count FROM notification ` +
`WHERE user_id IN (SELECT user_id FROM notification WHERE updated_unix >= ? AND ` +
`updated_unix < ?) AND status = ? GROUP BY user_id`
var res []UserIDCount
return res, db.GetEngine(ctx).SQL(sql, since, until, NotificationStatusUnread).Find(&res)
}
// SetIssueReadBy sets issue to be read by given user.
func SetIssueReadBy(ctx context.Context, issueID, userID int64) error {
if err := issues_model.UpdateIssueUserByRead(ctx, userID, issueID); err != nil {

View File

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

View File

@@ -52,7 +52,7 @@ type IssueByRepositoryCount struct {
func GetStatistic(ctx context.Context) (stats Statistic) {
e := db.GetEngine(ctx)
stats.Counter.User = user_model.CountUsers(ctx, nil)
stats.Counter.Org, _ = organization.CountOrgs(ctx, organization.FindOrgOptions{IncludePrivate: true})
stats.Counter.Org, _ = db.Count[organization.Organization](ctx, organization.FindOrgOptions{IncludePrivate: true})
stats.Counter.PublicKey, _ = e.Count(new(asymkey_model.PublicKey))
stats.Counter.Repo, _ = repo_model.CountRepositories(ctx, repo_model.CountRepositoryOptions{})
stats.Counter.Watch, _ = e.Count(new(repo_model.Watch))
@@ -102,7 +102,7 @@ func GetStatistic(ctx context.Context) (stats Statistic) {
stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
stats.Counter.Release, _ = e.Count(new(repo_model.Release))
stats.Counter.AuthSource = auth.CountSources(ctx, auth.FindSourcesOptions{})
stats.Counter.AuthSource, _ = db.Count[auth.Source](ctx, auth.FindSourcesOptions{})
stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone))
stats.Counter.Label, _ = e.Count(new(issues_model.Label))

View File

@@ -179,45 +179,33 @@ func SearchPublicKeyByContentExact(ctx context.Context, content string) (*Public
return key, nil
}
// SearchPublicKey returns a list of public keys matching the provided arguments.
func SearchPublicKey(ctx context.Context, uid int64, fingerprint string) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 5)
type FindPublicKeyOptions struct {
db.ListOptions
OwnerID int64
Fingerprint string
KeyTypes []KeyType
NotKeytype KeyType
LoginSourceID int64
}
func (opts FindPublicKeyOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if uid != 0 {
cond = cond.And(builder.Eq{"owner_id": uid})
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
if fingerprint != "" {
cond = cond.And(builder.Eq{"fingerprint": fingerprint})
if opts.Fingerprint != "" {
cond = cond.And(builder.Eq{"fingerprint": opts.Fingerprint})
}
return keys, db.GetEngine(ctx).Where(cond).Find(&keys)
}
// ListPublicKeys returns a list of public keys belongs to given user.
func ListPublicKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*PublicKey, error) {
sess := db.GetEngine(ctx).Where("owner_id = ? AND type != ?", uid, KeyTypePrincipal)
if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions)
keys := make([]*PublicKey, 0, listOptions.PageSize)
return keys, sess.Find(&keys)
if len(opts.KeyTypes) > 0 {
cond = cond.And(builder.In("type", opts.KeyTypes))
}
keys := make([]*PublicKey, 0, 5)
return keys, sess.Find(&keys)
}
// CountPublicKeys count public keys a user has
func CountPublicKeys(ctx context.Context, userID int64) (int64, error) {
sess := db.GetEngine(ctx).Where("owner_id = ? AND type != ?", userID, KeyTypePrincipal)
return sess.Count(&PublicKey{})
}
// ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source.
func ListPublicKeysBySource(ctx context.Context, uid, authSourceID int64) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 5)
return keys, db.GetEngine(ctx).
Where("owner_id = ? AND login_source_id = ?", uid, authSourceID).
Find(&keys)
if opts.NotKeytype > 0 {
cond = cond.And(builder.Neq{"type": opts.NotKeytype})
}
if opts.LoginSourceID > 0 {
cond = cond.And(builder.Eq{"login_source_id": opts.LoginSourceID})
}
return cond
}
// UpdatePublicKeyUpdated updates public key use time.
@@ -394,7 +382,10 @@ func SynchronizePublicKeys(ctx context.Context, usr *user_model.User, s *auth.So
// Get Public Keys from DB with current LDAP source
var giteaKeys []string
keys, err := ListPublicKeysBySource(ctx, usr.ID, s.ID)
keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{
OwnerID: usr.ID,
LoginSourceID: s.ID,
})
if err != nil {
log.Error("synchronizePublicKeys[%s]: Error listing Public SSH Keys for user %s: %v", s.Name, usr.Name, err)
}

View File

@@ -21,7 +21,10 @@ import (
func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *user_model.User) *CommitVerification {
// Now try to associate the signature with the committer, if present
if committer.ID != 0 {
keys, err := ListPublicKeys(ctx, committer.ID, db.ListOptions{})
keys, err := db.Find[PublicKey](ctx, FindPublicKeyOptions{
OwnerID: committer.ID,
NotKeytype: KeyTypePrincipal,
})
if err != nil { // Skipping failed to get ssh keys of user
log.Error("ListPublicKeys: %v", err)
return &CommitVerification{

View File

@@ -210,7 +210,7 @@ type ListDeployKeysOptions struct {
Fingerprint string
}
func (opt ListDeployKeysOptions) toCond() builder.Cond {
func (opt ListDeployKeysOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opt.RepoID != 0 {
cond = cond.And(builder.Eq{"repo_id": opt.RepoID})
@@ -223,23 +223,3 @@ func (opt ListDeployKeysOptions) toCond() builder.Cond {
}
return cond
}
// ListDeployKeys returns a list of deploy keys matching the provided arguments.
func ListDeployKeys(ctx context.Context, opts *ListDeployKeysOptions) ([]*DeployKey, error) {
sess := db.GetEngine(ctx).Where(opts.toCond())
if opts.Page != 0 {
sess = db.SetSessionPagination(sess, opts)
keys := make([]*DeployKey, 0, opts.PageSize)
return keys, sess.Find(&keys)
}
keys := make([]*DeployKey, 0, 5)
return keys, sess.Find(&keys)
}
// CountDeployKeys returns count deploy keys matching the provided arguments.
func CountDeployKeys(ctx context.Context, opts *ListDeployKeysOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toCond()).Count(&DeployKey{})
}

View File

@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/util"
lru "github.com/hashicorp/golang-lru/v2"
"xorm.io/builder"
)
// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
@@ -201,25 +202,18 @@ type ListAccessTokensOptions struct {
UserID int64
}
// ListAccessTokens returns a list of access tokens belongs to given user.
func ListAccessTokens(ctx context.Context, opts ListAccessTokensOptions) ([]*AccessToken, error) {
sess := db.GetEngine(ctx).Where("uid=?", opts.UserID)
if len(opts.Name) != 0 {
sess = sess.Where("name=?", opts.Name)
func (opts ListAccessTokensOptions) ToConds() builder.Cond {
cond := builder.NewCond()
// user id is required, otherwise it will return all result which maybe a possible bug
cond = cond.And(builder.Eq{"uid": opts.UserID})
if len(opts.Name) > 0 {
cond = cond.And(builder.Eq{"name": opts.Name})
}
return cond
}
sess = sess.Desc("created_unix")
if opts.Page != 0 {
sess = db.SetSessionPagination(sess, &opts)
tokens := make([]*AccessToken, 0, opts.PageSize)
return tokens, sess.Find(&tokens)
}
tokens := make([]*AccessToken, 0, 5)
return tokens, sess.Find(&tokens)
func (opts ListAccessTokensOptions) ToOrders() string {
return "created_unix DESC"
}
// UpdateAccessToken updates information of access token.
@@ -228,15 +222,6 @@ func UpdateAccessToken(ctx context.Context, t *AccessToken) error {
return err
}
// CountAccessTokens count access tokens belongs to given user by options
func CountAccessTokens(ctx context.Context, opts ListAccessTokensOptions) (int64, error) {
sess := db.GetEngine(ctx).Where("uid=?", opts.UserID)
if len(opts.Name) != 0 {
sess = sess.Where("name=?", opts.Name)
}
return sess.Count(&AccessToken{})
}
// DeleteAccessTokenByID deletes access token by given ID.
func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error {
cnt, err := db.GetEngine(ctx).ID(id).Delete(&AccessToken{

View File

@@ -85,7 +85,7 @@ func TestGetAccessTokenBySHA(t *testing.T) {
func TestListAccessTokens(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
tokens, err := auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 1})
tokens, err := db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 1})
assert.NoError(t, err)
if assert.Len(t, tokens, 2) {
assert.Equal(t, int64(1), tokens[0].UID)
@@ -94,14 +94,14 @@ func TestListAccessTokens(t *testing.T) {
assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B")
}
tokens, err = auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 2})
tokens, err = db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 2})
assert.NoError(t, err)
if assert.Len(t, tokens, 1) {
assert.Equal(t, int64(2), tokens[0].UID)
assert.Equal(t, "Token A", tokens[0].Name)
}
tokens, err = auth_model.ListAccessTokens(db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 100})
tokens, err = db.Find[auth_model.AccessToken](db.DefaultContext, auth_model.ListAccessTokensOptions{UserID: 100})
assert.NoError(t, err)
assert.Empty(t, tokens)
}

View File

@@ -5,6 +5,7 @@ package auth
import (
"context"
"crypto/sha256"
"encoding/base32"
"encoding/base64"
"fmt"
@@ -19,7 +20,6 @@ import (
"code.gitea.io/gitea/modules/util"
uuid "github.com/google/uuid"
"github.com/minio/sha256-simd"
"golang.org/x/crypto/bcrypt"
"xorm.io/builder"
"xorm.io/xorm"
@@ -243,13 +243,6 @@ func GetOAuth2ApplicationByID(ctx context.Context, id int64) (app *OAuth2Applica
return app, nil
}
// GetOAuth2ApplicationsByUserID returns all oauth2 applications owned by the user
func GetOAuth2ApplicationsByUserID(ctx context.Context, userID int64) (apps []*OAuth2Application, err error) {
apps = make([]*OAuth2Application, 0)
err = db.GetEngine(ctx).Where("uid = ?", userID).Find(&apps)
return apps, err
}
// CreateOAuth2ApplicationOptions holds options to create an oauth2 application
type CreateOAuth2ApplicationOptions struct {
Name string
@@ -372,25 +365,6 @@ func DeleteOAuth2Application(ctx context.Context, id, userid int64) error {
return committer.Commit()
}
// ListOAuth2Applications returns a list of oauth2 applications belongs to given user.
func ListOAuth2Applications(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*OAuth2Application, int64, error) {
sess := db.GetEngine(ctx).
Where("uid=?", uid).
Desc("id")
if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions)
apps := make([]*OAuth2Application, 0, listOptions.PageSize)
total, err := sess.FindAndCount(&apps)
return apps, total, err
}
apps := make([]*OAuth2Application, 0, 5)
total, err := sess.FindAndCount(&apps)
return apps, total, err
}
//////////////////////////////////////////////////////
// OAuth2AuthorizationCode is a code to obtain an access token in combination with the client secret once. It has a limited lifetime.

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

View File

@@ -264,3 +264,8 @@ func inTransaction(ctx context.Context) (*xorm.Session, bool) {
return nil, false
}
}
func Exists[T any](ctx context.Context, opts FindOptions) (bool, error) {
var bean T
return GetEngine(ctx).Where(opts.ToConds()).Exist(&bean)
}

View File

@@ -15,6 +15,7 @@ import (
const (
// DefaultMaxInSize represents default variables number on IN () in SQL
DefaultMaxInSize = 50
defaultFindSliceSize = 10
)
// Paginator is the base for different ListOptions types
@@ -52,7 +53,12 @@ type ListOptions struct {
ListAll bool // if true, then PageSize and Page will not be taken
}
var _ Paginator = &ListOptions{}
var ListOptionsAll = ListOptions{ListAll: true}
var (
_ Paginator = &ListOptions{}
_ FindOptions = ListOptions{}
)
// GetSkipTake returns the skip and take values
func (opts *ListOptions) GetSkipTake() (skip, take int) {
@@ -67,8 +73,16 @@ func (opts *ListOptions) GetStartEnd() (start, end int) {
return start, end
}
func (opts ListOptions) GetPage() int {
return opts.Page
}
func (opts ListOptions) GetPageSize() int {
return opts.PageSize
}
// IsListAll indicates PageSize and Page will be ignored
func (opts *ListOptions) IsListAll() bool {
func (opts ListOptions) IsListAll() bool {
return opts.ListAll
}
@@ -85,6 +99,10 @@ func (opts *ListOptions) SetDefaultValues() {
}
}
func (opts ListOptions) ToConds() builder.Cond {
return builder.NewCond()
}
// AbsoluteListOptions absolute options to paginate results
type AbsoluteListOptions struct {
skip int
@@ -124,29 +142,63 @@ func (opts *AbsoluteListOptions) GetStartEnd() (start, end int) {
// FindOptions represents a find options
type FindOptions interface {
Paginator
GetPage() int
GetPageSize() int
IsListAll() bool
ToConds() builder.Cond
}
type FindOptionsOrder interface {
ToOrders() string
}
// Find represents a common find function which accept an options interface
func Find[T any](ctx context.Context, opts FindOptions, objects *[]T) error {
func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
sess := GetEngine(ctx).Where(opts.ToConds())
if !opts.IsListAll() {
sess.Limit(opts.GetSkipTake())
page, pageSize := opts.GetPage(), opts.GetPageSize()
if !opts.IsListAll() && pageSize > 0 && page >= 1 {
sess.Limit(pageSize, (page-1)*pageSize)
}
return sess.Find(objects)
if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" {
sess.OrderBy(newOpt.ToOrders())
}
findPageSize := defaultFindSliceSize
if pageSize > 0 {
findPageSize = pageSize
}
objects := make([]*T, 0, findPageSize)
if err := sess.Find(&objects); err != nil {
return nil, err
}
return objects, nil
}
// Count represents a common count function which accept an options interface
func Count[T any](ctx context.Context, opts FindOptions, object T) (int64, error) {
return GetEngine(ctx).Where(opts.ToConds()).Count(object)
func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
var object T
return GetEngine(ctx).Where(opts.ToConds()).Count(&object)
}
// FindAndCount represents a common findandcount function which accept an options interface
func FindAndCount[T any](ctx context.Context, opts FindOptions, objects *[]T) (int64, error) {
func FindAndCount[T any](ctx context.Context, opts FindOptions) ([]*T, int64, error) {
sess := GetEngine(ctx).Where(opts.ToConds())
if !opts.IsListAll() {
sess.Limit(opts.GetSkipTake())
page, pageSize := opts.GetPage(), opts.GetPageSize()
if !opts.IsListAll() && pageSize > 0 && page >= 1 {
sess.Limit(pageSize, (page-1)*pageSize)
}
return sess.FindAndCount(objects)
if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" {
sess.OrderBy(newOpt.ToOrders())
}
findPageSize := defaultFindSliceSize
if pageSize > 0 {
findPageSize = pageSize
}
objects := make([]*T, 0, findPageSize)
cnt, err := sess.FindAndCount(&objects)
if err != nil {
return nil, 0, err
}
return objects, cnt, nil
}

View File

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

View File

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

View File

@@ -456,7 +456,7 @@ func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
return builder.Select("org_id").From("org_user").Where(cond)
}
func (opts FindOrgOptions) toConds() builder.Cond {
func (opts FindOrgOptions) ToConds() builder.Cond {
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
if opts.UserID > 0 {
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
@@ -467,23 +467,8 @@ func (opts FindOrgOptions) toConds() builder.Cond {
return cond
}
// FindOrgs returns a list of organizations according given conditions
func FindOrgs(ctx context.Context, opts FindOrgOptions) ([]*Organization, error) {
orgs := make([]*Organization, 0, 10)
sess := db.GetEngine(ctx).
Where(opts.toConds()).
Asc("`user`.name")
if opts.Page > 0 && opts.PageSize > 0 {
sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1))
}
return orgs, sess.Find(&orgs)
}
// CountOrgs returns total count organizations according options
func CountOrgs(ctx context.Context, opts FindOrgOptions) (int64, error) {
return db.GetEngine(ctx).
Where(opts.toConds()).
Count(new(Organization))
func (opts FindOrgOptions) ToOrders() string {
return "`user`.name ASC"
}
// HasOrgOrUserVisible tells if the given user can see the given org or user

View File

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

View File

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

View File

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

View File

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

View File

@@ -78,7 +78,7 @@ type FindSecretsOptions struct {
Name string
}
func (opts *FindSecretsOptions) toConds() builder.Cond {
func (opts FindSecretsOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
@@ -96,22 +96,6 @@ func (opts *FindSecretsOptions) toConds() builder.Cond {
return cond
}
func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error) {
var secrets []*Secret
sess := db.GetEngine(ctx)
if opts.PageSize != 0 {
sess = db.SetSessionPagination(sess, &opts.ListOptions)
}
return secrets, sess.
Where(opts.toConds()).
Find(&secrets)
}
// CountSecrets counts the secrets
func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret))
}
// UpdateSecret changes org or user reop secret.
func UpdateSecret(ctx context.Context, secretID int64, data string) error {
encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data)

View File

@@ -96,19 +96,6 @@ func GetExternalLogin(ctx context.Context, externalLoginUser *ExternalLoginUser)
return db.GetEngine(ctx).Get(externalLoginUser)
}
// ListAccountLinks returns a map with the ExternalLoginUser and its LoginSource
func ListAccountLinks(ctx context.Context, user *User) ([]*ExternalLoginUser, error) {
externalAccounts := make([]*ExternalLoginUser, 0, 5)
err := db.GetEngine(ctx).Where("user_id=?", user.ID).
Desc("login_source_id").
Find(&externalAccounts)
if err != nil {
return nil, err
}
return externalAccounts, nil
}
// LinkExternalToUser link the external user to the user
func LinkExternalToUser(ctx context.Context, user *User, externalLoginUser *ExternalLoginUser) error {
has, err := db.GetEngine(ctx).Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID).
@@ -173,28 +160,23 @@ func UpdateExternalUserByExternalID(ctx context.Context, external *ExternalLogin
// FindExternalUserOptions represents an options to find external users
type FindExternalUserOptions struct {
db.ListOptions
Provider string
Limit int
Start int
UserID int64
OrderBy string
}
func (opts FindExternalUserOptions) toConds() builder.Cond {
func (opts FindExternalUserOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if len(opts.Provider) > 0 {
cond = cond.And(builder.Eq{"provider": opts.Provider})
}
if opts.UserID > 0 {
cond = cond.And(builder.Eq{"user_id": opts.UserID})
}
return cond
}
// FindExternalUsersByProvider represents external users via provider
func FindExternalUsersByProvider(ctx context.Context, opts FindExternalUserOptions) ([]ExternalLoginUser, error) {
var users []ExternalLoginUser
err := db.GetEngine(ctx).Where(opts.toConds()).
Limit(opts.Limit, opts.Start).
OrderBy("login_source_id ASC, external_id ASC").
Find(&users)
if err != nil {
return nil, err
}
return users, nil
func (opts FindExternalUserOptions) ToOrders() string {
return opts.OrderBy
}

View File

@@ -435,7 +435,7 @@ type ListWebhookOptions struct {
IsActive util.OptionalBool
}
func (opts *ListWebhookOptions) toCond() builder.Cond {
func (opts ListWebhookOptions) ToConds() builder.Cond {
cond := builder.NewCond()
if opts.RepoID != 0 {
cond = cond.And(builder.Eq{"webhook.repo_id": opts.RepoID})
@@ -449,27 +449,6 @@ func (opts *ListWebhookOptions) toCond() builder.Cond {
return cond
}
// ListWebhooksByOpts return webhooks based on options
func ListWebhooksByOpts(ctx context.Context, opts *ListWebhookOptions) ([]*Webhook, error) {
sess := db.GetEngine(ctx).Where(opts.toCond())
if opts.Page != 0 {
sess = db.SetSessionPagination(sess, opts)
webhooks := make([]*Webhook, 0, opts.PageSize)
err := sess.Find(&webhooks)
return webhooks, err
}
webhooks := make([]*Webhook, 0, 10)
err := sess.Find(&webhooks)
return webhooks, err
}
// CountWebhooksByOpts count webhooks based on options and ignore pagination
func CountWebhooksByOpts(ctx context.Context, opts *ListWebhookOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toCond()).Count(&Webhook{})
}
// UpdateWebhook updates information of webhook.
func UpdateWebhook(ctx context.Context, w *Webhook) error {
_, err := db.GetEngine(ctx).ID(w.ID).AllCols().Update(w)

View File

@@ -123,7 +123,7 @@ func TestGetWebhookByOwnerID(t *testing.T) {
func TestGetActiveWebhooksByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue})
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: util.OptionalBoolTrue})
assert.NoError(t, err)
if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(1), hooks[0].ID)
@@ -133,7 +133,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) {
func TestGetWebhooksByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{RepoID: 1})
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1})
assert.NoError(t, err)
if assert.Len(t, hooks, 2) {
assert.Equal(t, int64(1), hooks[0].ID)
@@ -143,7 +143,7 @@ func TestGetWebhooksByRepoID(t *testing.T) {
func TestGetActiveWebhooksByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue})
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: util.OptionalBoolTrue})
assert.NoError(t, err)
if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(3), hooks[0].ID)
@@ -153,7 +153,7 @@ func TestGetActiveWebhooksByOwnerID(t *testing.T) {
func TestGetWebhooksByOwnerID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
hooks, err := ListWebhooksByOpts(db.DefaultContext, &ListWebhookOptions{OwnerID: 3})
hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3})
assert.NoError(t, err)
if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(3), hooks[0].ID)

View File

@@ -563,6 +563,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
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)
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
func countOrphanedRepos(ctx context.Context) (int64, error) {
return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=user.id")
return db.CountOrphanedObjects(ctx, "repository", "user", "repository.owner_id=`user`.id")
}
// deleteOrphanedRepos delete repository where user of owner_id do not exist
@@ -43,7 +43,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) {
default:
var ids []int64
if err := e.Table("`repository`").
Join("LEFT", "`user`", "repository.owner_id=user.id").
Join("LEFT", "`user`", "repository.owner_id=`user`.id").
Where(builder.IsNull{"`user`.id"}).
Select("`repository`.id").Limit(batchSize).Find(&ids); err != nil {
return deleted, err

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"
"runtime/pprof"
"strconv"
"sync"
"syscall"
"time"
@@ -22,51 +21,6 @@ import (
"code.gitea.io/gitea/modules/setting"
)
// Manager manages the graceful shutdown process
type Manager struct {
isChild bool
forked bool
lock *sync.RWMutex
state state
shutdownCtx context.Context
hammerCtx context.Context
terminateCtx context.Context
managerCtx context.Context
shutdownCtxCancel context.CancelFunc
hammerCtxCancel context.CancelFunc
terminateCtxCancel context.CancelFunc
managerCtxCancel context.CancelFunc
runningServerWaitGroup sync.WaitGroup
createServerWaitGroup sync.WaitGroup
terminateWaitGroup sync.WaitGroup
toRunAtShutdown []func()
toRunAtTerminate []func()
}
func newGracefulManager(ctx context.Context) *Manager {
manager := &Manager{
isChild: len(os.Getenv(listenFDsEnv)) > 0 && os.Getppid() > 1,
lock: &sync.RWMutex{},
}
manager.createServerWaitGroup.Add(numberOfServersToCreate)
manager.start(ctx)
return manager
}
type systemdNotifyMsg string
const (
readyMsg systemdNotifyMsg = "READY=1"
stoppingMsg systemdNotifyMsg = "STOPPING=1"
reloadingMsg systemdNotifyMsg = "RELOADING=1"
watchdogMsg systemdNotifyMsg = "WATCHDOG=1"
)
func statusMsg(msg string) systemdNotifyMsg {
return systemdNotifyMsg("STATUS=" + msg)
}
func pidMsg() systemdNotifyMsg {
return systemdNotifyMsg("MAINPID=" + strconv.Itoa(os.Getpid()))
}
@@ -89,27 +43,13 @@ func (g *Manager) notify(msg systemdNotifyMsg) {
}
}
func (g *Manager) start(ctx context.Context) {
// Make contexts
g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx)
g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx)
g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx)
g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx)
// Next add pprof labels to these contexts
g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate"))
g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown"))
g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer"))
g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager"))
func (g *Manager) start() {
// Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager
pprof.SetGoroutineLabels(g.managerCtx)
defer pprof.SetGoroutineLabels(ctx)
defer pprof.SetGoroutineLabels(g.ctx)
g.isChild = len(os.Getenv(listenFDsEnv)) > 0 && os.Getppid() > 1
// Set the running state & handle signals
if !g.setStateTransition(stateInit, stateRunning) {
panic("invalid graceful manager state: transition from init to running failed")
}
g.notify(statusMsg("Starting Gitea"))
g.notify(pidMsg())
go g.handleSignals(g.managerCtx)
@@ -118,11 +58,9 @@ func (g *Manager) start(ctx context.Context) {
startupDone := make(chan struct{})
go func() {
defer close(startupDone)
// Wait till we're done getting all of the listeners and then close
// the unused ones
// Wait till we're done getting all the listeners and then close the unused ones
g.createServerWaitGroup.Wait()
// Ignore the error here there's not much we can do with it
// They're logged in the CloseProvidedListeners function
// Ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function
_ = CloseProvidedListeners()
g.notify(readyMsg)
}()
@@ -133,7 +71,7 @@ func (g *Manager) start(ctx context.Context) {
return
case <-g.IsShutdown():
func() {
// When waitgroup counter goes negative it will panic - we don't care about this so we can just ignore it.
// When WaitGroup counter goes negative it will panic - we don't care about this so we can just ignore it.
defer func() {
_ = recover()
}()
@@ -255,29 +193,3 @@ func (g *Manager) DoGracefulRestart() {
g.doShutdown()
}
}
// DoImmediateHammer causes an immediate hammer
func (g *Manager) DoImmediateHammer() {
g.notify(statusMsg("Sending immediate hammer"))
g.doHammerTime(0 * time.Second)
}
// DoGracefulShutdown causes a graceful shutdown
func (g *Manager) DoGracefulShutdown() {
g.lock.Lock()
if !g.forked {
g.lock.Unlock()
g.notify(stoppingMsg)
} else {
g.lock.Unlock()
g.notify(statusMsg("Shutting down after fork"))
}
g.doShutdown()
}
// RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die.
// Any call to RegisterServer must be matched by a call to ServerDone
func (g *Manager) RegisterServer() {
KillParent()
g.runningServerWaitGroup.Add(1)
}

View File

@@ -7,11 +7,9 @@
package graceful
import (
"context"
"os"
"runtime/pprof"
"strconv"
"sync"
"time"
"code.gitea.io/gitea/modules/log"
@@ -30,64 +28,11 @@ const (
acceptHammerCode = svc.Accepted(hammerCode)
)
// Manager manages the graceful shutdown process
type Manager struct {
ctx context.Context
isChild bool
lock *sync.RWMutex
state state
shutdownCtx context.Context
hammerCtx context.Context
terminateCtx context.Context
managerCtx context.Context
shutdownCtxCancel context.CancelFunc
hammerCtxCancel context.CancelFunc
terminateCtxCancel context.CancelFunc
managerCtxCancel context.CancelFunc
runningServerWaitGroup sync.WaitGroup
createServerWaitGroup sync.WaitGroup
terminateWaitGroup sync.WaitGroup
shutdownRequested chan struct{}
toRunAtShutdown []func()
toRunAtTerminate []func()
}
func newGracefulManager(ctx context.Context) *Manager {
manager := &Manager{
isChild: false,
lock: &sync.RWMutex{},
ctx: ctx,
}
manager.createServerWaitGroup.Add(numberOfServersToCreate)
manager.start()
return manager
}
func (g *Manager) start() {
// Make contexts
g.terminateCtx, g.terminateCtxCancel = context.WithCancel(g.ctx)
g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(g.ctx)
g.hammerCtx, g.hammerCtxCancel = context.WithCancel(g.ctx)
g.managerCtx, g.managerCtxCancel = context.WithCancel(g.ctx)
// Next add pprof labels to these contexts
g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate"))
g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown"))
g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer"))
g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager"))
// Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager
pprof.SetGoroutineLabels(g.managerCtx)
defer pprof.SetGoroutineLabels(g.ctx)
// Make channels
g.shutdownRequested = make(chan struct{})
// Set the running state
if !g.setStateTransition(stateInit, stateRunning) {
panic("invalid graceful manager state: transition from init to running failed")
}
if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
log.Trace("Skipping SVC check as SKIP_MINWINSVC is set")
return
@@ -201,30 +146,6 @@ hammerLoop:
return false, 0
}
// DoImmediateHammer causes an immediate hammer
func (g *Manager) DoImmediateHammer() {
g.doHammerTime(0 * time.Second)
}
// DoGracefulShutdown causes a graceful shutdown
func (g *Manager) DoGracefulShutdown() {
g.lock.Lock()
select {
case <-g.shutdownRequested:
g.lock.Unlock()
default:
close(g.shutdownRequested)
g.lock.Unlock()
g.doShutdown()
}
}
// RegisterServer registers the running of a listening server.
// Any call to RegisterServer must be matched by a call to ServerDone
func (g *Manager) RegisterServer() {
g.runningServerWaitGroup.Add(1)
}
func (g *Manager) awaitServer(limit time.Duration) bool {
c := make(chan struct{})
go func() {
@@ -249,3 +170,11 @@ func (g *Manager) awaitServer(limit time.Duration) bool {
}
}
}
func (g *Manager) notify(msg systemdNotifyMsg) {
// Windows doesn't use systemd to notify
}
func KillParent() {
// Windows doesn't need to "kill parent" because there is no graceful restart
}

View File

@@ -19,6 +19,7 @@ import (
// any modification to the underlying policies once it's been created.
type Sanitizer struct {
defaultPolicy *bluemonday.Policy
descriptionPolicy *bluemonday.Policy
rendererPolicies map[string]*bluemonday.Policy
init sync.Once
}
@@ -41,6 +42,7 @@ func NewSanitizer() {
func InitializeSanitizer() {
sanitizer.rendererPolicies = map[string]*bluemonday.Policy{}
sanitizer.defaultPolicy = createDefaultPolicy()
sanitizer.descriptionPolicy = createRepoDescriptionPolicy()
for name, renderer := range renderers {
sanitizerRules := renderer.SanitizerRules()
@@ -161,6 +163,27 @@ func createDefaultPolicy() *bluemonday.Policy {
return policy
}
// createRepoDescriptionPolicy returns a minimal more strict policy that is used for
// repository descriptions.
func createRepoDescriptionPolicy() *bluemonday.Policy {
policy := bluemonday.NewPolicy()
// Allow italics and bold.
policy.AllowElements("i", "b", "em", "strong")
// Allow code.
policy.AllowElements("code")
// Allow links
policy.AllowAttrs("href", "target", "rel").OnElements("a")
// Allow classes for emojis
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^emoji$`)).OnElements("img", "span")
policy.AllowAttrs("aria-label").OnElements("span")
return policy
}
func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitizerRule) {
for _, rule := range rules {
if rule.AllowDataURIImages {
@@ -176,6 +199,12 @@ func addSanitizerRules(policy *bluemonday.Policy, rules []setting.MarkupSanitize
}
}
// SanitizeDescription sanitizes the HTML generated for a repository description.
func SanitizeDescription(s string) string {
NewSanitizer()
return sanitizer.descriptionPolicy.Sanitize(s)
}
// Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
func Sanitize(s string) string {
NewSanitizer()

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) {
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...)
}
func (r *Route) Options(pattern string, h ...any) {
r.Methods("OPTIONS", pattern, h...)
}
// GetOptions delegate get and options method
func (r *Route) GetOptions(pattern string, h ...any) {
r.Methods("GET,OPTIONS", pattern, h...)

View File

@@ -3529,7 +3529,11 @@ runs.status = Status
runs.actors_no_select = All actors
runs.status_no_select = All status
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.empty_commit_message = (empty commit message)
workflow.disable = Disable Workflow
workflow.disable_success = Workflow '%s' disabled successfully.

View File

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

View File

@@ -13,6 +13,7 @@ import (
"time"
"code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
)
@@ -86,7 +87,10 @@ func listChunksByRunID(st storage.ObjectStorage, runID int64) (map[int64][]*chun
func mergeChunksForRun(ctx *ArtifactContext, st storage.ObjectStorage, runID int64, artifactName string) error {
// read all db artifacts by name
artifacts, err := actions.ListArtifactsByRunIDAndName(ctx, runID, artifactName)
artifacts, err := db.Find[actions.ActionArtifact](ctx, actions.FindArtifactsOptions{
RunID: runID,
ArtifactName: artifactName,
})
if err != nil {
return err
}

View File

@@ -8,6 +8,7 @@ import (
"fmt"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret"
actions_module "code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/container"
@@ -67,12 +68,12 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s
return secrets
}
ownerSecrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID})
ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID})
if err != nil {
log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err)
// go on
}
repoSecrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID})
repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID})
if err != nil {
log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err)
// go on
@@ -94,13 +95,13 @@ func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map
variables := map[string]string{}
// Org / User level
ownerVariables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID})
ownerVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID})
if err != nil {
log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err)
}
// Repo level
repoVariables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID})
repoVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID})
if err != nil {
log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err)
}
@@ -200,7 +201,7 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str
}
needs := container.SetOf(task.Job.Needs...)
jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID})
jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID})
if err != nil {
return nil, fmt.Errorf("FindRunJobs: %w", err)
}

View File

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

View File

@@ -8,6 +8,7 @@ import (
"strings"
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
@@ -21,7 +22,17 @@ func NewAvailable(ctx *context.APIContext) {
// responses:
// "200":
// "$ref": "#/responses/NotificationCount"
ctx.JSON(http.StatusOK, api.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)})
total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
UserID: ctx.Doer.ID,
Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
})
if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "db.Count[activities_model.Notification]", err)
return
}
ctx.JSON(http.StatusOK, api.NotificationCount{New: total})
}
func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@ import (
"strconv"
"strings"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
project_model "code.gitea.io/gitea/models/project"
attachment_model "code.gitea.io/gitea/models/repo"
@@ -59,9 +60,12 @@ func Projects(ctx *context.Context) {
} else {
projectType = project_model.TypeIndividual
}
projects, total, err := project_model.FindProjects(ctx, project_model.SearchOptions{
OwnerID: ctx.ContextUser.ID,
projects, total, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{
ListOptions: db.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
OwnerID: ctx.ContextUser.ID,
IsClosed: util.OptionalBoolOf(isShowClosed),
OrderBy: project_model.GetSearchOrderByBySortType(sortType),
Type: projectType,
@@ -72,7 +76,7 @@ func Projects(ctx *context.Context) {
return
}
opTotal, err := project_model.CountProjects(ctx, project_model.SearchOptions{
opTotal, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{
OwnerID: ctx.ContextUser.ID,
IsClosed: util.OptionalBoolOf(!isShowClosed),
Type: projectType,

View File

@@ -215,7 +215,7 @@ func Webhooks(ctx *context.Context) {
ctx.Data["BaseLinkNew"] = ctx.Org.OrgLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID})
ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{OwnerID: ctx.Org.Organization.ID})
if err != nil {
ctx.ServerError("ListWebhooksByOpts", err)
return

View File

@@ -8,6 +8,7 @@ import (
"net/http"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
@@ -35,7 +36,9 @@ func Applications(ctx *context.Context) {
ctx.Data["PageIsOrgSettings"] = true
ctx.Data["PageIsSettingsApplications"] = true
apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, ctx.Org.Organization.ID)
apps, err := db.Find[auth.OAuth2Application](ctx, auth.FindOAuth2ApplicationsOptions{
OwnerID: ctx.Org.Organization.ID,
})
if err != nil {
ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
return

View File

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

View File

@@ -512,7 +512,7 @@ func ArtifactsView(ctx *context_module.Context) {
}
for _, art := range artifacts {
status := "completed"
if art.Status == int64(actions_model.ArtifactStatusExpired) {
if art.Status == actions_model.ArtifactStatusExpired {
status = "expired"
}
artifactsResponse.Artifacts = append(artifactsResponse.Artifacts, &ArtifactsViewItem{
@@ -538,7 +538,10 @@ func ArtifactsDownloadView(ctx *context_module.Context) {
return
}
artifacts, err := actions_model.ListArtifactsByRunIDAndName(ctx, run.ID, artifactName)
artifacts, err := db.Find[actions_model.ActionArtifact](ctx, actions_model.FindArtifactsOptions{
RunID: run.ID,
ArtifactName: artifactName,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, err.Error())
return

View File

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

View File

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

View File

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

View File

@@ -22,7 +22,7 @@ func DeployKeys(ctx *context.Context) {
ctx.Data["PageIsSettingsKeys"] = true
ctx.Data["DisableSSH"] = setting.SSH.Disabled
keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
keys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
if err != nil {
ctx.ServerError("ListDeployKeys", err)
return
@@ -39,7 +39,7 @@ func DeployKeysPost(ctx *context.Context) {
ctx.Data["PageIsSettingsKeys"] = true
ctx.Data["DisableSSH"] = setting.SSH.Disabled
keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
keys, err := db.Find[asymkey_model.DeployKey](ctx, asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID})
if err != nil {
ctx.ServerError("ListDeployKeys", err)
return

View File

@@ -12,6 +12,7 @@ import (
"path"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
user_model "code.gitea.io/gitea/models/user"
@@ -46,7 +47,7 @@ func Webhooks(ctx *context.Context) {
ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.com/usage/webhooks")
ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID})
if err != nil {
ctx.ServerError("GetWebhooksByRepoID", err)
return

View File

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

View File

@@ -18,7 +18,7 @@ import (
)
func SetVariablesContext(ctx *context.Context, ownerID, repoID int64) {
variables, err := actions_model.FindVariables(ctx, actions_model.FindVariablesOpts{
variables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{
OwnerID: ownerID,
RepoID: repoID,
})

View File

@@ -4,6 +4,7 @@
package secrets
import (
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
@@ -14,7 +15,7 @@ import (
)
func SetSecretsContext(ctx *context.Context, ownerID, repoID int64) {
secrets, err := secret_model.FindSecrets(ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID})
secrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: ownerID, RepoID: repoID})
if err != nil {
ctx.ServerError("FindSecrets", err)
return

View File

@@ -60,7 +60,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
}
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
orgs, err := organization.FindOrgs(ctx, organization.FindOrgOptions{
orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{
UserID: ctx.ContextUser.ID,
IncludePrivate: showPrivate,
})
@@ -141,7 +141,7 @@ func LoadHeaderCount(ctx *context.Context) error {
} else {
projectType = project_model.TypeIndividual
}
projectCount, err := project_model.CountProjects(ctx, project_model.SearchOptions{
projectCount, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{
OwnerID: ctx.ContextUser.ID,
IsClosed: util.OptionalBoolOf(false),
Type: projectType,

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
func ShowSSHKeys(ctx *context.Context) {
keys, err := asymkey_model.ListPublicKeys(ctx, ctx.ContextUser.ID, db.ListOptions{})
keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
OwnerID: ctx.ContextUser.ID,
})
if err != nil {
ctx.ServerError("ListPublicKeys", err)
return

View File

@@ -42,7 +42,10 @@ func GetNotificationCount(ctx *context.Context) {
}
ctx.Data["NotificationUnreadCount"] = func() int64 {
count, err := activities_model.GetNotificationCount(ctx, ctx.Doer, activities_model.NotificationStatusUnread)
count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
UserID: ctx.Doer.ID,
Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
})
if err != nil {
if err != goctx.Canceled {
log.Error("Unable to GetNotificationCount for user:%-v: %v", ctx.Doer, err)
@@ -89,7 +92,10 @@ func getNotifications(ctx *context.Context) {
status = activities_model.NotificationStatusUnread
}
total, err := activities_model.GetNotificationCount(ctx, ctx.Doer, status)
total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
UserID: ctx.Doer.ID,
Status: []activities_model.NotificationStatus{status},
})
if err != nil {
ctx.ServerError("ErrGetNotificationCount", err)
return
@@ -103,12 +109,21 @@ func getNotifications(ctx *context.Context) {
}
statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned}
notifications, err := activities_model.NotificationsForUser(ctx, ctx.Doer, statuses, page, perPage)
nls, err := db.Find[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
ListOptions: db.ListOptions{
PageSize: perPage,
Page: page,
},
UserID: ctx.Doer.ID,
Status: statuses,
})
if err != nil {
ctx.ServerError("ErrNotificationsForUser", err)
ctx.ServerError("db.Find[activities_model.Notification]", err)
return
}
notifications := activities_model.NotificationList(nls)
failCount := 0
repos, failures, err := notifications.LoadRepos(ctx)
@@ -409,5 +424,15 @@ func NotificationWatching(ctx *context.Context) {
// NewAvailable returns the notification counts
func NewAvailable(ctx *context.Context) {
ctx.JSON(http.StatusOK, structs.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)})
total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
UserID: ctx.Doer.ID,
Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
})
if err != nil {
log.Error("db.Count[activities_model.Notification]", err)
ctx.JSON(http.StatusOK, structs.NotificationCount{New: 0})
return
}
ctx.JSON(http.StatusOK, structs.NotificationCount{New: total})
}

View File

@@ -8,6 +8,7 @@ import (
"net/http"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
@@ -88,7 +89,7 @@ func DeleteApplication(ctx *context.Context) {
func loadApplicationsData(ctx *context.Context) {
ctx.Data["AccessTokenScopePublicOnly"] = auth_model.AccessTokenScopePublicOnly
tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
if err != nil {
ctx.ServerError("ListAccessTokens", err)
return
@@ -97,7 +98,9 @@ func loadApplicationsData(ctx *context.Context) {
ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin
if setting.OAuth2.Enable {
ctx.Data["Applications"], err = auth_model.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID)
ctx.Data["Applications"], err = db.Find[auth_model.OAuth2Application](ctx, auth_model.FindOAuth2ApplicationsOptions{
OwnerID: ctx.Doer.ID,
})
if err != nil {
ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
return

View File

@@ -260,7 +260,10 @@ func DeleteKey(ctx *context.Context) {
}
func loadKeysData(ctx *context.Context) {
keys, err := asymkey_model.ListPublicKeys(ctx, ctx.Doer.ID, db.ListOptions{})
keys, err := db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{
OwnerID: ctx.Doer.ID,
NotKeytype: asymkey_model.KeyTypePrincipal,
})
if err != nil {
ctx.ServerError("ListPublicKeys", err)
return

View File

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

View File

@@ -9,6 +9,7 @@ import (
"sort"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
@@ -68,14 +69,17 @@ func loadSecurityData(ctx *context.Context) {
}
ctx.Data["WebAuthnCredentials"] = credentials
tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
if err != nil {
ctx.ServerError("ListAccessTokens", err)
return
}
ctx.Data["Tokens"] = tokens
accountLinks, err := user_model.ListAccountLinks(ctx, ctx.Doer)
accountLinks, err := db.Find[user_model.ExternalLoginUser](ctx, user_model.FindExternalUserOptions{
UserID: ctx.Doer.ID,
OrderBy: "login_source_id DESC",
})
if err != nil {
ctx.ServerError("ListAccountLinks", err)
return
@@ -107,7 +111,7 @@ func loadSecurityData(ctx *context.Context) {
}
ctx.Data["AccountLinks"] = sources
authSources, err := auth_model.FindSources(ctx, auth_model.FindSourcesOptions{
authSources, err := db.Find[auth_model.Source](ctx, auth_model.FindSourcesOptions{
IsActive: util.OptionalBoolNone,
LoginType: auth_model.OAuth2,
})

View File

@@ -6,6 +6,7 @@ package setting
import (
"net/http"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
@@ -24,7 +25,7 @@ func Webhooks(ctx *context.Context) {
ctx.Data["BaseLinkNew"] = setting.AppSubURL + "/user/settings/hooks"
ctx.Data["Description"] = ctx.Tr("settings.hooks.desc")
ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{OwnerID: ctx.Doer.ID})
ws, err := db.Find[webhook.Webhook](ctx, webhook.ListWebhookOptions{OwnerID: ctx.Doer.ID})
if err != nil {
ctx.ServerError("ListWebhooksByOpts", err)
return

View File

@@ -533,8 +533,10 @@ func registerRoutes(m *web.Route) {
m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth)
}, ignSignInAndCsrf, reqSignIn)
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.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.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 {
tasks, err := actions_model.FindTasks(ctx, opts)
tasks, err := db.Find[actions_model.ActionTask](ctx, opts)
if err != nil {
return fmt.Errorf("find tasks: %w", err)
}
@@ -74,7 +74,7 @@ func stopTasks(ctx context.Context, opts actions_model.FindTaskOptions) error {
// CancelAbandonedJobs cancels the jobs which have waiting status, but haven't been picked by a runner for a long time
func CancelAbandonedJobs(ctx context.Context) error {
jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{
jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{
Statuses: []actions_model.Status{actions_model.StatusWaiting, actions_model.StatusBlocked},
UpdatedBefore: timeutil.TimeStamp(time.Now().Add(-setting.Actions.AbandonedJobTimeout).Unix()),
})

View File

@@ -44,7 +44,7 @@ func jobEmitterQueueHandler(items ...*jobUpdate) []*jobUpdate {
}
func checkJobsOfRun(ctx context.Context, runID int64) error {
jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: runID})
jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: runID})
if err != nil {
return err
}

View File

@@ -11,6 +11,7 @@ import (
"strings"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
access_model "code.gitea.io/gitea/models/perm/access"
@@ -298,7 +299,7 @@ func handleWorkflows(
continue
}
alljobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: run.ID})
alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID})
if err != nil {
log.Error("FindRunJobs: %v", err)
continue
@@ -377,7 +378,7 @@ func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun, repo *rep
}
// don't need approval if the user has been approved before
if count, err := actions_model.CountRuns(ctx, actions_model.FindRunOptions{
if count, err := db.Count[actions_model.ActionRun](ctx, actions_model.FindRunOptions{
RepoID: repo.ID,
TriggerUserID: user.ID,
Approved: true,
@@ -408,7 +409,7 @@ func handleSchedules(
return nil
}
if count, err := actions_model.CountSchedules(ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}); err != nil {
if count, err := db.Count[actions_model.ActionSchedule](ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}); err != nil {
log.Error("CountSchedules: %v", err)
return err
} else if count > 0 {

View File

@@ -67,7 +67,10 @@ ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ib
for i, kase := range testCases {
s.ID = int64(i) + 20
asymkey_model.AddPublicKeysBySource(db.DefaultContext, user, s, []string{kase.keyString})
keys, err := asymkey_model.ListPublicKeysBySource(db.DefaultContext, user.ID, s.ID)
keys, err := db.Find[asymkey_model.PublicKey](db.DefaultContext, asymkey_model.FindPublicKeyOptions{
OwnerID: user.ID,
LoginSourceID: s.ID,
})
assert.NoError(t, err)
if err != nil {
continue

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