mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-13 02:02:53 +09:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96d3fcf179 | ||
|
|
265f485295 | ||
|
|
f144521aea | ||
|
|
6f4d5c0b8c | ||
|
|
1ec622db24 | ||
|
|
40d51188c0 | ||
|
|
87db4a47c8 | ||
|
|
cd2dd5a67d | ||
|
|
46beb7f33f | ||
|
|
3107093394 | ||
|
|
272ae03341 | ||
|
|
b56a9f6ded | ||
|
|
c5c44d0951 | ||
|
|
8f2805f757 | ||
|
|
5eaf91e919 | ||
|
|
b7e3adc66c | ||
|
|
5b5f8aab19 | ||
|
|
fef34790bb | ||
|
|
8b590de186 | ||
|
|
5105d2093c | ||
|
|
08445d5d86 | ||
|
|
b71d4c3ec0 | ||
|
|
bf537adf8a | ||
|
|
8c8c24f8eb | ||
|
|
fee9c05ed3 | ||
|
|
e15fe85335 | ||
|
|
4f5122a7fe | ||
|
|
84e65afffd | ||
|
|
d2908b2794 | ||
|
|
24e03a125d | ||
|
|
76e892317b | ||
|
|
5001f63c07 | ||
|
|
6d22ca15ab | ||
|
|
ea9f5a57e4 | ||
|
|
96141e4e55 | ||
|
|
ca5f0c93c6 | ||
|
|
196100a07a | ||
|
|
bc3d8bff73 | ||
|
|
7f81110461 | ||
|
|
5ed0eefc9a | ||
|
|
4b89c0f996 | ||
|
|
7cae4dfc00 | ||
|
|
28b8e0b43e | ||
|
|
23838c2c2e | ||
|
|
f9763f1366 | ||
|
|
a2314ca9c5 | ||
|
|
994ba35f11 | ||
|
|
447422fe27 | ||
|
|
9bfee5014b | ||
|
|
7128929a0d | ||
|
|
efcbaf8fa8 | ||
|
|
c997e90738 | ||
|
|
ffab076b72 | ||
|
|
117d9a117f | ||
|
|
f8c5f202b7 | ||
|
|
7213506680 | ||
|
|
1f82be6604 | ||
|
|
56bedf2bcc | ||
|
|
f7567f798d | ||
|
|
93ede4bc83 | ||
|
|
9f63d27ec4 | ||
|
|
073d8c50dd |
9
.github/workflows/release-tag-rc.yml
vendored
9
.github/workflows/release-tag-rc.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
run: |
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
@@ -56,6 +56,10 @@ jobs:
|
||||
- name: upload binaries to s3
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
- name: Install GH CLI
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
run: |
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
||||
@@ -74,6 +78,8 @@ jobs:
|
||||
id: meta
|
||||
with:
|
||||
images: gitea/gitea
|
||||
flavor: |
|
||||
latest=false
|
||||
# 1.2.3-rc0
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
@@ -105,6 +111,7 @@ jobs:
|
||||
images: gitea/gitea
|
||||
# each tag below will have the suffix of -rootless
|
||||
flavor: |
|
||||
latest=false
|
||||
suffix=-rootless
|
||||
# 1.2.3-rc0
|
||||
tags: |
|
||||
|
||||
12
.github/workflows/release-tag-version.yml
vendored
12
.github/workflows/release-tag-version.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
- name: Get cleaned branch name
|
||||
id: clean_name
|
||||
run: |
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
|
||||
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
|
||||
echo "Cleaned name is ${REF_NAME}"
|
||||
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
|
||||
- name: configure aws
|
||||
@@ -58,9 +58,13 @@ jobs:
|
||||
- name: upload binaries to s3
|
||||
run: |
|
||||
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
|
||||
- name: Install GH CLI
|
||||
uses: dev-hanz-ops/install-gh-cli-action@v0.1.0
|
||||
with:
|
||||
gh-cli-version: 2.39.1
|
||||
- name: create github release
|
||||
run: |
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --draft --notes-from-tag dist/release/*
|
||||
gh release create ${{ github.ref_name }} --title ${{ github.ref_name }} --notes-from-tag dist/release/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
docker-rootful:
|
||||
@@ -82,7 +86,6 @@ jobs:
|
||||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
@@ -114,14 +117,13 @@ jobs:
|
||||
images: gitea/gitea
|
||||
# each tag below will have the suffix of -rootless
|
||||
flavor: |
|
||||
suffix=-rootless
|
||||
suffix=-rootless,onlatest=true
|
||||
# this will generate tags in the following format (with -rootless suffix added):
|
||||
# latest
|
||||
# 1
|
||||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
|
||||
66
CHANGELOG.md
66
CHANGELOG.md
@@ -4,6 +4,72 @@ This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.com).
|
||||
|
||||
## [1.21.2](https://github.com/go-gitea/gitea/releases/tag/1.21.2) - 2023-12-12
|
||||
|
||||
* SECURITY
|
||||
* Rebuild with recently released golang version
|
||||
* Fix missing check (#28406) (#28411)
|
||||
* Do some missing checks (#28423) (#28432)
|
||||
* BUGFIXES
|
||||
* Fix margin in server signed signature verification view (#28379) (#28381)
|
||||
* Fix object does not exist error when checking citation file (#28314) (#28369)
|
||||
* Use `filepath` instead of `path` to create SQLite3 database file (#28374) (#28378)
|
||||
* Fix the runs will not be displayed bug when the main branch have no workflows but other branches have (#28359) (#28365)
|
||||
* Handle repository.size column being NULL in migration v263 (#28336) (#28363)
|
||||
* Convert git commit summary to valid UTF8. (#28356) (#28358)
|
||||
* Fix migration panic due to an empty review comment diff (#28334) (#28362)
|
||||
* Add `HEAD` support for rpm repo files (#28309) (#28360)
|
||||
* Fix RPM/Debian signature key creation (#28352) (#28353)
|
||||
* Keep profile tab when clicking on Language (#28320) (#28331)
|
||||
* Fix missing issue search index update when changing status (#28325) (#28330)
|
||||
* Fix wrong link in `protect_branch_name_pattern_desc` (#28313) (#28315)
|
||||
* Read `previous` info from git blame (#28306) (#28310)
|
||||
* Ignore "non-existing" errors when getDirectorySize calculates the size (#28276) (#28285)
|
||||
* Use appSubUrl for OAuth2 callback URL tip (#28266) (#28275)
|
||||
* Meilisearch: require all query terms to be matched (#28293) (#28296)
|
||||
* Fix required error for token name (#28267) (#28284)
|
||||
* Fix issue will be detected as pull request when checking `First-time contributor` (#28237) (#28271)
|
||||
* Use full width for project boards (#28225) (#28245)
|
||||
* Increase "version" when update the setting value to a same value as before (#28243) (#28244)
|
||||
* Also sync DB branches on push if necessary (#28361) (#28403)
|
||||
* Make gogit Repository.GetBranchNames consistent (#28348) (#28386)
|
||||
* Recover from panic in cron task (#28409) (#28425)
|
||||
* Deprecate query string auth tokens (#28390) (#28430)
|
||||
* ENHANCEMENTS
|
||||
* Improve doctor cli behavior (#28422) (#28424)
|
||||
* Fix margin in server signed signature verification view (#28379) (#28381)
|
||||
* Refactor template empty checks (#28351) (#28354)
|
||||
* Read `previous` info from git blame (#28306) (#28310)
|
||||
* Use full width for project boards (#28225) (#28245)
|
||||
* Enable system users search via the API (#28013) (#28018)
|
||||
|
||||
## [1.21.1](https://github.com/go-gitea/gitea/releases/tag/1.21.1) - 2023-11-26
|
||||
|
||||
* SECURITY
|
||||
* Fix comment permissions (#28213) (#28216)
|
||||
* BUGFIXES
|
||||
* Fix delete-orphaned-repos (#28200) (#28202)
|
||||
* Make CORS work for oauth2 handlers (#28184) (#28185)
|
||||
* Fix missing buttons (#28179) (#28181)
|
||||
* Fix no ActionTaskOutput table waring (#28149) (#28152)
|
||||
* Fix empty action run title (#28113) (#28148)
|
||||
* Use "is-loading" to avoid duplicate form submit for code comment (#28143) (#28147)
|
||||
* Fix Matrix and MSTeams nil dereference (#28089) (#28105)
|
||||
* Fix incorrect pgsql conn builder behavior (#28085) (#28098)
|
||||
* Fix system config cache expiration timing (#28072) (#28090)
|
||||
* Restricted users only see repos in orgs which their team was assigned to (#28025) (#28051)
|
||||
* API
|
||||
* Fix permissions for Token DELETE endpoint to match GET and POST (#27610) (#28099)
|
||||
* ENHANCEMENTS
|
||||
* Do not display search box when there's no packages yet (#28146) (#28159)
|
||||
* Add missing `packages.cleanup.success` (#28129) (#28132)
|
||||
* DOCS
|
||||
* Docs: Replace deprecated IS_TLS_ENABLED mailer setting in email setup (#28205) (#28208)
|
||||
* Fix the description about the default setting for action in quick start document (#28160) (#28168)
|
||||
* Add guide page to actions when there's no workflows (#28145) (#28153)
|
||||
* MISC
|
||||
* Use full width for PR comparison (#28182) (#28186)
|
||||
|
||||
## [1.21.0](https://github.com/go-gitea/gitea/releases/tag/v1.21.0) - 2023-11-14
|
||||
|
||||
* BREAKING
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/migrations"
|
||||
migrate_base "code.gitea.io/gitea/models/migrations/base"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/doctor"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
@@ -22,6 +23,19 @@ import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = &cli.Command{
|
||||
Name: "doctor",
|
||||
Usage: "Diagnose and optionally fix problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
||||
|
||||
Subcommands: []*cli.Command{
|
||||
cmdDoctorCheck,
|
||||
cmdRecreateTable,
|
||||
cmdDoctorConvert,
|
||||
},
|
||||
}
|
||||
|
||||
var cmdDoctorCheck = &cli.Command{
|
||||
Name: "check",
|
||||
Usage: "Diagnose and optionally fix problems",
|
||||
@@ -60,19 +74,6 @@ var cmdDoctorCheck = &cli.Command{
|
||||
},
|
||||
}
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = &cli.Command{
|
||||
Name: "doctor",
|
||||
Usage: "Diagnose and optionally fix problems",
|
||||
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
|
||||
|
||||
Subcommands: []*cli.Command{
|
||||
cmdDoctorCheck,
|
||||
cmdRecreateTable,
|
||||
cmdDoctorConvert,
|
||||
},
|
||||
}
|
||||
|
||||
var cmdRecreateTable = &cli.Command{
|
||||
Name: "recreate-table",
|
||||
Usage: "Recreate tables from XORM definitions and copy the data.",
|
||||
@@ -177,6 +178,7 @@ func runDoctorCheck(ctx *cli.Context) error {
|
||||
if ctx.IsSet("list") {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
|
||||
_, _ = w.Write([]byte("Default\tName\tTitle\n"))
|
||||
doctor.SortChecks(doctor.Checks)
|
||||
for _, check := range doctor.Checks {
|
||||
if check.IsDefault {
|
||||
_, _ = w.Write([]byte{'*'})
|
||||
@@ -192,26 +194,20 @@ func runDoctorCheck(ctx *cli.Context) error {
|
||||
|
||||
var checks []*doctor.Check
|
||||
if ctx.Bool("all") {
|
||||
checks = doctor.Checks
|
||||
checks = make([]*doctor.Check, len(doctor.Checks))
|
||||
copy(checks, doctor.Checks)
|
||||
} else if ctx.IsSet("run") {
|
||||
addDefault := ctx.Bool("default")
|
||||
names := ctx.StringSlice("run")
|
||||
for i, name := range names {
|
||||
names[i] = strings.ToLower(strings.TrimSpace(name))
|
||||
}
|
||||
|
||||
runNamesSet := container.SetOf(ctx.StringSlice("run")...)
|
||||
for _, check := range doctor.Checks {
|
||||
if addDefault && check.IsDefault {
|
||||
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
|
||||
checks = append(checks, check)
|
||||
continue
|
||||
}
|
||||
for _, name := range names {
|
||||
if name == check.Name {
|
||||
checks = append(checks, check)
|
||||
break
|
||||
}
|
||||
runNamesSet.Remove(check.Name)
|
||||
}
|
||||
}
|
||||
if len(runNamesSet) > 0 {
|
||||
return fmt.Errorf("unknown checks: %q", strings.Join(runNamesSet.Values(), ","))
|
||||
}
|
||||
} else {
|
||||
for _, check := range doctor.Checks {
|
||||
if check.IsDefault {
|
||||
@@ -219,6 +215,5 @@ func runDoctorCheck(ctx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
|
||||
}
|
||||
|
||||
33
cmd/doctor_test.go
Normal file
33
cmd/doctor_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/doctor"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func TestDoctorRun(t *testing.T) {
|
||||
doctor.Register(&doctor.Check{
|
||||
Title: "Test Check",
|
||||
Name: "test-check",
|
||||
Run: func(ctx context.Context, logger log.Logger, autofix bool) error { return nil },
|
||||
|
||||
SkipDatabaseInitialization: true,
|
||||
})
|
||||
app := cli.NewApp()
|
||||
app.Commands = []*cli.Command{cmdDoctorCheck}
|
||||
err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
|
||||
assert.NoError(t, err)
|
||||
err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
|
||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
||||
err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
|
||||
assert.ErrorContains(t, err, `unknown checks: "no-such"`)
|
||||
}
|
||||
@@ -491,6 +491,11 @@ INTERNAL_TOKEN=
|
||||
;; Cache successful token hashes. API tokens are stored in the DB as pbkdf2 hashes however, this means that there is a potentially significant hashing load when there are multiple API operations.
|
||||
;; This cache will store the successfully hashed tokens in a LRU cache as a balance between performance and security.
|
||||
;SUCCESSFUL_TOKENS_CACHE_SIZE = 20
|
||||
;;
|
||||
;; Reject API tokens sent in URL query string (Accept Header-based API tokens only). This avoids security vulnerabilities
|
||||
;; stemming from cached/logged plain-text API tokens.
|
||||
;; In future releases, this will become the default behavior
|
||||
;DISABLE_QUERY_AUTH_TOKEN = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -341,7 +341,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
||||
- `SSH_AUTHORIZED_PRINCIPALS_ALLOW`: **off** or **username, email**: \[off, username, email, anything\]: Specify the principals values that users are allowed to use as principal. When set to `anything` no checks are done on the principal string. When set to `off` authorized principal are not allowed to be set.
|
||||
- `SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE`: **false/true**: Gitea will create a authorized_principals file by default when it is not using the internal ssh server and `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`.
|
||||
- `SSH_AUTHORIZED_PRINCIPALS_BACKUP`: **false/true**: Enable SSH Authorized Principals Backup when rewriting all keys, default is true if `SSH_AUTHORIZED_PRINCIPALS_ALLOW` is not `off`.
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}**: Set the template for the command to passed on authorized keys. Possible keys are: AppPath, AppWorkPath, CustomConf, CustomPath, Key - where Key is a `models/asymkey.PublicKey` and the others are strings which are shellquoted.
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **`{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}`**: Set the template for the command to passed on authorized keys. Possible keys are: AppPath, AppWorkPath, CustomConf, CustomPath, Key - where Key is a `models/asymkey.PublicKey` and the others are strings which are shellquoted.
|
||||
- `SSH_SERVER_CIPHERS`: **chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com**: For the built-in SSH server, choose the ciphers to support for SSH connections, for system SSH this setting has no effect.
|
||||
- `SSH_SERVER_KEY_EXCHANGES`: **curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1**: For the built-in SSH server, choose the key exchange algorithms to support for SSH connections, for system SSH this setting has no effect.
|
||||
- `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1**: For the built-in SSH server, choose the MACs to support for SSH connections, for system SSH this setting has no effect
|
||||
@@ -1404,7 +1404,7 @@ Please note that using `self` is not recommended for most cases, as it could mak
|
||||
Additionally, it requires you to mirror all the actions you need to your Gitea instance, which may not be worth it.
|
||||
Therefore, please use `self` only if you understand what you are doing.
|
||||
|
||||
In earlier versions (<= 1.19), `DEFAULT_ACTIONS_URL` cound be set to any custom URLs like `https://gitea.com` or `http://your-git-server,https://gitea.com`, and the default value was `https://gitea.com`.
|
||||
In earlier versions (`<= 1.19`), `DEFAULT_ACTIONS_URL` cound be set to any custom URLs like `https://gitea.com` or `http://your-git-server,https://gitea.com`, and the default value was `https://gitea.com`.
|
||||
However, later updates removed those options, and now the only options are `github` and `self`, with the default value being `github`.
|
||||
However, if you want to use actions from other git server, you can use a complete URL in `uses` field, it's supported by Gitea (but not GitHub).
|
||||
Like `uses: https://gitea.com/actions/checkout@v3` or `uses: http://your-git-server/actions/checkout@v3`.
|
||||
|
||||
@@ -335,7 +335,7 @@ menu:
|
||||
- `SSH_AUTHORIZED_PRINCIPALS_ALLOW`: **off** 或 **username, email**:\[off, username, email, anything\]:指定允许用户用作 principal 的值。当设置为 `anything` 时,对 principal 字符串不执行任何检查。当设置为 `off` 时,不允许设置授权的 principal。
|
||||
- `SSH_CREATE_AUTHORIZED_PRINCIPALS_FILE`: **false/true**:当 Gitea 不使用内置 SSH 服务器且 `SSH_AUTHORIZED_PRINCIPALS_ALLOW` 不为 `off` 时,默认情况下 Gitea 会创建一个 authorized_principals 文件。
|
||||
- `SSH_AUTHORIZED_PRINCIPALS_BACKUP`: **false/true**:在重写所有密钥时启用 SSH 授权 principal 备份,默认值为 true(如果 `SSH_AUTHORIZED_PRINCIPALS_ALLOW` 不为 `off`)。
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}**:设置用于传递授权密钥的命令模板。可能的密钥是:AppPath、AppWorkPath、CustomConf、CustomPath、Key,其中 Key 是 `models/asymkey.PublicKey`,其他是 shellquoted 字符串。
|
||||
- `SSH_AUTHORIZED_KEYS_COMMAND_TEMPLATE`: **`{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}`**:设置用于传递授权密钥的命令模板。可能的密钥是:AppPath、AppWorkPath、CustomConf、CustomPath、Key,其中 Key 是 `models/asymkey.PublicKey`,其他是 shellquoted 字符串。
|
||||
- `SSH_SERVER_CIPHERS`: **chacha20-poly1305@openssh.com, aes128-ctr, aes192-ctr, aes256-ctr, aes128-gcm@openssh.com, aes256-gcm@openssh.com**:对于内置的 SSH 服务器,选择支持的 SSH 连接的加密方法,对于系统 SSH,此设置无效。
|
||||
- `SSH_SERVER_KEY_EXCHANGES`: **curve25519-sha256, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521, diffie-hellman-group14-sha256, diffie-hellman-group14-sha1**:对于内置 SSH 服务器,选择支持的 SSH 连接的密钥交换算法,对于系统 SSH,此设置无效。
|
||||
- `SSH_SERVER_MACS`: **hmac-sha2-256-etm@openssh.com, hmac-sha2-256, hmac-sha1**:对于内置 SSH 服务器,选择支持的 SSH 连接的 MAC 算法,对于系统 SSH,此设置无效。
|
||||
@@ -1344,7 +1344,7 @@ PROXY_HOSTS = *.github.com
|
||||
此外,它要求您将所有所需的操作镜像到您的 Gitea 实例,这可能不值得。
|
||||
因此,请仅在您了解自己在做什么的情况下使用 `self`。
|
||||
|
||||
在早期版本(<= 1.19)中,`DEFAULT_ACTIONS_URL` 可以设置为任何自定义 URL,例如 `https://gitea.com` 或 `http://your-git-server,https://gitea.com`,默认值为 `https://gitea.com`。
|
||||
在早期版本(`<= 1.19`)中,`DEFAULT_ACTIONS_URL` 可以设置为任何自定义 URL,例如 `https://gitea.com` 或 `http://your-git-server,https://gitea.com`,默认值为 `https://gitea.com`。
|
||||
然而,后来的更新删除了这些选项,现在唯一的选项是 `github` 和 `self`,默认值为 `github`。
|
||||
但是,如果您想要使用其他 Git 服务器中的操作,您可以在 `uses` 字段中使用完整的 URL,Gitea 支持此功能(GitHub 不支持)。
|
||||
例如 `uses: https://gitea.com/actions/checkout@v3` 或 `uses: http://your-git-server/actions/checkout@v3`。
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -55,13 +55,13 @@ PASSWD = `password`
|
||||
|
||||
要发送测试邮件以验证设置,请转到 Gitea > 站点管理 > 配置 > SMTP 邮件配置。
|
||||
|
||||
有关所有选项的完整列表,请查看[配置速查表](doc/administration/config-cheat-sheet.zh-cn.md)。
|
||||
有关所有选项的完整列表,请查看[配置速查表](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。
|
||||
|
||||
@@ -33,7 +33,7 @@ CERT_FILE = cert.pem
|
||||
KEY_FILE = key.pem
|
||||
```
|
||||
|
||||
请注意,如果您的证书由第三方证书颁发机构签名(即不是自签名的),则 cert.pem 应包含证书链。服务器证书必须是 cert.pem 中的第一个条目,后跟中介(如果有)。不必包含根证书,因为连接客户端必须已经拥有根证书才能建立信任关系。要了解有关配置值的更多信息,请查看 [配置备忘单](../config-cheat-sheet#server-server)。
|
||||
请注意,如果您的证书由第三方证书颁发机构签名(即不是自签名的),则 cert.pem 应包含证书链。服务器证书必须是 cert.pem 中的第一个条目,后跟中介(如果有)。不必包含根证书,因为连接客户端必须已经拥有根证书才能建立信任关系。要了解有关配置值的更多信息,请查看 [配置备忘单](administration/config-cheat-sheet#server-server)。
|
||||
|
||||
对于“CERT_FILE”或“KEY_FILE”字段,当文件路径是相对路径时,文件路径相对于“GITEA_CUSTOM”环境变量。它也可以是绝对路径。
|
||||
|
||||
|
||||
@@ -19,10 +19,7 @@ menu:
|
||||
|
||||
## Enabling/configuring API access
|
||||
|
||||
By default, `ENABLE_SWAGGER` is true, and
|
||||
`MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat
|
||||
Sheet](administration/config-cheat-sheet.md) for more
|
||||
information.
|
||||
By default, `ENABLE_SWAGGER` is true, and `MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat Sheet](administration/config-cheat-sheet.md) for more information.
|
||||
|
||||
## Authentication
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ menu:
|
||||
|
||||
## 开启/配置 API 访问
|
||||
|
||||
通常情况下, `ENABLE_SWAGGER` 默认开启并且参数 `MAX_RESPONSE_ITEMS` 默认为 50。您可以从 [Config Cheat
|
||||
Sheet](administration/config-cheat-sheet.md) 中获取更多配置相关信息。
|
||||
通常情况下, `ENABLE_SWAGGER` 默认开启并且参数 `MAX_RESPONSE_ITEMS` 默认为 50。您可以从 [Config Cheat Sheet](administration/config-cheat-sheet.md) 中获取更多配置相关信息。
|
||||
|
||||
## 通过 API 认证
|
||||
|
||||
|
||||
@@ -138,9 +138,9 @@ All Gitea instances have the built-in API and there is no way to disable it comp
|
||||
You can, however, disable showing its documentation by setting `ENABLE_SWAGGER` to `false` in the `api` section of your `app.ini`.
|
||||
For more information, refer to Gitea's [API docs](development/api-usage.md).
|
||||
|
||||
You can see the latest API (for example) on <https://try.gitea.io/api/swagger>.
|
||||
You can see the latest API (for example) on https://try.gitea.io/api/swagger
|
||||
|
||||
You can also see an example of the `swagger.json` file at <https://try.gitea.io/swagger.v1.json>.
|
||||
You can also see an example of the `swagger.json` file at https://try.gitea.io/swagger.v1.json
|
||||
|
||||
## Adjusting your server for public/private use
|
||||
|
||||
|
||||
@@ -142,9 +142,9 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供
|
||||
但是,您可以在app.ini的api部分将ENABLE_SWAGGER设置为false,以禁用其文档显示。
|
||||
有关更多信息,请参阅Gitea的[API文档](development/api-usage.md)。
|
||||
|
||||
您可以在上查看最新的API(例如)<https://try.gitea.io/api/swagger>。
|
||||
您可以在上查看最新的API(例如)https://try.gitea.io/api/swagger
|
||||
|
||||
您还可以在上查看`swagger.json`文件的示例 <https://try.gitea.io/swagger.v1.json>。
|
||||
您还可以在上查看`swagger.json`文件的示例 https://try.gitea.io/swagger.v1.json
|
||||
|
||||
## 调整服务器用于公共/私有使用
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ chmod 770 /etc/gitea
|
||||
- 使用 `gitea generate secret` 创建 `SECRET_KEY` 和 `INTERNAL_TOKEN`
|
||||
- 提供所有必要的密钥
|
||||
|
||||
详情参考 [命令行文档](/zh-cn/command-line/) 中有关 `gitea generate secret` 的内容。
|
||||
详情参考 [命令行文档](administration/command-line.md) 中有关 `gitea generate secret` 的内容。
|
||||
|
||||
### 配置 Gitea 工作路径
|
||||
|
||||
@@ -209,6 +209,6 @@ remote: ./hooks/pre-receive.d/gitea: line 2: [...]: No such file or directory
|
||||
|
||||
如果您没有使用 Gitea 内置的 SSH 服务器,您还需要通过在管理选项中运行任务 `Update the '.ssh/authorized_keys' file with Gitea SSH keys.` 来重新编写授权密钥文件。
|
||||
|
||||
> 更多经验总结,请参考英文版 [Troubleshooting](/en-us/install-from-binary/#troubleshooting)
|
||||
> 更多经验总结,请参考英文版 [Troubleshooting](https://docs.gitea.com/installation/install-from-binary#troubleshooting)
|
||||
|
||||
如果从本页中没有找到你需要的内容,请访问 [帮助页面](help/support.md)
|
||||
|
||||
@@ -64,7 +64,7 @@ git checkout v@version@ # or git checkout pr-xyz
|
||||
|
||||
- `go` @minGoVersion@ 或更高版本,请参阅 [这里](https://golang.org/dl/)
|
||||
- `node` @minNodeVersion@ 或更高版本,并且安装 `npm`, 请参阅 [这里](https://nodejs.org/zh-cn/download/)
|
||||
- `make`, 请参阅 [这里](/zh-cn/hacking-on-gitea/)
|
||||
- `make`, 请参阅 [这里](development/hacking-on-gitea.md)
|
||||
|
||||
为了尽可能简化编译过程,提供了各种 [make任务](https://github.com/go-gitea/gitea/blob/main/Makefile)。
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -116,8 +116,8 @@ Pre and Post steps don't have their own section in the job log user interface.
|
||||
|
||||
Previously (Pre 1.21.0), `[actions].DEFAULT_ACTIONS_URL` defaulted to `https://gitea.com`.
|
||||
We have since restricted this option to only allow two values (`github` and `self`).
|
||||
When set to `github`, the new default, Gitea will download non-fully-qualified actions from <https://github.com>.
|
||||
For example, if you use `uses: actions/checkout@v3`, it will download the checkout repository from <https://github.com/actions/checkout.git>.
|
||||
When set to `github`, the new default, Gitea will download non-fully-qualified actions from `https://github.com`.
|
||||
For example, if you use `uses: actions/checkout@v3`, it will download the checkout repository from `https://github.com/actions/checkout.git`.
|
||||
|
||||
If you want to download an action from another git hoster, you can use an absolute URL, e.g. `uses: https://gitea.com/actions/checkout@v3`.
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -23,7 +23,7 @@ menu:
|
||||
您可以按照[文档](installation/from-package.md) 来设置一个新实例或升级现有实例。
|
||||
无论您如何安装或运行Gitea,只要版本号是1.19.0或更高即可。
|
||||
|
||||
默认情况下,Actions是禁用的,因此您需要将以下内容添加到配置文件中以启用它:
|
||||
从1.21.0开始,默认情况下,Actions是启用的。如果您正在使用1.21.0之前的版本,您需要将以下内容添加到配置文件中以启用它:
|
||||
|
||||
```ini
|
||||
[actions]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -92,10 +92,9 @@ func CountUserGPGKeys(ctx context.Context, userID int64) (int64, error) {
|
||||
return db.GetEngine(ctx).Where("owner_id=? AND primary_key_id=''", userID).Count(&GPGKey{})
|
||||
}
|
||||
|
||||
// GetGPGKeyByID returns public key by given ID.
|
||||
func GetGPGKeyByID(ctx context.Context, keyID int64) (*GPGKey, error) {
|
||||
func GetGPGKeyForUserByID(ctx context.Context, ownerID, keyID int64) (*GPGKey, error) {
|
||||
key := new(GPGKey)
|
||||
has, err := db.GetEngine(ctx).ID(keyID).Get(key)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND owner_id=?", keyID, ownerID).Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
@@ -225,7 +224,7 @@ func deleteGPGKey(ctx context.Context, keyID string) (int64, error) {
|
||||
|
||||
// DeleteGPGKey deletes GPG key information in database.
|
||||
func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err error) {
|
||||
key, err := GetGPGKeyByID(ctx, id)
|
||||
key, err := GetGPGKeyForUserByID(ctx, doer.ID, id)
|
||||
if err != nil {
|
||||
if IsErrGPGKeyNotExist(err) {
|
||||
return nil
|
||||
@@ -233,11 +232,6 @@ func DeleteGPGKey(ctx context.Context, doer *user_model.User, id int64) (err err
|
||||
return fmt.Errorf("GetPublicKeyByID: %w", err)
|
||||
}
|
||||
|
||||
// Check if user has access to delete this key.
|
||||
if !doer.IsAdmin && doer.ID != key.OwnerID {
|
||||
return ErrGPGKeyAccessDenied{doer.ID, key.ID}
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -178,6 +178,15 @@ func GetByBean(ctx context.Context, bean any) (bool, error) {
|
||||
return GetEngine(ctx).Get(bean)
|
||||
}
|
||||
|
||||
func Exist[T any](ctx context.Context, cond builder.Cond) (bool, error) {
|
||||
if !cond.IsValid() {
|
||||
return false, ErrConditionRequired{}
|
||||
}
|
||||
|
||||
var bean T
|
||||
return GetEngine(ctx).Where(cond).NoAutoCondition().Exist(&bean)
|
||||
}
|
||||
|
||||
// DeleteByBean deletes all records according non-empty fields of the bean as conditions.
|
||||
func DeleteByBean(ctx context.Context, bean any) (int64, error) {
|
||||
return GetEngine(ctx).Delete(bean)
|
||||
|
||||
@@ -72,3 +72,21 @@ func (err ErrNotExist) Error() string {
|
||||
func (err ErrNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrConditionRequired represents an error which require condition.
|
||||
type ErrConditionRequired struct{}
|
||||
|
||||
// IsErrConditionRequired checks if an error is an ErrConditionRequired
|
||||
func IsErrConditionRequired(err error) bool {
|
||||
_, ok := err.(ErrConditionRequired)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrConditionRequired) Error() string {
|
||||
return "condition is required"
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrConditionRequired) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
@@ -66,3 +66,12 @@
|
||||
tree_path: "README.md"
|
||||
created_unix: 946684812
|
||||
invalidated: true
|
||||
|
||||
-
|
||||
id: 8
|
||||
type: 0 # comment
|
||||
poster_id: 2
|
||||
issue_id: 4 # in repo_id 2
|
||||
content: "comment in private pository"
|
||||
created_unix: 946684811
|
||||
updated_unix: 946684811
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
priority: 0
|
||||
is_closed: true
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
num_comments: 1
|
||||
created_unix: 946684830
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
||||
@@ -205,10 +205,9 @@ func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateBranch updates the branch information in the database. If the branch exist, it will update latest commit of this branch information
|
||||
// If it doest not exist, insert a new record into database
|
||||
func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) error {
|
||||
cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
|
||||
// UpdateBranch updates the branch information in the database.
|
||||
func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) (int64, error) {
|
||||
return db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName).
|
||||
Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted, updated_unix").
|
||||
Update(&Branch{
|
||||
CommitID: commit.ID.String(),
|
||||
@@ -217,21 +216,6 @@ func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string
|
||||
CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
|
||||
IsDeleted: false,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cnt > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return db.Insert(ctx, &Branch{
|
||||
RepoID: repoID,
|
||||
Name: branchName,
|
||||
CommitID: commit.ID.String(),
|
||||
CommitMessage: commit.Summary(),
|
||||
PusherID: pusherID,
|
||||
CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()),
|
||||
})
|
||||
}
|
||||
|
||||
// AddDeletedBranch adds a deleted branch to the database
|
||||
@@ -308,6 +292,17 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
var branch Branch
|
||||
exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if !exist || branch.IsDeleted {
|
||||
return ErrBranchNotExist{
|
||||
RepoID: repo.ID,
|
||||
BranchName: from,
|
||||
}
|
||||
}
|
||||
|
||||
// 1. update branch in database
|
||||
if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{
|
||||
Name: to,
|
||||
|
||||
@@ -73,7 +73,7 @@ type FindBranchOptions struct {
|
||||
Keyword string
|
||||
}
|
||||
|
||||
func (opts *FindBranchOptions) Cond() builder.Cond {
|
||||
func (opts FindBranchOptions) ToConds() builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.RepoID > 0 {
|
||||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
|
||||
@@ -92,7 +92,7 @@ func (opts *FindBranchOptions) Cond() builder.Cond {
|
||||
}
|
||||
|
||||
func CountBranches(ctx context.Context, opts FindBranchOptions) (int64, error) {
|
||||
return db.GetEngine(ctx).Where(opts.Cond()).Count(&Branch{})
|
||||
return db.GetEngine(ctx).Where(opts.ToConds()).Count(&Branch{})
|
||||
}
|
||||
|
||||
func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
|
||||
@@ -108,7 +108,7 @@ func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session {
|
||||
}
|
||||
|
||||
func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, error) {
|
||||
sess := db.GetEngine(ctx).Where(opts.Cond())
|
||||
sess := db.GetEngine(ctx).Where(opts.ToConds())
|
||||
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func FindBranches(ctx context.Context, opts FindBranchOptions) (BranchList, erro
|
||||
}
|
||||
|
||||
func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, error) {
|
||||
sess := db.GetEngine(ctx).Select("name").Where(opts.Cond())
|
||||
sess := db.GetEngine(ctx).Select("name").Where(opts.ToConds())
|
||||
if opts.PageSize > 0 && !opts.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &opts.ListOptions)
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func TestAddDeletedBranch(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
|
||||
_, err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1016,6 +1016,7 @@ type FindCommentsOptions struct {
|
||||
Type CommentType
|
||||
IssueIDs []int64
|
||||
Invalidated util.OptionalBool
|
||||
IsPull util.OptionalBool
|
||||
}
|
||||
|
||||
// ToConds implements FindOptions interface
|
||||
@@ -1050,6 +1051,9 @@ func (opts *FindCommentsOptions) ToConds() builder.Cond {
|
||||
if !opts.Invalidated.IsNone() {
|
||||
cond = cond.And(builder.Eq{"comment.invalidated": opts.Invalidated.IsTrue()})
|
||||
}
|
||||
if opts.IsPull != util.OptionalBoolNone {
|
||||
cond = cond.And(builder.Eq{"issue.is_pull": opts.IsPull.IsTrue()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
@@ -1057,7 +1061,7 @@ func (opts *FindCommentsOptions) ToConds() builder.Cond {
|
||||
func FindComments(ctx context.Context, opts *FindCommentsOptions) (CommentList, error) {
|
||||
comments := make([]*Comment, 0, 10)
|
||||
sess := db.GetEngine(ctx).Where(opts.ToConds())
|
||||
if opts.RepoID > 0 {
|
||||
if opts.RepoID > 0 || opts.IsPull != util.OptionalBoolNone {
|
||||
sess.Join("INNER", "issue", "issue.id = comment.issue_id")
|
||||
}
|
||||
|
||||
|
||||
@@ -218,9 +218,9 @@ func GetIssueContentHistoryByID(dbCtx context.Context, id int64) (*ContentHistor
|
||||
}
|
||||
|
||||
// GetIssueContentHistoryAndPrev get a history and the previous non-deleted history (to compare)
|
||||
func GetIssueContentHistoryAndPrev(dbCtx context.Context, id int64) (history, prevHistory *ContentHistory, err error) {
|
||||
func GetIssueContentHistoryAndPrev(dbCtx context.Context, issueID, id int64) (history, prevHistory *ContentHistory, err error) {
|
||||
history = &ContentHistory{}
|
||||
has, err := db.GetEngine(dbCtx).ID(id).Get(history)
|
||||
has, err := db.GetEngine(dbCtx).Where("id=? AND issue_id=?", id, issueID).Get(history)
|
||||
if err != nil {
|
||||
log.Error("failed to get issue content history %v. err=%v", id, err)
|
||||
return nil, nil, err
|
||||
|
||||
@@ -58,13 +58,13 @@ func TestContentHistory(t *testing.T) {
|
||||
hasHistory2, _ := issues_model.HasIssueContentHistory(dbCtx, 10, 1)
|
||||
assert.False(t, hasHistory2)
|
||||
|
||||
h6, h6Prev, _ := issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6)
|
||||
h6, h6Prev, _ := issues_model.GetIssueContentHistoryAndPrev(dbCtx, 10, 6)
|
||||
assert.EqualValues(t, 6, h6.ID)
|
||||
assert.EqualValues(t, 5, h6Prev.ID)
|
||||
|
||||
// soft-delete
|
||||
_ = issues_model.SoftDeleteIssueContentHistory(dbCtx, 5)
|
||||
h6, h6Prev, _ = issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6)
|
||||
h6, h6Prev, _ = issues_model.GetIssueContentHistoryAndPrev(dbCtx, 10, 6)
|
||||
assert.EqualValues(t, 6, h6.ID)
|
||||
assert.EqualValues(t, 4, h6Prev.ID)
|
||||
|
||||
|
||||
@@ -32,7 +32,12 @@ func AddGitSizeAndLFSSizeToRepositoryTable(x *xorm.Engine) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = sess.Exec(`UPDATE repository SET git_size = size - lfs_size`)
|
||||
_, err = sess.Exec(`UPDATE repository SET size = 0 WHERE size IS NULL`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = sess.Exec(`UPDATE repository SET git_size = size - lfs_size WHERE size > lfs_size`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -311,6 +311,18 @@ func GetProjectByID(ctx context.Context, id int64) (*Project, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// GetProjectForRepoByID returns the projects in a repository
|
||||
func GetProjectForRepoByID(ctx context.Context, repoID, id int64) (*Project, error) {
|
||||
p := new(Project)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", id, repoID).Get(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrProjectNotExist{ID: id}
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// UpdateProject updates project properties
|
||||
func UpdateProject(ctx context.Context, p *Project) error {
|
||||
if !IsCardTypeValid(p.CardType) {
|
||||
|
||||
@@ -207,6 +207,21 @@ func GetReleaseByID(ctx context.Context, id int64) (*Release, error) {
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// GetReleaseForRepoByID returns release with given ID.
|
||||
func GetReleaseForRepoByID(ctx context.Context, repoID, id int64) (*Release, error) {
|
||||
rel := new(Release)
|
||||
has, err := db.GetEngine(ctx).
|
||||
Where("id=? AND repo_id=?", id, repoID).
|
||||
Get(rel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrReleaseNotExist{id, ""}
|
||||
}
|
||||
|
||||
return rel, nil
|
||||
}
|
||||
|
||||
// FindReleasesOptions describes the conditions to Find releases
|
||||
type FindReleasesOptions struct {
|
||||
db.ListOptions
|
||||
|
||||
@@ -47,6 +47,14 @@ func (err ErrUserDoesNotHaveAccessToRepo) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
type ErrRepoIsArchived struct {
|
||||
Repo *Repository
|
||||
}
|
||||
|
||||
func (err ErrRepoIsArchived) Error() string {
|
||||
return fmt.Sprintf("%s is archived", err.Repo.LogString())
|
||||
}
|
||||
|
||||
var (
|
||||
reservedRepoNames = []string{".", "..", "-"}
|
||||
reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
|
||||
@@ -654,6 +662,14 @@ func (repo *Repository) GetTrustModel() TrustModelType {
|
||||
return trustModel
|
||||
}
|
||||
|
||||
// MustNotBeArchived returns ErrRepoIsArchived if the repo is archived
|
||||
func (repo *Repository) MustNotBeArchived() error {
|
||||
if repo.IsArchived {
|
||||
return ErrRepoIsArchived{Repo: repo}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// __________ .__ __
|
||||
// \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
|
||||
// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
|
||||
|
||||
@@ -652,12 +652,12 @@ func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) bu
|
||||
userOrgTeamUnitRepoCond("`repository`.id", user.ID, unitType),
|
||||
)
|
||||
}
|
||||
cond = cond.Or(
|
||||
// 4. Repositories that we directly own
|
||||
builder.Eq{"`repository`.owner_id": user.ID},
|
||||
// 4. Repositories that we directly own
|
||||
cond = cond.Or(builder.Eq{"`repository`.owner_id": user.ID})
|
||||
if !user.IsRestricted {
|
||||
// 5. Be able to see all public repos in private organizations that we are an org_user of
|
||||
userOrgPublicRepoCond(user.ID),
|
||||
)
|
||||
cond = cond.Or(userOrgPublicRepoCond(user.ID))
|
||||
}
|
||||
}
|
||||
|
||||
return cond
|
||||
|
||||
@@ -81,7 +81,7 @@ func SetSettings(ctx context.Context, settings map[string]string) error {
|
||||
return err
|
||||
}
|
||||
for k, v := range settings {
|
||||
res, err := e.Exec("UPDATE system_setting SET setting_value=? WHERE setting_key=?", v, k)
|
||||
res, err := e.Exec("UPDATE system_setting SET version=version+1, setting_value=? WHERE setting_key=?", v, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -115,24 +115,26 @@ func (d *dbConfigCachedGetter) GetValue(ctx context.Context, key string) (v stri
|
||||
|
||||
func (d *dbConfigCachedGetter) GetRevision(ctx context.Context) int {
|
||||
d.mu.RLock()
|
||||
defer d.mu.RUnlock()
|
||||
if time.Since(d.cacheTime) < time.Second {
|
||||
return d.revision
|
||||
cachedDuration := time.Since(d.cacheTime)
|
||||
cachedRevision := d.revision
|
||||
d.mu.RUnlock()
|
||||
|
||||
if cachedDuration < time.Second {
|
||||
return cachedRevision
|
||||
}
|
||||
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if GetRevision(ctx) != d.revision {
|
||||
d.mu.RUnlock()
|
||||
d.mu.Lock()
|
||||
rev, set, err := GetAllSettings(ctx)
|
||||
if err != nil {
|
||||
log.Error("Unable to get all settings: %v", err)
|
||||
} else {
|
||||
d.cacheTime = time.Now()
|
||||
d.revision = rev
|
||||
d.settings = set
|
||||
}
|
||||
d.mu.Unlock()
|
||||
d.mu.RLock()
|
||||
}
|
||||
d.cacheTime = time.Now()
|
||||
return d.revision
|
||||
}
|
||||
|
||||
|
||||
@@ -39,4 +39,16 @@ func TestSettings(t *testing.T) {
|
||||
assert.EqualValues(t, 3, rev)
|
||||
assert.Len(t, settings, 2)
|
||||
assert.EqualValues(t, "false", settings[keyName])
|
||||
|
||||
// setting the same value should not trigger DuplicateKey error, and the "version" should be increased
|
||||
setting := &system.Setting{SettingKey: keyName}
|
||||
_, err = db.GetByBean(db.DefaultContext, setting)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, setting.Version)
|
||||
err = system.SetSettings(db.DefaultContext, map[string]string{keyName: "false"})
|
||||
assert.NoError(t, err)
|
||||
setting = &system.Setting{SettingKey: keyName}
|
||||
_, err = db.GetByBean(db.DefaultContext, setting)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, setting.Version)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ func NewReplaceUser(name string) *User {
|
||||
}
|
||||
|
||||
const (
|
||||
GhostUserID = -1
|
||||
ActionsUserID = -2
|
||||
ActionsUserName = "gitea-actions"
|
||||
ActionsFullName = "Gitea Actions"
|
||||
|
||||
@@ -392,39 +392,40 @@ func CreateWebhooks(ctx context.Context, ws []*Webhook) error {
|
||||
return db.Insert(ctx, ws)
|
||||
}
|
||||
|
||||
// getWebhook uses argument bean as query condition,
|
||||
// ID must be specified and do not assign unnecessary fields.
|
||||
func getWebhook(bean *Webhook) (*Webhook, error) {
|
||||
has, err := db.GetEngine(db.DefaultContext).Get(bean)
|
||||
// GetWebhookByID returns webhook of repository by given ID.
|
||||
func GetWebhookByID(ctx context.Context, id int64) (*Webhook, error) {
|
||||
bean := new(Webhook)
|
||||
has, err := db.GetEngine(ctx).ID(id).Get(bean)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrWebhookNotExist{ID: bean.ID}
|
||||
return nil, ErrWebhookNotExist{ID: id}
|
||||
}
|
||||
return bean, nil
|
||||
}
|
||||
|
||||
// GetWebhookByID returns webhook of repository by given ID.
|
||||
func GetWebhookByID(id int64) (*Webhook, error) {
|
||||
return getWebhook(&Webhook{
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
|
||||
// GetWebhookByRepoID returns webhook of repository by given ID.
|
||||
func GetWebhookByRepoID(repoID, id int64) (*Webhook, error) {
|
||||
return getWebhook(&Webhook{
|
||||
ID: id,
|
||||
RepoID: repoID,
|
||||
})
|
||||
func GetWebhookByRepoID(ctx context.Context, repoID, id int64) (*Webhook, error) {
|
||||
webhook := new(Webhook)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND repo_id=?", id, repoID).Get(webhook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrWebhookNotExist{ID: id}
|
||||
}
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
// GetWebhookByOwnerID returns webhook of a user or organization by given ID.
|
||||
func GetWebhookByOwnerID(ownerID, id int64) (*Webhook, error) {
|
||||
return getWebhook(&Webhook{
|
||||
ID: id,
|
||||
OwnerID: ownerID,
|
||||
})
|
||||
func GetWebhookByOwnerID(ctx context.Context, ownerID, id int64) (*Webhook, error) {
|
||||
webhook := new(Webhook)
|
||||
has, err := db.GetEngine(ctx).Where("id=? AND owner_id=?", id, ownerID).Get(webhook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, ErrWebhookNotExist{ID: id}
|
||||
}
|
||||
return webhook, nil
|
||||
}
|
||||
|
||||
// ListWebhookOptions are options to filter webhooks on ListWebhooksByOpts
|
||||
@@ -482,20 +483,20 @@ func UpdateWebhookLastStatus(w *Webhook) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// deleteWebhook uses argument bean as query condition,
|
||||
// DeleteWebhookByID uses argument bean as query condition,
|
||||
// ID must be specified and do not assign unnecessary fields.
|
||||
func deleteWebhook(bean *Webhook) (err error) {
|
||||
ctx, committer, err := db.TxContext(db.DefaultContext)
|
||||
func DeleteWebhookByID(ctx context.Context, id int64) (err error) {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
if count, err := db.DeleteByBean(ctx, bean); err != nil {
|
||||
if count, err := db.DeleteByID(ctx, id, new(Webhook)); err != nil {
|
||||
return err
|
||||
} else if count == 0 {
|
||||
return ErrWebhookNotExist{ID: bean.ID}
|
||||
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: bean.ID}); err != nil {
|
||||
return ErrWebhookNotExist{ID: id}
|
||||
} else if _, err = db.DeleteByBean(ctx, &HookTask{HookID: id}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -503,17 +504,17 @@ func deleteWebhook(bean *Webhook) (err error) {
|
||||
}
|
||||
|
||||
// DeleteWebhookByRepoID deletes webhook of repository by given ID.
|
||||
func DeleteWebhookByRepoID(repoID, id int64) error {
|
||||
return deleteWebhook(&Webhook{
|
||||
ID: id,
|
||||
RepoID: repoID,
|
||||
})
|
||||
func DeleteWebhookByRepoID(ctx context.Context, repoID, id int64) error {
|
||||
if _, err := GetWebhookByRepoID(ctx, repoID, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return DeleteWebhookByID(ctx, id)
|
||||
}
|
||||
|
||||
// DeleteWebhookByOwnerID deletes webhook of a user or organization by given ID.
|
||||
func DeleteWebhookByOwnerID(ownerID, id int64) error {
|
||||
return deleteWebhook(&Webhook{
|
||||
ID: id,
|
||||
OwnerID: ownerID,
|
||||
})
|
||||
func DeleteWebhookByOwnerID(ctx context.Context, ownerID, id int64) error {
|
||||
if _, err := GetWebhookByOwnerID(ctx, ownerID, id); err != nil {
|
||||
return err
|
||||
}
|
||||
return DeleteWebhookByID(ctx, id)
|
||||
}
|
||||
|
||||
@@ -101,22 +101,22 @@ func TestCreateWebhook(t *testing.T) {
|
||||
|
||||
func TestGetWebhookByRepoID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hook, err := GetWebhookByRepoID(1, 1)
|
||||
hook, err := GetWebhookByRepoID(db.DefaultContext, 1, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), hook.ID)
|
||||
|
||||
_, err = GetWebhookByRepoID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
_, err = GetWebhookByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
|
||||
func TestGetWebhookByOwnerID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
hook, err := GetWebhookByOwnerID(3, 3)
|
||||
hook, err := GetWebhookByOwnerID(db.DefaultContext, 3, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(3), hook.ID)
|
||||
|
||||
_, err = GetWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
_, err = GetWebhookByOwnerID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
@@ -174,10 +174,10 @@ func TestUpdateWebhook(t *testing.T) {
|
||||
func TestDeleteWebhookByRepoID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2, RepoID: 1})
|
||||
assert.NoError(t, DeleteWebhookByRepoID(1, 2))
|
||||
assert.NoError(t, DeleteWebhookByRepoID(db.DefaultContext, 1, 2))
|
||||
unittest.AssertNotExistsBean(t, &Webhook{ID: 2, RepoID: 1})
|
||||
|
||||
err := DeleteWebhookByRepoID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
err := DeleteWebhookByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
@@ -185,10 +185,10 @@ func TestDeleteWebhookByRepoID(t *testing.T) {
|
||||
func TestDeleteWebhookByOwnerID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 3, OwnerID: 3})
|
||||
assert.NoError(t, DeleteWebhookByOwnerID(3, 3))
|
||||
assert.NoError(t, DeleteWebhookByOwnerID(db.DefaultContext, 3, 3))
|
||||
unittest.AssertNotExistsBean(t, &Webhook{ID: 3, OwnerID: 3})
|
||||
|
||||
err := DeleteWebhookByOwnerID(unittest.NonexistentID, unittest.NonexistentID)
|
||||
err := DeleteWebhookByOwnerID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrWebhookNotExist(err))
|
||||
}
|
||||
|
||||
@@ -560,6 +560,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 {
|
||||
|
||||
@@ -79,6 +79,7 @@ var Checks []*Check
|
||||
|
||||
// RunChecks runs the doctor checks for the provided list
|
||||
func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) error {
|
||||
SortChecks(checks)
|
||||
// the checks output logs by a special logger, they do not use the default logger
|
||||
logger := log.BaseLoggerToGeneralLogger(&doctorCheckLogger{colorize: colorize})
|
||||
loggerStep := log.BaseLoggerToGeneralLogger(&doctorCheckStepLogger{colorize: colorize})
|
||||
@@ -104,20 +105,23 @@ func RunChecks(ctx context.Context, colorize, autofix bool, checks []*Check) err
|
||||
logger.Info("OK")
|
||||
}
|
||||
}
|
||||
logger.Info("\nAll done.")
|
||||
logger.Info("\nAll done (checks: %d).", len(checks))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Register registers a command with the list
|
||||
func Register(command *Check) {
|
||||
Checks = append(Checks, command)
|
||||
sort.SliceStable(Checks, func(i, j int) bool {
|
||||
if Checks[i].Priority == Checks[j].Priority {
|
||||
return Checks[i].Name < Checks[j].Name
|
||||
}
|
||||
|
||||
func SortChecks(checks []*Check) {
|
||||
sort.SliceStable(checks, func(i, j int) bool {
|
||||
if checks[i].Priority == checks[j].Priority {
|
||||
return checks[i].Name < checks[j].Name
|
||||
}
|
||||
if Checks[i].Priority == 0 {
|
||||
if checks[i].Priority == 0 {
|
||||
return false
|
||||
}
|
||||
return Checks[i].Priority < Checks[j].Priority
|
||||
return checks[i].Priority < checks[j].Priority
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -39,7 +39,7 @@ func deleteOrphanedRepos(ctx context.Context) (int64, error) {
|
||||
for {
|
||||
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
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -18,8 +19,10 @@ import (
|
||||
|
||||
// BlamePart represents block of blame - continuous lines with one sha
|
||||
type BlamePart struct {
|
||||
Sha string
|
||||
Lines []string
|
||||
Sha string
|
||||
Lines []string
|
||||
PreviousSha string
|
||||
PreviousPath string
|
||||
}
|
||||
|
||||
// BlameReader returns part of file blame one by one
|
||||
@@ -43,30 +46,38 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
|
||||
var blamePart *BlamePart
|
||||
|
||||
if r.lastSha != nil {
|
||||
blamePart = &BlamePart{*r.lastSha, make([]string, 0)}
|
||||
blamePart = &BlamePart{
|
||||
Sha: *r.lastSha,
|
||||
Lines: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
var line []byte
|
||||
var lineBytes []byte
|
||||
var isPrefix bool
|
||||
var err error
|
||||
|
||||
for err != io.EOF {
|
||||
line, isPrefix, err = r.bufferedReader.ReadLine()
|
||||
lineBytes, isPrefix, err = r.bufferedReader.ReadLine()
|
||||
if err != nil && err != io.EOF {
|
||||
return blamePart, err
|
||||
}
|
||||
|
||||
if len(line) == 0 {
|
||||
if len(lineBytes) == 0 {
|
||||
// isPrefix will be false
|
||||
continue
|
||||
}
|
||||
|
||||
lines := shaLineRegex.FindSubmatch(line)
|
||||
line := string(lineBytes)
|
||||
|
||||
lines := shaLineRegex.FindStringSubmatch(line)
|
||||
if lines != nil {
|
||||
sha1 := string(lines[1])
|
||||
sha1 := lines[1]
|
||||
|
||||
if blamePart == nil {
|
||||
blamePart = &BlamePart{sha1, make([]string, 0)}
|
||||
blamePart = &BlamePart{
|
||||
Sha: sha1,
|
||||
Lines: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
if blamePart.Sha != sha1 {
|
||||
@@ -81,9 +92,11 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
|
||||
return blamePart, nil
|
||||
}
|
||||
} else if line[0] == '\t' {
|
||||
code := line[1:]
|
||||
|
||||
blamePart.Lines = append(blamePart.Lines, string(code))
|
||||
blamePart.Lines = append(blamePart.Lines, line[1:])
|
||||
} else if strings.HasPrefix(line, "previous ") {
|
||||
parts := strings.SplitN(line[len("previous "):], " ", 2)
|
||||
blamePart.PreviousSha = parts[0]
|
||||
blamePart.PreviousPath = parts[1]
|
||||
}
|
||||
|
||||
// need to munch to end of line...
|
||||
|
||||
@@ -24,15 +24,17 @@ func TestReadingBlameOutput(t *testing.T) {
|
||||
|
||||
parts := []*BlamePart{
|
||||
{
|
||||
"72866af952e98d02a73003501836074b286a78f6",
|
||||
[]string{
|
||||
Sha: "72866af952e98d02a73003501836074b286a78f6",
|
||||
Lines: []string{
|
||||
"# test_repo",
|
||||
"Test repository for testing migration from github to gitea",
|
||||
},
|
||||
},
|
||||
{
|
||||
"f32b0a9dfd09a60f616f29158f772cedd89942d2",
|
||||
[]string{"", "Do not make any changes to this repo it is used for unit testing"},
|
||||
Sha: "f32b0a9dfd09a60f616f29158f772cedd89942d2",
|
||||
Lines: []string{"", "Do not make any changes to this repo it is used for unit testing"},
|
||||
PreviousSha: "72866af952e98d02a73003501836074b286a78f6",
|
||||
PreviousPath: "README.md",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -64,16 +66,18 @@ func TestReadingBlameOutput(t *testing.T) {
|
||||
|
||||
full := []*BlamePart{
|
||||
{
|
||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
[]string{"line", "line"},
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line"},
|
||||
},
|
||||
{
|
||||
"45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||
[]string{"changed line"},
|
||||
Sha: "45fb6cbc12f970b04eacd5cd4165edd11c8d7376",
|
||||
Lines: []string{"changed line"},
|
||||
PreviousSha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
PreviousPath: "blame.txt",
|
||||
},
|
||||
{
|
||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
[]string{"line", "line", ""},
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line", ""},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -89,8 +93,8 @@ func TestReadingBlameOutput(t *testing.T) {
|
||||
Bypass: false,
|
||||
Parts: []*BlamePart{
|
||||
{
|
||||
"af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
[]string{"line", "line", "changed line", "line", "line", ""},
|
||||
Sha: "af7486bd54cfc39eea97207ca666aa69c9d6df93",
|
||||
Lines: []string{"line", "line", "changed line", "line", "line", ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -43,8 +43,9 @@ func (c *Commit) Message() string {
|
||||
}
|
||||
|
||||
// Summary returns first line of commit message.
|
||||
// The string is forced to be valid UTF8
|
||||
func (c *Commit) Summary() string {
|
||||
return strings.Split(strings.TrimSpace(c.CommitMessage), "\n")[0]
|
||||
return strings.ToValidUTF8(strings.Split(strings.TrimSpace(c.CommitMessage), "\n")[0], "?")
|
||||
}
|
||||
|
||||
// ParentID returns oid of n-th parent (0-based index).
|
||||
|
||||
@@ -8,6 +8,7 @@ package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
@@ -52,32 +53,46 @@ func (repo *Repository) IsBranchExist(name string) bool {
|
||||
|
||||
// GetBranches returns branches from the repository, skipping "skip" initial branches and
|
||||
// returning at most "limit" branches, or all branches if "limit" is 0.
|
||||
// Branches are returned with sort of `-commiterdate` as the nogogit
|
||||
// implementation. This requires full fetch, sort and then the
|
||||
// skip/limit applies later as gogit returns in undefined order.
|
||||
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
|
||||
var branchNames []string
|
||||
type BranchData struct {
|
||||
name string
|
||||
committerDate int64
|
||||
}
|
||||
var branchData []BranchData
|
||||
|
||||
branches, err := repo.gogitRepo.Branches()
|
||||
branchIter, err := repo.gogitRepo.Branches()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
i := 0
|
||||
count := 0
|
||||
_ = branches.ForEach(func(branch *plumbing.Reference) error {
|
||||
count++
|
||||
if i < skip {
|
||||
i++
|
||||
return nil
|
||||
} else if limit != 0 && count > skip+limit {
|
||||
_ = branchIter.ForEach(func(branch *plumbing.Reference) error {
|
||||
obj, err := repo.gogitRepo.CommitObject(branch.Hash())
|
||||
if err != nil {
|
||||
// skip branch if can't find commit
|
||||
return nil
|
||||
}
|
||||
|
||||
branchNames = append(branchNames, strings.TrimPrefix(branch.Name().String(), BranchPrefix))
|
||||
branchData = append(branchData, BranchData{strings.TrimPrefix(branch.Name().String(), BranchPrefix), obj.Committer.When.Unix()})
|
||||
return nil
|
||||
})
|
||||
|
||||
// TODO: Sort?
|
||||
sort.Slice(branchData, func(i, j int) bool {
|
||||
return !(branchData[i].committerDate < branchData[j].committerDate)
|
||||
})
|
||||
|
||||
return branchNames, count, nil
|
||||
var branchNames []string
|
||||
maxPos := len(branchData)
|
||||
if limit > 0 {
|
||||
maxPos = min(skip+limit, maxPos)
|
||||
}
|
||||
for i := skip; i < maxPos; i++ {
|
||||
branchNames = append(branchNames, branchData[i].name)
|
||||
}
|
||||
|
||||
return branchNames, len(branchData), nil
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
|
||||
@@ -211,10 +211,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
||||
skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits)
|
||||
|
||||
searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(options.Keyword, &meilisearch.SearchRequest{
|
||||
Filter: query.Statement(),
|
||||
Limit: int64(limit),
|
||||
Offset: int64(skip),
|
||||
Sort: sortBy,
|
||||
Filter: query.Statement(),
|
||||
Limit: int64(limit),
|
||||
Offset: int64(skip),
|
||||
Sort: sortBy,
|
||||
MatchingStrategy: "all",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -160,24 +160,25 @@ const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | o
|
||||
// getDirectorySize returns the disk consumption for a given path
|
||||
func getDirectorySize(path string) (int64, error) {
|
||||
var size int64
|
||||
err := filepath.WalkDir(path, func(_ string, info os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) { // ignore the error because the file maybe deleted during traversing.
|
||||
return nil
|
||||
}
|
||||
err := filepath.WalkDir(path, func(_ string, entry os.DirEntry, err error) error {
|
||||
if os.IsNotExist(err) { // ignore the error because some files (like temp/lock file) may be deleted during traversing.
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
if entry.IsDir() {
|
||||
return nil
|
||||
}
|
||||
f, err := info.Info()
|
||||
if err != nil {
|
||||
info, err := entry.Info()
|
||||
if os.IsNotExist(err) { // ignore the error as above
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if (f.Mode() & notRegularFileMode) == 0 {
|
||||
size += f.Size()
|
||||
if (info.Mode() & notRegularFileMode) == 0 {
|
||||
size += info.Size()
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
})
|
||||
return size, err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var Attachment = struct {
|
||||
}{
|
||||
Storage: &Storage{},
|
||||
AllowedTypes: ".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip",
|
||||
MaxSize: 4,
|
||||
MaxSize: 2048,
|
||||
MaxFiles: 5,
|
||||
Enabled: true,
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func loadAttachmentFrom(rootCfg ConfigProvider) (err error) {
|
||||
}
|
||||
|
||||
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip")
|
||||
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
|
||||
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(2048)
|
||||
Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5)
|
||||
Attachment.Enabled = sec.Key("ENABLED").MustBool(true)
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -109,7 +108,7 @@ func DBConnStr() (string, error) {
|
||||
connStr = fmt.Sprintf("%s:%s@%s(%s)/%s%scharset=%s&parseTime=true&tls=%s",
|
||||
Database.User, Database.Passwd, connType, Database.Host, Database.Name, paramSep, Database.MysqlCharset, tls)
|
||||
case "postgres":
|
||||
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, paramSep, Database.SSLMode)
|
||||
connStr = getPostgreSQLConnectionString(Database.Host, Database.User, Database.Passwd, Database.Name, Database.SSLMode)
|
||||
case "mssql":
|
||||
host, port := ParseMSSQLHostPort(Database.Host)
|
||||
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, Database.Name, Database.User, Database.Passwd)
|
||||
@@ -117,7 +116,7 @@ func DBConnStr() (string, error) {
|
||||
if !EnableSQLite3 {
|
||||
return "", errors.New("this Gitea binary was not built with SQLite3 support")
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(Database.Path), os.ModePerm); err != nil {
|
||||
return "", fmt.Errorf("Failed to create directories: %w", err)
|
||||
}
|
||||
journalMode := ""
|
||||
@@ -157,7 +156,8 @@ func parsePostgreSQLHostPort(info string) (host, port string) {
|
||||
return host, port
|
||||
}
|
||||
|
||||
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbParam, dbsslMode string) (connStr string) {
|
||||
func getPostgreSQLConnectionString(dbHost, dbUser, dbPasswd, dbName, dbsslMode string) (connStr string) {
|
||||
dbName, dbParam, _ := strings.Cut(dbName, "?")
|
||||
host, port := parsePostgreSQLHostPort(dbHost)
|
||||
connURL := url.URL{
|
||||
Scheme: "postgres",
|
||||
|
||||
@@ -59,38 +59,39 @@ func Test_parsePostgreSQLHostPort(t *testing.T) {
|
||||
func Test_getPostgreSQLConnectionString(t *testing.T) {
|
||||
tests := []struct {
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Passwd string
|
||||
Name string
|
||||
Param string
|
||||
SSLMode string
|
||||
Output string
|
||||
}{
|
||||
{
|
||||
Host: "/tmp/pg.sock",
|
||||
Port: "4321",
|
||||
User: "testuser",
|
||||
Passwd: "space space !#$%^^%^```-=?=",
|
||||
Name: "gitea",
|
||||
Param: "",
|
||||
SSLMode: "false",
|
||||
Output: "postgres://testuser:space%20space%20%21%23$%25%5E%5E%25%5E%60%60%60-=%3F=@:5432/gitea?host=%2Ftmp%2Fpg.sock&sslmode=false",
|
||||
},
|
||||
{
|
||||
Host: "localhost",
|
||||
Port: "1234",
|
||||
User: "pgsqlusername",
|
||||
Passwd: "I love Gitea!",
|
||||
Name: "gitea",
|
||||
Param: "",
|
||||
SSLMode: "true",
|
||||
Output: "postgres://pgsqlusername:I%20love%20Gitea%21@localhost:5432/gitea?sslmode=true",
|
||||
},
|
||||
{
|
||||
Host: "localhost:1234",
|
||||
User: "user",
|
||||
Passwd: "pass",
|
||||
Name: "gitea?param=1",
|
||||
Output: "postgres://user:pass@localhost:1234/gitea?param=1&sslmode=",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.Param, test.SSLMode)
|
||||
connStr := getPostgreSQLConnectionString(test.Host, test.User, test.Passwd, test.Name, test.SSLMode)
|
||||
assert.Equal(t, test.Output, connStr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ var (
|
||||
Enabled: true,
|
||||
TempPath: "data/tmp/uploads",
|
||||
AllowedTypes: "",
|
||||
FileMaxSize: 3,
|
||||
FileMaxSize: 50,
|
||||
MaxFiles: 5,
|
||||
},
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ var (
|
||||
PasswordHashAlgo string
|
||||
PasswordCheckPwn bool
|
||||
SuccessfulTokensCacheSize int
|
||||
DisableQueryAuthToken bool
|
||||
CSRFCookieName = "_csrf"
|
||||
CSRFCookieHTTPOnly = true
|
||||
)
|
||||
@@ -159,4 +160,11 @@ func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
PasswordComplexity = append(PasswordComplexity, name)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: default value should be true in future releases
|
||||
DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false)
|
||||
|
||||
if !DisableQueryAuthToken {
|
||||
log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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...)
|
||||
|
||||
@@ -2303,7 +2303,7 @@ settings.dismiss_stale_approvals_desc = When new commits that change the content
|
||||
settings.require_signed_commits = Require Signed Commits
|
||||
settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable.
|
||||
settings.protect_branch_name_pattern = Protected Branch Name Pattern
|
||||
settings.protect_branch_name_pattern_desc = "Protected branch name patterns. See <a href="github.com/gobwas/glob">the documentation</a> for pattern syntax. Examples: main, release/**"
|
||||
settings.protect_branch_name_pattern_desc = "Protected branch name patterns. See <a href="https://github.com/gobwas/glob">the documentation</a> for pattern syntax. Examples: main, release/**"
|
||||
settings.protect_patterns = Patterns
|
||||
settings.protect_protected_file_patterns = "Protected file patterns (separated using semicolon ';'):"
|
||||
settings.protect_protected_file_patterns_desc = "Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (';'). See <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> documentation for pattern syntax. Examples: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>."
|
||||
@@ -2876,6 +2876,7 @@ packages.package_manage_panel = Package Management
|
||||
packages.total_size = Total Size: %s
|
||||
packages.unreferenced_size = Unreferenced Size: %s
|
||||
packages.cleanup = Clean up expired data
|
||||
packages.cleanup.success = Cleaned up expired data successfully
|
||||
packages.owner = Owner
|
||||
packages.creator = Creator
|
||||
packages.name = Name
|
||||
@@ -3525,7 +3526,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.
|
||||
|
||||
@@ -520,7 +520,10 @@ func CommonRoutes() *web.Route {
|
||||
r.Get("", rpm.DownloadPackageFile)
|
||||
r.Delete("", reqPackageAccess(perm.AccessModeWrite), rpm.DeletePackageFile)
|
||||
})
|
||||
r.Get("/repodata/{filename}", rpm.GetRepositoryFile)
|
||||
r.Group("/repodata/{filename}", func() {
|
||||
r.Head("", rpm.CheckRepositoryFileExistence)
|
||||
r.Get("", rpm.GetRepositoryFile)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/rubygems", func() {
|
||||
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
|
||||
|
||||
@@ -57,6 +57,30 @@ func GetRepositoryKey(ctx *context.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func CheckRepositoryFileExistence(ctx *context.Context) {
|
||||
pv, err := rpm_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), packages_model.EmptyFileKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetServeHeaders(&context.ServeHeaderOptions{
|
||||
Filename: pf.Name,
|
||||
LastModified: pf.CreatedUnix.AsLocalTime(),
|
||||
})
|
||||
ctx.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// Gets a pre-generated repository metadata file
|
||||
func GetRepositoryFile(ctx *context.Context) {
|
||||
pv, err := rpm_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID)
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
@@ -35,10 +35,12 @@
|
||||
// type: apiKey
|
||||
// name: token
|
||||
// in: query
|
||||
// description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.
|
||||
// AccessToken:
|
||||
// type: apiKey
|
||||
// name: access_token
|
||||
// in: query
|
||||
// description: This authentication option is deprecated for removal in Gitea 1.23. Please use AuthorizationHeaderToken instead.
|
||||
// AuthorizationHeaderToken:
|
||||
// type: apiKey
|
||||
// name: Authorization
|
||||
@@ -787,6 +789,31 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC
|
||||
}
|
||||
}
|
||||
|
||||
func individualPermsChecker(ctx *context.APIContext) {
|
||||
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
|
||||
if ctx.ContextUser.IsIndividual() {
|
||||
switch {
|
||||
case ctx.ContextUser.Visibility == api.VisibleTypePrivate:
|
||||
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
case ctx.ContextUser.Visibility == api.VisibleTypeLimited:
|
||||
if ctx.Doer == nil {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for and warn against deprecated authentication options
|
||||
func checkDeprecatedAuthMethods(ctx *context.APIContext) {
|
||||
if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" {
|
||||
ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.")
|
||||
}
|
||||
}
|
||||
|
||||
// Routes registers all v1 APIs routes to web application.
|
||||
func Routes() *web.Route {
|
||||
m := web.NewRoute()
|
||||
@@ -805,6 +832,8 @@ func Routes() *web.Route {
|
||||
}
|
||||
m.Use(context.APIContexter())
|
||||
|
||||
m.Use(checkDeprecatedAuthMethods)
|
||||
|
||||
// Get user from session if logged in.
|
||||
m.Use(apiAuth(buildAuthGroup()))
|
||||
|
||||
@@ -887,7 +916,7 @@ func Routes() *web.Route {
|
||||
}, reqSelfOrAdmin(), reqBasicOrRevProxyAuth())
|
||||
|
||||
m.Get("/activities/feeds", user.ListUserActivityFeeds)
|
||||
}, context_service.UserAssignmentAPI())
|
||||
}, context_service.UserAssignmentAPI(), individualPermsChecker)
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser))
|
||||
|
||||
// Users (requires user scope)
|
||||
@@ -1258,8 +1287,8 @@ func Routes() *web.Route {
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
m.Group("/issues", func() {
|
||||
m.Combo("").Get(repo.ListIssues).
|
||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
|
||||
m.Get("/pinned", repo.ListPinnedIssues)
|
||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), reqRepoReader(unit.TypeIssues), repo.CreateIssue)
|
||||
m.Get("/pinned", reqRepoReader(unit.TypeIssues), repo.ListPinnedIssues)
|
||||
m.Group("/comments", func() {
|
||||
m.Get("", repo.ListRepoIssueComments)
|
||||
m.Group("/{id}", func() {
|
||||
|
||||
@@ -262,12 +262,11 @@ func CreateBranch(ctx *context.APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, oldCommit.ID.String(), opt.BranchName)
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, oldCommit.ID.String(), opt.BranchName)
|
||||
if err != nil {
|
||||
if git_model.IsErrBranchNotExist(err) {
|
||||
ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
|
||||
}
|
||||
if models.IsErrTagAlreadyExists(err) {
|
||||
} else if models.IsErrTagAlreadyExists(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
|
||||
} else if git_model.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
|
||||
ctx.Error(http.StatusConflict, "", "The branch already exists.")
|
||||
|
||||
@@ -301,7 +301,7 @@ func DeleteHook(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")); err != nil {
|
||||
if webhook.IsErrWebhookNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
|
||||
@@ -462,6 +462,24 @@ func ListIssues(ctx *context.APIContext) {
|
||||
isPull = util.OptionalBoolNone
|
||||
}
|
||||
|
||||
if isPull != util.OptionalBoolNone && !ctx.Repo.CanReadIssuesOrPulls(isPull.IsTrue()) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if isPull == util.OptionalBoolNone {
|
||||
canReadIssues := ctx.Repo.CanRead(unit.TypeIssues)
|
||||
canReadPulls := ctx.Repo.CanRead(unit.TypePullRequests)
|
||||
if !canReadIssues && !canReadPulls {
|
||||
ctx.NotFound()
|
||||
return
|
||||
} else if !canReadIssues {
|
||||
isPull = util.OptionalBoolTrue
|
||||
} else if !canReadPulls {
|
||||
isPull = util.OptionalBoolFalse
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: we should be more efficient here
|
||||
createdByID := getUserIDForFilter(ctx, "created_by")
|
||||
if ctx.Written() {
|
||||
@@ -593,6 +611,10 @@ func GetIssue(ctx *context.APIContext) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue))
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,11 @@ import (
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
@@ -71,6 +73,11 @@ func ListIssueComments(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
|
||||
return
|
||||
}
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
issue.Repo = ctx.Repo.Repository
|
||||
|
||||
opts := &issues_model.FindCommentsOptions{
|
||||
@@ -271,12 +278,27 @@ func ListRepoIssueComments(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
var isPull util.OptionalBool
|
||||
canReadIssue := ctx.Repo.CanRead(unit.TypeIssues)
|
||||
canReadPull := ctx.Repo.CanRead(unit.TypePullRequests)
|
||||
if canReadIssue && canReadPull {
|
||||
isPull = util.OptionalBoolNone
|
||||
} else if canReadIssue {
|
||||
isPull = util.OptionalBoolFalse
|
||||
} else if canReadPull {
|
||||
isPull = util.OptionalBoolTrue
|
||||
} else {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
opts := &issues_model.FindCommentsOptions{
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Type: issues_model.CommentTypeComment,
|
||||
Since: since,
|
||||
Before: before,
|
||||
IsPull: isPull,
|
||||
}
|
||||
|
||||
comments, err := issues_model.FindComments(ctx, opts)
|
||||
@@ -365,6 +387,11 @@ func CreateIssueComment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
|
||||
ctx.Error(http.StatusForbidden, "CreateIssueComment", errors.New(ctx.Tr("repo.issues.comment_on_locked")))
|
||||
return
|
||||
@@ -434,6 +461,11 @@ func GetIssueComment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Type != issues_model.CommentTypeComment {
|
||||
ctx.Status(http.StatusNoContent)
|
||||
return
|
||||
@@ -552,7 +584,17 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
@@ -655,7 +697,17 @@ func deleteIssueComment(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
} else if comment.Type != issues_model.CommentTypeComment {
|
||||
|
||||
@@ -325,6 +325,10 @@ func getIssueCommentSafe(ctx *context.APIContext) *issues_model.Comment {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
return nil
|
||||
}
|
||||
|
||||
comment.Issue.Repo = ctx.Repo.Repository
|
||||
|
||||
return comment
|
||||
|
||||
@@ -61,6 +61,12 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
||||
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
@@ -190,9 +196,19 @@ func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
|
||||
return
|
||||
}
|
||||
|
||||
err = comment.LoadIssue(ctx)
|
||||
if err != nil {
|
||||
if err = comment.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "comment.LoadIssue() failed", err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull) {
|
||||
|
||||
@@ -159,6 +159,12 @@ func GetDeployKey(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
// this check make it more consistent
|
||||
if key.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if err = key.GetContent(); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetContent", err)
|
||||
return
|
||||
|
||||
@@ -49,13 +49,12 @@ func GetRelease(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
id := ctx.ParamsInt64(":id")
|
||||
release, err := repo_model.GetReleaseByID(ctx, id)
|
||||
release, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
|
||||
return
|
||||
}
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
|
||||
release.IsTag || release.RepoID != ctx.Repo.Repository.ID {
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) || release.IsTag {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
@@ -315,13 +314,12 @@ func EditRelease(ctx *context.APIContext) {
|
||||
|
||||
form := web.GetForm(ctx).(*api.EditReleaseOption)
|
||||
id := ctx.ParamsInt64(":id")
|
||||
rel, err := repo_model.GetReleaseByID(ctx, id)
|
||||
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
|
||||
return
|
||||
}
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
|
||||
rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
@@ -393,17 +391,16 @@ func DeleteRelease(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/empty"
|
||||
|
||||
id := ctx.ParamsInt64(":id")
|
||||
rel, err := repo_model.GetReleaseByID(ctx, id)
|
||||
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, id)
|
||||
if err != nil && !repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseForRepoByID", err)
|
||||
return
|
||||
}
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) ||
|
||||
rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
|
||||
if err != nil && repo_model.IsErrReleaseNotExist(err) || rel.IsTag {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
if err := release_service.DeleteReleaseByID(ctx, id, ctx.Doer, false); err != nil {
|
||||
if err := release_service.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, false); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
|
||||
@@ -17,6 +17,23 @@ import (
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
)
|
||||
|
||||
func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool {
|
||||
release, err := repo_model.GetReleaseByID(ctx, releaseID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.NotFound()
|
||||
return false
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
return false
|
||||
}
|
||||
if release.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// GetReleaseAttachment gets a single attachment of the release
|
||||
func GetReleaseAttachment(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoGetReleaseAttachment
|
||||
@@ -54,6 +71,10 @@ func GetReleaseAttachment(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
@@ -176,13 +197,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
|
||||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
release, err := repo_model.GetReleaseByID(ctx, releaseID)
|
||||
if err != nil {
|
||||
if repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -203,7 +218,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
|
||||
attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, header.Size, &repo_model.Attachment{
|
||||
Name: filename,
|
||||
UploaderID: ctx.Doer.ID,
|
||||
RepoID: release.RepoID,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
ReleaseID: releaseID,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -264,6 +279,10 @@ func EditReleaseAttachment(ctx *context.APIContext) {
|
||||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
@@ -328,6 +347,10 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
|
||||
|
||||
// Check if release exists an load release
|
||||
releaseID := ctx.ParamsInt64(":id")
|
||||
if !checkReleaseMatchRepo(ctx, releaseID) {
|
||||
return
|
||||
}
|
||||
|
||||
attachID := ctx.ParamsInt64(":attachment_id")
|
||||
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
|
||||
if err != nil {
|
||||
|
||||
@@ -112,7 +112,7 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, release.ID, ctx.Doer, false); err != nil {
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, release, ctx.Doer, false); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
|
||||
@@ -268,7 +268,7 @@ func DeleteTag(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, tag.ID, ctx.Doer, true); err != nil {
|
||||
if err = releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
||||
return
|
||||
|
||||
@@ -193,7 +193,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := auth_model.DeleteAccessTokenByID(ctx, tokenID, ctx.Doer.ID); err != nil {
|
||||
if err := auth_model.DeleteAccessTokenByID(ctx, tokenID, ctx.ContextUser.ID); err != nil {
|
||||
if auth_model.IsErrAccessTokenNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
@@ -343,6 +343,10 @@ func GetOauth2Application(ctx *context.APIContext) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if app.UID != ctx.Doer.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
app.ClientSecret = ""
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ func GetGPGKey(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
key, err := asymkey_model.GetGPGKeyByID(ctx, ctx.ParamsInt64(":id"))
|
||||
key, err := asymkey_model.GetGPGKeyForUserByID(ctx, ctx.Doer.ID, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrGPGKeyNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
||||
@@ -62,6 +62,11 @@ func GetHook(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Doer.IsAdmin && hook.OwnerID != ctx.Doer.ID {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
apiHook, err := webhook_service.ToHook(ctx.Doer.HomeLink(), hook)
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
|
||||
@@ -54,19 +54,33 @@ func Search(ctx *context.APIContext) {
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
UID: ctx.FormInt64("uid"),
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: listOptions,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]any{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
uid := ctx.FormInt64("uid")
|
||||
var users []*user_model.User
|
||||
var maxResults int64
|
||||
var err error
|
||||
|
||||
switch uid {
|
||||
case user_model.GhostUserID:
|
||||
maxResults = 1
|
||||
users = []*user_model.User{user_model.NewGhostUser()}
|
||||
case user_model.ActionsUserID:
|
||||
maxResults = 1
|
||||
users = []*user_model.User{user_model.NewActionsUser()}
|
||||
default:
|
||||
users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
UID: uid,
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: listOptions,
|
||||
})
|
||||
return
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]any{
|
||||
"ok": false,
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
|
||||
|
||||
@@ -53,7 +53,7 @@ func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) {
|
||||
|
||||
// GetOwnerHook gets an user or organization webhook. Errors are written to ctx.
|
||||
func GetOwnerHook(ctx *context.APIContext, ownerID, hookID int64) (*webhook.Webhook, error) {
|
||||
w, err := webhook.GetWebhookByOwnerID(ownerID, hookID)
|
||||
w, err := webhook.GetWebhookByOwnerID(ctx, ownerID, hookID)
|
||||
if err != nil {
|
||||
if webhook.IsErrWebhookNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@@ -68,7 +68,7 @@ func GetOwnerHook(ctx *context.APIContext, ownerID, hookID int64) (*webhook.Webh
|
||||
// GetRepoHook get a repo's webhook. If there is an error, write to `ctx`
|
||||
// accordingly and return the error
|
||||
func GetRepoHook(ctx *context.APIContext, repoID, hookID int64) (*webhook.Webhook, error) {
|
||||
w, err := webhook.GetWebhookByRepoID(repoID, hookID)
|
||||
w, err := webhook.GetWebhookByRepoID(ctx, repoID, hookID)
|
||||
if err != nil {
|
||||
if webhook.IsErrWebhookNotExist(err) {
|
||||
ctx.NotFound()
|
||||
@@ -401,7 +401,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
|
||||
|
||||
// DeleteOwnerHook deletes the hook owned by the owner.
|
||||
func DeleteOwnerHook(ctx *context.APIContext, owner *user_model.User, hookID int64) {
|
||||
if err := webhook.DeleteWebhookByOwnerID(owner.ID, hookID); err != nil {
|
||||
if err := webhook.DeleteWebhookByOwnerID(ctx, owner.ID, hookID); err != nil {
|
||||
if webhook.IsErrWebhookNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -233,7 +233,7 @@ func Webhooks(ctx *context.Context) {
|
||||
|
||||
// DeleteWebhook response for delete webhook
|
||||
func DeleteWebhook(ctx *context.Context) {
|
||||
if err := webhook.DeleteWebhookByOwnerID(ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
|
||||
if err := webhook.DeleteWebhookByOwnerID(ctx, ctx.Org.Organization.ID, ctx.FormInt64("id")); err != nil {
|
||||
ctx.Flash.Error("DeleteWebhookByOwnerID: " + err.Error())
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
||||
|
||||
@@ -201,6 +201,7 @@ func List(ctx *context.Context) {
|
||||
pager.AddParamString("actor", fmt.Sprint(actorID))
|
||||
pager.AddParamString("status", fmt.Sprint(status))
|
||||
ctx.Data["Page"] = pager
|
||||
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0
|
||||
|
||||
ctx.HTML(http.StatusOK, tplListActions)
|
||||
}
|
||||
|
||||
@@ -114,12 +114,12 @@ func RefBlame(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
commitNames, previousCommits := processBlameParts(ctx, result.Parts)
|
||||
commitNames := processBlameParts(ctx, result.Parts)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
renderBlame(ctx, result.Parts, commitNames, previousCommits)
|
||||
renderBlame(ctx, result.Parts, commitNames)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplRepoHome)
|
||||
}
|
||||
@@ -185,12 +185,9 @@ func fillBlameResult(br *git.BlameReader, r *blameResult) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[string]*user_model.UserCommit, map[string]string) {
|
||||
func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) map[string]*user_model.UserCommit {
|
||||
// store commit data by SHA to look up avatar info etc
|
||||
commitNames := make(map[string]*user_model.UserCommit)
|
||||
// previousCommits contains links from SHA to parent SHA,
|
||||
// if parent also contains the current TreePath.
|
||||
previousCommits := make(map[string]string)
|
||||
// and as blameParts can reference the same commits multiple
|
||||
// times, we cache the lookup work locally
|
||||
commits := make([]*git.Commit, 0, len(blameParts))
|
||||
@@ -214,29 +211,11 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
|
||||
} else {
|
||||
ctx.ServerError("Repo.GitRepo.GetCommit", err)
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
commitCache[sha] = commit
|
||||
}
|
||||
|
||||
// find parent commit
|
||||
if commit.ParentCount() > 0 {
|
||||
psha := commit.Parents[0]
|
||||
previousCommit, ok := commitCache[psha.String()]
|
||||
if !ok {
|
||||
previousCommit, _ = commit.Parent(0)
|
||||
if previousCommit != nil {
|
||||
commitCache[psha.String()] = previousCommit
|
||||
}
|
||||
}
|
||||
// only store parent commit ONCE, if it has the file
|
||||
if previousCommit != nil {
|
||||
if haz1, _ := previousCommit.HasFile(ctx.Repo.TreePath); haz1 {
|
||||
previousCommits[commit.ID.String()] = previousCommit.ID.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commits = append(commits, commit)
|
||||
}
|
||||
|
||||
@@ -245,10 +224,10 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) (map[st
|
||||
commitNames[c.ID.String()] = c
|
||||
}
|
||||
|
||||
return commitNames, previousCommits
|
||||
return commitNames
|
||||
}
|
||||
|
||||
func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit, previousCommits map[string]string) {
|
||||
func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit) {
|
||||
repoLink := ctx.Repo.RepoLink
|
||||
|
||||
language := ""
|
||||
@@ -295,7 +274,6 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
|
||||
}
|
||||
|
||||
commit := commitNames[part.Sha]
|
||||
previousSha := previousCommits[part.Sha]
|
||||
if index == 0 {
|
||||
// Count commit number
|
||||
commitCnt++
|
||||
@@ -313,8 +291,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
|
||||
br.Avatar = gotemplate.HTML(avatar)
|
||||
br.RepoLink = repoLink
|
||||
br.PartSha = part.Sha
|
||||
br.PreviousSha = previousSha
|
||||
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath))
|
||||
br.PreviousSha = part.PreviousSha
|
||||
br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(part.PreviousSha), util.PathEscapeSegments(part.PreviousPath))
|
||||
br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha))
|
||||
br.CommitMessage = commit.CommitMessage
|
||||
br.CommitSince = commitSince
|
||||
|
||||
@@ -191,9 +191,9 @@ func CreateBranch(ctx *context.Context) {
|
||||
}
|
||||
err = release_service.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, target, form.NewBranchName, "")
|
||||
} else if ctx.Repo.IsViewBranch {
|
||||
err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.BranchName, form.NewBranchName)
|
||||
err = repo_service.CreateNewBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.BranchName, form.NewBranchName)
|
||||
} else {
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.CommitID, form.NewBranchName)
|
||||
err = repo_service.CreateNewBranchFromCommit(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.Repo.CommitID, form.NewBranchName)
|
||||
}
|
||||
if err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1306,7 +1306,7 @@ func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *use
|
||||
return roleDescriptor, err
|
||||
} else if hasMergedPR {
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoContributor
|
||||
} else {
|
||||
} else if issue.IsPull {
|
||||
// only display first time contributor in the first opening pull request
|
||||
roleDescriptor.RoleInRepo = issues_model.RoleRepoFirstTimeContributor
|
||||
}
|
||||
@@ -3091,6 +3091,11 @@ func UpdateCommentContent(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
return
|
||||
@@ -3157,6 +3162,11 @@ func DeleteComment(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
ctx.Error(http.StatusForbidden)
|
||||
return
|
||||
@@ -3283,6 +3293,11 @@ func ChangeCommentReaction(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull)) {
|
||||
if log.IsTrace() {
|
||||
if ctx.IsSigned {
|
||||
@@ -3426,6 +3441,21 @@ func GetCommentAttachments(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := comment.LoadIssue(ctx); err != nil {
|
||||
ctx.NotFoundOrServerError("LoadIssue", issues_model.IsErrIssueNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
if comment.Issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.Permission.CanReadIssuesOrPulls(comment.Issue.IsPull) {
|
||||
ctx.NotFound("CanReadIssuesOrPulls", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if !comment.Type.HasAttachmentSupport() {
|
||||
ctx.ServerError("GetCommentAttachments", fmt.Errorf("comment type %v does not support attachments", comment.Type))
|
||||
return
|
||||
|
||||
@@ -122,7 +122,7 @@ func GetContentHistoryDetail(ctx *context.Context) {
|
||||
}
|
||||
|
||||
historyID := ctx.FormInt64("history_id")
|
||||
history, prevHistory, err := issues_model.GetIssueContentHistoryAndPrev(ctx, historyID)
|
||||
history, prevHistory, err := issues_model.GetIssueContentHistoryAndPrev(ctx, issue.ID, historyID)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusNotFound, map[string]any{
|
||||
"message": "Can not find the content history",
|
||||
@@ -193,15 +193,29 @@ func SoftDeleteContentHistory(ctx *context.Context) {
|
||||
var comment *issues_model.Comment
|
||||
var history *issues_model.ContentHistory
|
||||
var err error
|
||||
|
||||
if history, err = issues_model.GetIssueContentHistoryByID(ctx, historyID); err != nil {
|
||||
log.Error("can not get issue content history %v. err=%v", historyID, err)
|
||||
return
|
||||
}
|
||||
if history.IssueID != issue.ID {
|
||||
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
if commentID != 0 {
|
||||
if history.CommentID != commentID {
|
||||
ctx.NotFound("CompareCommentID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
|
||||
if comment, err = issues_model.GetCommentByID(ctx, commentID); err != nil {
|
||||
log.Error("can not get comment for issue content history %v. err=%v", historyID, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if history, err = issues_model.GetIssueContentHistoryByID(ctx, historyID); err != nil {
|
||||
log.Error("can not get issue content history %v. err=%v", historyID, err)
|
||||
return
|
||||
if comment.IssueID != issue.ID {
|
||||
ctx.NotFound("CompareIssueID", issues_model.ErrCommentNotExist{})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
canSoftDelete := canSoftDeleteContentHistory(ctx, issue, comment, history)
|
||||
|
||||
@@ -90,6 +90,12 @@ func IssuePinMove(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if issue.RepoID != ctx.Repo.Repository.ID {
|
||||
ctx.Status(http.StatusNotFound)
|
||||
log.Error("Issue does not belong to this repository")
|
||||
return
|
||||
}
|
||||
|
||||
err = issue.MovePin(ctx, form.Position)
|
||||
if err != nil {
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
|
||||
@@ -464,7 +464,7 @@ func AddBoardToProjectPost(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
project, err := project_model.GetProjectForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
|
||||
@@ -613,7 +613,27 @@ func DeleteTag(ctx *context.Context) {
|
||||
}
|
||||
|
||||
func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
|
||||
if err := releaseservice.DeleteReleaseByID(ctx, ctx.FormInt64("id"), ctx.Doer, isDelTag); err != nil {
|
||||
redirect := func() {
|
||||
if isDelTag {
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/tags")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases")
|
||||
}
|
||||
|
||||
rel, err := repo_model.GetReleaseForRepoByID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id"))
|
||||
if err != nil {
|
||||
if repo_model.IsErrReleaseNotExist(err) {
|
||||
ctx.NotFound("GetReleaseForRepoByID", err)
|
||||
} else {
|
||||
ctx.Flash.Error("DeleteReleaseByID: " + err.Error())
|
||||
redirect()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := releaseservice.DeleteReleaseByID(ctx, ctx.Repo.Repository, rel, ctx.Doer, isDelTag); err != nil {
|
||||
if models.IsErrProtectedTagName(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.release.tag_name_protected"))
|
||||
} else {
|
||||
@@ -627,10 +647,5 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if isDelTag {
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/tags")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSONRedirect(ctx.Repo.RepoLink + "/releases")
|
||||
redirect()
|
||||
}
|
||||
|
||||
@@ -589,9 +589,9 @@ func checkWebhook(ctx *context.Context) (*ownerRepoCtx, *webhook.Webhook) {
|
||||
|
||||
var w *webhook.Webhook
|
||||
if orCtx.RepoID > 0 {
|
||||
w, err = webhook.GetWebhookByRepoID(orCtx.RepoID, ctx.ParamsInt64(":id"))
|
||||
w, err = webhook.GetWebhookByRepoID(ctx, orCtx.RepoID, ctx.ParamsInt64(":id"))
|
||||
} else if orCtx.OwnerID > 0 {
|
||||
w, err = webhook.GetWebhookByOwnerID(orCtx.OwnerID, ctx.ParamsInt64(":id"))
|
||||
w, err = webhook.GetWebhookByOwnerID(ctx, orCtx.OwnerID, ctx.ParamsInt64(":id"))
|
||||
} else if orCtx.IsAdmin {
|
||||
w, err = webhook.GetSystemOrDefaultWebhook(ctx, ctx.ParamsInt64(":id"))
|
||||
}
|
||||
@@ -643,7 +643,7 @@ func WebHooksEdit(ctx *context.Context) {
|
||||
// TestWebhook test if web hook is work fine
|
||||
func TestWebhook(ctx *context.Context) {
|
||||
hookID := ctx.ParamsInt64(":id")
|
||||
w, err := webhook.GetWebhookByRepoID(ctx.Repo.Repository.ID, hookID)
|
||||
w, err := webhook.GetWebhookByRepoID(ctx, ctx.Repo.Repository.ID, hookID)
|
||||
if err != nil {
|
||||
ctx.Flash.Error("GetWebhookByRepoID: " + err.Error())
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
@@ -724,7 +724,7 @@ func ReplayWebhook(ctx *context.Context) {
|
||||
|
||||
// DeleteWebhook delete a webhook
|
||||
func DeleteWebhook(ctx *context.Context) {
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
|
||||
if err := webhook.DeleteWebhookByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
|
||||
ctx.Flash.Error("DeleteWebhookByRepoID: " + err.Error())
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
||||
|
||||
@@ -702,21 +702,14 @@ func checkCitationFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||
}
|
||||
for _, entry := range allEntries {
|
||||
if entry.Name() == "CITATION.cff" || entry.Name() == "CITATION.bib" {
|
||||
ctx.Data["CitiationExist"] = true
|
||||
// Read Citation file contents
|
||||
blob := entry.Blob()
|
||||
dataRc, err := blob.DataAsync()
|
||||
if err != nil {
|
||||
ctx.ServerError("DataAsync", err)
|
||||
return
|
||||
if content, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
|
||||
log.Error("checkCitationFile: GetBlobContent: %v", err)
|
||||
} else {
|
||||
ctx.Data["CitiationExist"] = true
|
||||
ctx.PageData["citationFileContent"] = content
|
||||
break
|
||||
}
|
||||
defer dataRc.Close()
|
||||
ctx.PageData["citationFileContent"], err = blob.GetBlobContent(setting.UI.MaxDisplayFileSize)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBlobContent", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ func Webhooks(ctx *context.Context) {
|
||||
|
||||
// DeleteWebhook response for delete webhook
|
||||
func DeleteWebhook(ctx *context.Context) {
|
||||
if err := webhook.DeleteWebhookByOwnerID(ctx.Doer.ID, ctx.FormInt64("id")); err != nil {
|
||||
if err := webhook.DeleteWebhookByOwnerID(ctx, ctx.Doer.ID, ctx.FormInt64("id")); err != nil {
|
||||
ctx.Flash.Error("DeleteWebhookByOwnerID: " + err.Error())
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success"))
|
||||
|
||||
@@ -532,8 +532,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() {
|
||||
@@ -793,6 +795,24 @@ func registerRoutes(m *web.Route) {
|
||||
}
|
||||
}
|
||||
|
||||
individualPermsChecker := func(ctx *context.Context) {
|
||||
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
|
||||
if ctx.ContextUser.IsIndividual() {
|
||||
switch {
|
||||
case ctx.ContextUser.Visibility == structs.VisibleTypePrivate:
|
||||
if ctx.Doer == nil || (ctx.ContextUser.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin) {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
case ctx.ContextUser.Visibility == structs.VisibleTypeLimited:
|
||||
if ctx.Doer == nil {
|
||||
ctx.NotFound("Visit Project", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ***** START: Organization *****
|
||||
m.Group("/org", func() {
|
||||
m.Group("/{org}", func() {
|
||||
@@ -973,11 +993,11 @@ func registerRoutes(m *web.Route) {
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
}, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true), individualPermsChecker)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Get("/code", user.CodeSearch)
|
||||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false))
|
||||
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
|
||||
}, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
|
||||
|
||||
m.Group("/{username}/{reponame}", func() {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
@@ -62,14 +63,19 @@ func (o *OAuth2) Name() string {
|
||||
// representing whether the token exists or not
|
||||
func parseToken(req *http.Request) (string, bool) {
|
||||
_ = req.ParseForm()
|
||||
// Check token.
|
||||
if token := req.Form.Get("token"); token != "" {
|
||||
return token, true
|
||||
}
|
||||
// Check access token.
|
||||
if token := req.Form.Get("access_token"); token != "" {
|
||||
return token, true
|
||||
if !setting.DisableQueryAuthToken {
|
||||
// Check token.
|
||||
if token := req.Form.Get("token"); token != "" {
|
||||
return token, true
|
||||
}
|
||||
// Check access token.
|
||||
if token := req.Form.Get("access_token"); token != "" {
|
||||
return token, true
|
||||
}
|
||||
} else if req.Form.Get("token") != "" || req.Form.Get("access_token") != "" {
|
||||
log.Warn("API token sent in query string but DISABLE_QUERY_AUTH_TOKEN=true")
|
||||
}
|
||||
|
||||
// check header token
|
||||
if auHead := req.Header.Get("Authorization"); auHead != "" {
|
||||
auths := strings.Fields(auHead)
|
||||
|
||||
@@ -84,13 +84,15 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
|
||||
t.lock.Unlock()
|
||||
defer func() {
|
||||
taskStatusTable.Stop(t.Name)
|
||||
if err := recover(); err != nil {
|
||||
// Recover a panic within the
|
||||
combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2))
|
||||
log.Error("PANIC whilst running task: %s Value: %v", t.Name, combinedErr)
|
||||
}
|
||||
}()
|
||||
graceful.GetManager().RunWithShutdownContext(func(baseCtx context.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
// Recover a panic within the execution of the task.
|
||||
combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2))
|
||||
log.Error("PANIC whilst running task: %s Value: %v", t.Name, combinedErr)
|
||||
}
|
||||
}()
|
||||
// Store the time of this run, before the function is executed, so it
|
||||
// matches the behavior of what the cron library does.
|
||||
t.lock.Lock()
|
||||
|
||||
@@ -365,7 +365,7 @@ func (f *EditVariableForm) Validate(req *http.Request, errs binding.Errors) bind
|
||||
|
||||
// NewAccessTokenForm form for creating access token
|
||||
type NewAccessTokenForm struct {
|
||||
Name string `binding:"Required;MaxSize(255)"`
|
||||
Name string `binding:"Required;MaxSize(255)" locale:"settings.token_name"`
|
||||
Scope []string
|
||||
}
|
||||
|
||||
|
||||
@@ -130,3 +130,25 @@ func (r *indexerNotifier) IssueChangeTitle(ctx context.Context, doer *user_model
|
||||
func (r *indexerNotifier) IssueChangeRef(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldRef string) {
|
||||
issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) IssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) {
|
||||
issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) IssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) {
|
||||
issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) IssueChangeMilestone(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) {
|
||||
issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) IssueChangeLabels(ctx context.Context, doer *user_model.User, issue *issues_model.Issue,
|
||||
addedLabels, removedLabels []*issues_model.Label,
|
||||
) {
|
||||
issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
|
||||
}
|
||||
|
||||
func (r *indexerNotifier) IssueClearLabels(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) {
|
||||
issue_indexer.UpdateIssueIndexer(ctx, issue.ID)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user