Merge remote-tracking branch 'origin/main' into richmahn-11835-api-for-git-refs
This commit is contained in:
commit
a54820f463
391 changed files with 8663 additions and 6408 deletions
|
@ -626,6 +626,8 @@ steps:
|
|||
commit_message: "[skip ci] Updated translations via Crowdin"
|
||||
remote: "git@github.com:go-gitea/gitea.git"
|
||||
environment:
|
||||
DRONE_COMMIT_AUTHOR_EMAIL: "teabot@gitea.io"
|
||||
DRONE_COMMIT_AUTHOR: GiteaBot
|
||||
GIT_PUSH_SSH_KEY:
|
||||
from_secret: git_push_ssh_key
|
||||
|
||||
|
@ -670,12 +672,14 @@ steps:
|
|||
pull: always
|
||||
settings:
|
||||
author_email: "teabot@gitea.io"
|
||||
author_name: GiteaBot
|
||||
author_name: "GiteaBot"
|
||||
branch: main
|
||||
commit: true
|
||||
commit_message: "[skip ci] Updated licenses and gitignores"
|
||||
remote: "git@github.com:go-gitea/gitea.git"
|
||||
environment:
|
||||
DRONE_COMMIT_AUTHOR_EMAIL: "teabot@gitea.io"
|
||||
DRONE_COMMIT_AUTHOR: "GiteaBot"
|
||||
GIT_PUSH_SSH_KEY:
|
||||
from_secret: git_push_ssh_key
|
||||
|
||||
|
|
|
@ -35,9 +35,6 @@ overrides:
|
|||
rules:
|
||||
import/no-unresolved: [0]
|
||||
import/no-extraneous-dependencies: [0]
|
||||
- files: ["*.test.js"]
|
||||
env:
|
||||
jest: true
|
||||
- files: ["*.config.js"]
|
||||
rules:
|
||||
import/no-unused-modules: [0]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
tasks:
|
||||
- name: Setup
|
||||
init: |
|
||||
cp -r contrib/ide/vscode .vscode
|
||||
make deps
|
||||
make build
|
||||
command: |
|
||||
|
@ -31,6 +32,7 @@ vscode:
|
|||
- DavidAnson.vscode-markdownlint
|
||||
- johnsoncodehk.volar
|
||||
- ms-azuretools.vscode-docker
|
||||
- zixuanchen.vitest-explorer
|
||||
|
||||
ports:
|
||||
- name: Gitea
|
||||
|
|
|
@ -12,7 +12,6 @@ linters:
|
|||
- dupl
|
||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||
- gofmt
|
||||
- misspell
|
||||
- gocritic
|
||||
- bidichk
|
||||
- ineffassign
|
||||
|
@ -148,9 +147,6 @@ issues:
|
|||
- path: models/issue_comment_list.go
|
||||
linters:
|
||||
- dupl
|
||||
- linters:
|
||||
- misspell
|
||||
text: '`Unknwon` is a misspelling of `Unknown`'
|
||||
- path: models/update.go
|
||||
linters:
|
||||
- unused
|
||||
|
|
80
CHANGELOG.md
80
CHANGELOG.md
|
@ -4,6 +4,86 @@ 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.io).
|
||||
|
||||
## [1.17.3](https://github.com/go-gitea/gitea/releases/tag/v1.17.3) - 2022-10-15
|
||||
|
||||
* SECURITY
|
||||
* Sanitize and Escape refs in git backend (#21464) (#21463)
|
||||
* Bump `golang.org/x/text` (#21412) (#21413)
|
||||
* Update bluemonday (#21281) (#21287)
|
||||
* ENHANCEMENTS
|
||||
* Fix empty container layer history and UI (#21251) (#21278)
|
||||
* Use en-US as fallback when using other default language (#21200) (#21256)
|
||||
* Make the vscode clone link respect transport protocol (#20557) (#21128)
|
||||
* BUGFIXES
|
||||
* Do DB update after merge in hammer context (#21401) (#21416)
|
||||
* Add Num{Issues,Pulls} stats checks (#21404) (#21414)
|
||||
* Stop logging CheckPath returns error: context canceled (#21064) (#21405)
|
||||
* Parse OAuth Authorization header when request omits client secret (#21351) (#21374)
|
||||
* Ignore port for loopback redirect URIs (#21293) (#21373)
|
||||
* Set SemverCompatible to false for Conan packages (#21275) (#21366)
|
||||
* Tag list should include draft releases with existing tags (#21263) (#21365)
|
||||
* Fix linked account translation (#21331) (#21334)
|
||||
* Make NuGet service index publicly accessible (#21242) (#21277)
|
||||
* Foreign ID conflicts if ID is 0 for each item (#21271) (#21272)
|
||||
* Use absolute links in feeds (#21229) (#21265)
|
||||
* Prevent invalid behavior for file reviewing when loading more files (#21230) (#21234)
|
||||
* Respect `REQUIRE_SIGNIN_VIEW` for packages (#20873) (#21232)
|
||||
* Treat git object mode 40755 as directory (#21195) (#21218)
|
||||
* Allow uppercase ASCII alphabet in PyPI package names (#21095) (#21217)
|
||||
* Fix limited user cannot view himself's profile (#21212)
|
||||
* Fix template bug of admin monitor (#21209)
|
||||
* Fix reaction of issues (#21185) (#21196)
|
||||
* Fix CSV diff for added/deleted files (#21189) (#21193)
|
||||
* Fix pagination limit parameter problem (#21111)
|
||||
* TESTING
|
||||
* Fix missing m.Run() in TestMain (#21341)
|
||||
* BUILD
|
||||
* Use Go 1.19 fmt for Gitea 1.17, sync emoji data (#21239)
|
||||
|
||||
## [1.17.2](https://github.com/go-gitea/gitea/releases/tag/v1.17.2) - 2022-09-06
|
||||
|
||||
* SECURITY
|
||||
* Double check CloneURL is acceptable (#20869) (#20892)
|
||||
* Add more checks in migration code (#21011) (#21050)
|
||||
* ENHANCEMENTS
|
||||
* Fix hard-coded timeout and error panic in API archive download endpoint (#20925) (#21051)
|
||||
* Improve arc-green code theme (#21039) (#21042)
|
||||
* Enable contenthash in filename for dynamic assets (#20813) (#20932)
|
||||
* Don't open new page for ext wiki on same repository (#20725) (#20910)
|
||||
* Disable doctor logging on panic (#20847) (#20898)
|
||||
* Remove calls to load Mirrors in user.Dashboard (#20855) (#20897)
|
||||
* Update codemirror to 5.65.8 (#20875)
|
||||
* Rework repo buttons (#20602, #20718) (#20719)
|
||||
* BUGFIXES
|
||||
* Ensure delete user deletes all comments (#21067) (#21068)
|
||||
* Delete unreferenced packages when deleting a package version (#20977) (#21060)
|
||||
* Redirect if user does not exist on admin pages (#20981) (#21059)
|
||||
* Set uploadpack.allowFilter etc on gitea serv to enable partial clones with ssh (#20902) (#21058)
|
||||
* Fix 500 on time in timeline API (#21052) (#21057)
|
||||
* Fill the specified ref in webhook test payload (#20961) (#21055)
|
||||
* Add another index for Action table on postgres (#21033) (#21054)
|
||||
* Fix broken insecureskipverify handling in redis connection uris (#20967) (#21053)
|
||||
* Add Dev, Peer and Optional dependencies to npm PackageMetadataVersion (#21017) (#21044)
|
||||
* Do not add links to Posters or Assignees with ID < 0 (#20577) (#21037)
|
||||
* Fix modified due date message (#20388) (#21032)
|
||||
* Fix missed sort bug (#21006)
|
||||
* Fix input.value attr for RequiredClaimName/Value (#20946) (#21001)
|
||||
* Change review buttons to icons to make space for text (#20934) (#20978)
|
||||
* Fix download archiver of a commit (#20962) (#20971)
|
||||
* Return 404 NotFound if requested attachment does not exist (#20886) (#20941)
|
||||
* Set no-tags in git fetch on compare (#20893) (#20936)
|
||||
* Allow multiple metadata files for Maven packages (#20674) (#20916)
|
||||
* Increase Content field size of gpg_key and public_key to MEDIUMTEXT (#20896) (#20911)
|
||||
* Fix mirror address setting not working (#20850) (#20904)
|
||||
* Fix push mirror address backend get error Address cause setting page display error (#20593) (#20901)
|
||||
* Fix panic when an invalid oauth2 name is passed (#20820) (#20900)
|
||||
* In PushMirrorsIterate and MirrorsIterate if limit is negative do not set it (#20837) (#20899)
|
||||
* Ensure that graceful start-up is informed of unused SSH listener (#20877) (#20888)
|
||||
* Pad GPG Key ID with preceding zeroes (#20878) (#20885)
|
||||
* Fix SQL Query for `SearchTeam` (#20844) (#20872)
|
||||
* Fix the mode of custom dir to 0700 in docker-rootless (#20861) (#20867)
|
||||
* Fix UI mis-align for PR commit history (#20845) (#20859)
|
||||
|
||||
## [1.17.1](https://github.com/go-gitea/gitea/releases/tag/1.17.1) - 2022-08-17
|
||||
|
||||
* SECURITY
|
||||
|
|
|
@ -146,6 +146,16 @@ If your PR could cause a breaking change you must add a BREAKING section to this
|
|||
|
||||
To explain how this could affect users and how to mitigate these changes.
|
||||
|
||||
Once code review starts on your PR, do not rebase nor squash your branch as it makes it
|
||||
difficult to review the new changes. Only if there is a need, sync your branch by merging
|
||||
the base branch into yours. Don't worry about merge commits messing up your tree as
|
||||
the final merge process squashes all commits into one, with the visible commit message (first
|
||||
line) being the PR title + PR index and description being the PR's first comment.
|
||||
|
||||
Once your PR gets the `lgtm/done` label, don't worry about keeping it up-to-date or breaking
|
||||
builds (unless there's a merge conflict or a request is made by a maintainer to make
|
||||
modifications). It is the maintainer team's responsibility from this point to get it merged.
|
||||
|
||||
## Styleguide
|
||||
|
||||
For imports you should use the following format (*without* the comments)
|
||||
|
|
|
@ -49,3 +49,4 @@ silentcode <silentcode@senga.org> (@silentcodeg)
|
|||
Wim <wim@42.be> (@42wim)
|
||||
xinyu <xinyu@nerv.org.cn> (@penlinux)
|
||||
Jason Song <i@wolfogre.com> (@wolfogre)
|
||||
Yarden Shoham <hrsi88@gmail.com> (@yardenshoham)
|
||||
|
|
36
Makefile
36
Makefile
|
@ -130,6 +130,7 @@ TEST_TAGS ?= sqlite sqlite_unlock_notify
|
|||
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
|
||||
|
||||
GO_DIRS := cmd tests models modules routers build services tools
|
||||
WEB_DIRS := web_src/js web_src/less
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" -not -path modules/options/bindata.go -not -path modules/public/bindata.go -not -path modules/templates/bindata.go)
|
||||
|
@ -263,11 +264,24 @@ clean:
|
|||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
|
||||
@# strip whitespace after '{{' and before `}}` unless there is only whitespace before it
|
||||
@$(SED_INPLACE) -e 's/{{[ ]\{1,\}/{{/g' -e '/^[ ]\{1,\}}}/! s/[ ]\{1,\}}}/}}/g' $(TEMPLATES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check: fmt
|
||||
@diff=$$(git diff $(GO_SOURCES) templates $(WEB_DIRS)); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: misspell-check
|
||||
misspell-check:
|
||||
go run $(MISSPELL_PACKAGE) -error $(GO_DIRS) $(WEB_DIRS)
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
@echo "Running go vet..."
|
||||
|
@ -311,22 +325,6 @@ errcheck:
|
|||
@echo "Running errcheck..."
|
||||
$(GO) run $(ERRCHECK_PACKAGE) $(GO_PACKAGES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
@# get all go files and run gitea-fmt (with gofmt) on them
|
||||
@diff=$$(MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -l '{file-list}'); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@diff2=$$(git diff templates); \
|
||||
if [ -n "$$diff2" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff2}"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
.PHONY: checks
|
||||
checks: checks-frontend checks-backend
|
||||
|
||||
|
@ -334,7 +332,7 @@ checks: checks-frontend checks-backend
|
|||
checks-frontend: lockfile-check svg-check
|
||||
|
||||
.PHONY: checks-backend
|
||||
checks-backend: tidy-check swagger-check swagger-validate
|
||||
checks-backend: tidy-check swagger-check fmt-check misspell-check swagger-validate
|
||||
|
||||
.PHONY: lint
|
||||
lint: lint-frontend lint-backend
|
||||
|
@ -372,7 +370,7 @@ test-backend:
|
|||
|
||||
.PHONY: test-frontend
|
||||
test-frontend: node_modules
|
||||
@NODE_OPTIONS="--experimental-vm-modules --no-warnings" npx jest --color
|
||||
npx vitest
|
||||
|
||||
.PHONY: test-check
|
||||
test-check:
|
||||
|
|
|
@ -151,6 +151,7 @@ Support this project by becoming a sponsor. Your logo will show up here with a l
|
|||
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://cynkra.com/" target="_blank"><img src="https://images.opencollective.com/cynkra/logo/square/64/192.png"></a>
|
||||
|
||||
## FAQ
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
)
|
||||
|
||||
// Windows has a limitation for command line arguments, the size can not exceed 32KB.
|
||||
// So we have to feed the files to some tools (like gofmt/misspell) batch by batch
|
||||
// So we have to feed the files to some tools (like gofmt) batch by batch
|
||||
|
||||
// We also introduce a `gitea-fmt` command, it does better import formatting than gofmt/goimports. `gitea-fmt` calls `gofmt` internally.
|
||||
|
||||
|
@ -195,7 +195,6 @@ Options:
|
|||
|
||||
Commands:
|
||||
%[1]s gofmt ...
|
||||
%[1]s misspell ...
|
||||
|
||||
Arguments:
|
||||
{file-list} the file list
|
||||
|
@ -206,6 +205,17 @@ Example:
|
|||
`, "file-batch-exec")
|
||||
}
|
||||
|
||||
func getGoVersion() string {
|
||||
goModFile, err := os.ReadFile("go.mod")
|
||||
if err != nil {
|
||||
log.Fatalf(`Faild to read "go.mod": %v`, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`)
|
||||
goModVersionLine := goModVersionRegex.Find(goModFile)
|
||||
return string(goModVersionLine[3:])
|
||||
}
|
||||
|
||||
func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) {
|
||||
fileFilter := mainOptions["file-filter"]
|
||||
if fileFilter == "" {
|
||||
|
@ -228,9 +238,9 @@ func containsString(a []string, s string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func giteaFormatGoImports(files []string, hasChangedFiles, doWriteFile bool) error {
|
||||
func giteaFormatGoImports(files []string, doWriteFile bool) error {
|
||||
for _, file := range files {
|
||||
if err := codeformat.FormatGoImports(file, hasChangedFiles, doWriteFile); err != nil {
|
||||
if err := codeformat.FormatGoImports(file, doWriteFile); err != nil {
|
||||
log.Printf("failed to format go imports: %s, err=%v", file, err)
|
||||
return err
|
||||
}
|
||||
|
@ -269,10 +279,8 @@ func main() {
|
|||
if containsString(subArgs, "-d") {
|
||||
log.Print("the -d option is not supported by gitea-fmt")
|
||||
}
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-l"), containsString(subArgs, "-w")))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", "1.17"}, substArgs...)))
|
||||
case "misspell":
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("MISSPELL_PACKAGE")}, substArgs...)))
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w")))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...)))
|
||||
default:
|
||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ package codeformat
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
|
@ -159,7 +158,7 @@ func formatGoImports(contentBytes []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
// FormatGoImports format the imports by our rules (see unit tests)
|
||||
func FormatGoImports(file string, doChangedFiles, doWriteFile bool) error {
|
||||
func FormatGoImports(file string, doWriteFile bool) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -183,10 +182,6 @@ func FormatGoImports(file string, doChangedFiles, doWriteFile bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if doChangedFiles {
|
||||
fmt.Println(file)
|
||||
}
|
||||
|
||||
if doWriteFile {
|
||||
f, err = os.OpenFile(file, os.O_TRUNC|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
|
|
|
@ -146,6 +146,10 @@ It can be used for backup and capture Gitea server image to send to maintainer`,
|
|||
Name: "skip-package-data",
|
||||
Usage: "Skip package data",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "skip-index",
|
||||
Usage: "Skip bleve index data",
|
||||
},
|
||||
cli.GenericFlag{
|
||||
Name: "type",
|
||||
Value: outputTypeEnum,
|
||||
|
@ -327,6 +331,11 @@ func runDump(ctx *cli.Context) error {
|
|||
excludes = append(excludes, opts.ProviderConfig)
|
||||
}
|
||||
|
||||
if ctx.IsSet("skip-index") && ctx.Bool("skip-index") {
|
||||
excludes = append(excludes, setting.Indexer.RepoPath)
|
||||
excludes = append(excludes, setting.Indexer.IssuePath)
|
||||
}
|
||||
|
||||
excludes = append(excludes, setting.RepoRootPath)
|
||||
excludes = append(excludes, setting.LFS.Path)
|
||||
excludes = append(excludes, setting.Attachment.Path)
|
||||
|
|
|
@ -2,14 +2,43 @@
|
|||
|
||||
DIR=/var/lib/gitea
|
||||
USER=git
|
||||
HOME=/home/${USER}
|
||||
GITEA_WORK_DIR=${DIR}
|
||||
EXECUTABLE=/usr/local/bin/gitea
|
||||
|
||||
export USER
|
||||
export HOME
|
||||
export GITEA_WORK_DIR
|
||||
|
||||
name=$RC_SVCNAME
|
||||
cfgfile="/etc/$RC_SVCNAME/app.ini"
|
||||
command="${EXECUTABLE}"
|
||||
command_user="${USER}"
|
||||
command_args="web -c /etc/$RC_SVCNAME/app.ini"
|
||||
command_background="yes"
|
||||
pidfile="/run/$RC_SVCNAME/$RC_SVCNAME.pid"
|
||||
start_stop_daemon_args="--user ${USER} --chdir ${DIR}"
|
||||
command="/usr/local/bin/gitea"
|
||||
command_args="web -c /etc/gitea/app.ini"
|
||||
command_background=yes
|
||||
pidfile=/run/gitea.pid
|
||||
|
||||
depend()
|
||||
{
|
||||
need net
|
||||
###
|
||||
# Don't forget to add the database service requirements
|
||||
###
|
||||
#after postgresql
|
||||
#after mysql
|
||||
#after mariadb
|
||||
#after memcached
|
||||
#after redis
|
||||
}
|
||||
|
||||
start_pre()
|
||||
{
|
||||
checkpath --directory --owner $command_user:$command_user --mode 0750 \
|
||||
/run/$RC_SVCNAME /var/log/$RC_SVCNAME
|
||||
##
|
||||
# If you want to bind Gitea to a port below 1024, uncomment
|
||||
# the value below
|
||||
##
|
||||
#setcap cap_net_bind_service=+ep "${EXECUTABLE}"
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
|
@ -34,6 +33,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers"
|
||||
markup_service "code.gitea.io/gitea/services/markup"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
|
@ -62,11 +62,7 @@ func runPR() {
|
|||
}
|
||||
setting.AppWorkPath = curDir
|
||||
setting.StaticRootPath = curDir
|
||||
setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
|
||||
if err != nil {
|
||||
log.Fatalf("url.Parse: %v\n", err)
|
||||
}
|
||||
|
||||
setting.GravatarSource = "https://secure.gravatar.com/avatar/"
|
||||
setting.AppURL = "http://localhost:8080/"
|
||||
setting.HTTPPort = "8080"
|
||||
setting.SSH.Domain = "localhost"
|
||||
|
@ -117,7 +113,7 @@ func runPR() {
|
|||
log.Printf("[PR] Setting up router\n")
|
||||
// routers.GlobalInit()
|
||||
external.RegisterRenderers()
|
||||
markup.Init()
|
||||
markup.Init(markup_service.ProcessorHelper())
|
||||
c := routers.NormalRoutes(graceful.GetManager().HammerContext())
|
||||
|
||||
log.Printf("[PR] Ready for testing !\n")
|
||||
|
|
|
@ -18,7 +18,7 @@ params:
|
|||
description: Git with a cup of tea
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.17.1
|
||||
version: 1.17.3
|
||||
minGoVersion: 1.18
|
||||
goVersion: 1.19
|
||||
minNodeVersion: 14
|
||||
|
|
|
@ -23,7 +23,7 @@ menu:
|
|||
|
||||
To get a quick working development environment you could use Gitpod.
|
||||
|
||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
||||
[](https://gitpod.io/#https://github.com/go-gitea/gitea)
|
||||
|
||||
## Installing go
|
||||
|
||||
|
|
|
@ -44,6 +44,12 @@ To use the Authorization Code Grant as a third party application it is required
|
|||
|
||||
Currently Gitea does not support scopes (see [#4300](https://github.com/go-gitea/gitea/issues/4300)) and all third party applications will be granted access to all resources of the user and their organizations.
|
||||
|
||||
## Client types
|
||||
|
||||
Gitea supports both confidential and public client types, [as defined by RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749#section-2.1).
|
||||
|
||||
For public clients, a redirect URI of a loopback IP address such as `http://127.0.0.1/` allows any port. Avoid using `localhost`, [as recommended by RFC 8252](https://datatracker.ietf.org/doc/html/rfc8252#section-8.3).
|
||||
|
||||
## Example
|
||||
|
||||
**Note:** This example does not use PKCE.
|
||||
|
|
|
@ -21,7 +21,7 @@ menu:
|
|||
|
||||
To help decide if Gitea is suited for your needs, here is how it compares to other Git self hosted options.
|
||||
|
||||
Be warned that we don't regularly check for feature changes in other products, so this list may be outdated. If you find anything that needs to be updated in the table below, please report it in an [issue on GitHub](https://github.com/go-gitea/gitea/issues).
|
||||
Be warned that we don't regularly check for feature changes in other products, so this list may be outdated. If you find anything that needs to be updated in the table below, please [open an issue](https://github.com/go-gitea/gitea/issues/new/choose).
|
||||
|
||||
_Symbols used in table:_
|
||||
|
||||
|
@ -34,102 +34,110 @@ _Symbols used in table:_
|
|||
## General Features
|
||||
|
||||
| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ----------------------------------- | ---------------------------------------------------| ---- | --------- | --------- | --------- | -------------- | ------------ |
|
||||
| ------------------------------------------------ | --------------------------------------------------- | ---- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| Open source and free | ✓ | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ |
|
||||
| Low resource usage (RAM/CPU) | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| Low RAM/ CPU usage | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| Multiple database support | ✓ | ✓ | ✘ | ⁄ | ⁄ | ✓ | ✓ |
|
||||
| Multiple OS support | ✓ | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ |
|
||||
| Easy upgrade process | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Markdown support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Orgmode support | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ? |
|
||||
| CSV support | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
|
||||
| Easy upgrades | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Telemetry | **✘** | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| Third-party render tool support | ✓ | ✘ | ✘ | ✘ | ✘ | ✓ | ? |
|
||||
| Static Git-powered pages | [✘](https://github.com/go-gitea/gitea/issues/302) | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Integrated Git-powered wiki | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
|
||||
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| Extensive API | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Built-in Package/Container Registry | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Sync commits to an external repo (push mirror) | ✓ | ✓ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Sync commits from an external repo (pull mirror) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ? |
|
||||
| Light and Dark Theme | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ? |
|
||||
| Custom Theme Support | ✓ | ✓ | ✘ | ✘ | ✘ | ✓ | ✘ |
|
||||
| Markdown support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| CSV support | ✓ | ✘ | ✓ | ✘ | ✘ | ✓ | ? |
|
||||
| 'GitHub / GitLab pages' | [✘](https://github.com/go-gitea/gitea/issues/302) | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Repo-specific wiki (as a repo itself) | ✓ | ✓ | ✓ | ✓ | ✓ | / | ✘ |
|
||||
| Deploy Tokens | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Repository Tokens with write rights | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Built-in Package/Container Registry | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| External git mirroring | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| WebAuthn (2FA) | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ? |
|
||||
| Built-in CI/CD | ✘ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| RSS Feeds | ✓ | ✘ | ✓ | ✘ | ✘ | ✘ | ✘ |
|
||||
| Built-in CI/CD | [✘](https://github.com/go-gitea/gitea/issues/13539) | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Subgroups: groups within groups | [✘](https://github.com/go-gitea/gitea/issues/1872) | ✘ | ✘ | ✓ | ✓ | ✘ | ✓ |
|
||||
| Interaction with other instances | [/](https://github.com/go-gitea/gitea/issues/18240) | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| Mermaid diagrams in Markdown | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Math syntax in Markdown | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
|
||||
## Code management
|
||||
|
||||
| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| -------------------------------------------- | ------------------------------------------------ | ---- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| ------------------------------------------- | --------------------------------------------------- | ---- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| Repository topics | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Repository code search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Global code search | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✓ |
|
||||
| Git LFS 2.0 | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Group Milestones | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Granular user roles (Code, Issues, Wiki etc) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Group Milestones | [✘](https://github.com/go-gitea/gitea/issues/14622) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Granular user roles (Code, Issues, Wiki, …) | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Verified Committer | ⁄ | ✘ | ? | ✓ | ✓ | ✓ | ✘ |
|
||||
| GPG Signed Commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| SSH Signed Commits | ✓ | ✘ | ✘ | ✘ | ✘ | ? | ? |
|
||||
| Reject unsigned commits | [✓](https://github.com/go-gitea/gitea/pull/9708) | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| SSH Signed Commits | ✓ | ✘ | ✓ | ✘ | ✘ | ? | ? |
|
||||
| Reject unsigned commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Migrating repos from other services | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Repository Activity page | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Branch manager | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Create new branches | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Web code editor | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Commit graph | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Template Repositories | [✓](https://github.com/go-gitea/gitea/pull/8768) | ✘ | ✓ | ✘ | ✓ | ✓ | ✘ |
|
||||
| Template Repositories | ✓ | ✘ | ✓ | ✘ | ✓ | ✓ | ✘ |
|
||||
| Git Blame | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Visual comparison of image changes | ✓ | ✘ | ✓ | ? | ? | ? | ? |
|
||||
|
||||
## Issue Tracker
|
||||
|
||||
| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ------------------------------- | -------------------------------------------------- | --------------------------------------------- | --------- | ----------------------------------------------------------------------- | --------- | -------------- | ------------ |
|
||||
| Issue tracker | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ (cloud only) | ✘ |
|
||||
| ----------------------------- | --------------------------------------------------- | ---- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| Issue tracker | ✓ | ✓ | ✓ | ✓ | ✓ | / | ✘ |
|
||||
| Issue templates | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Labels | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Time tracking | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Multiple assignees for issues | ✓ | ✘ | ✓ | ✘ | ✓ | ✘ | ✘ |
|
||||
| Related issues | ✘ | ✘ | ⁄ | [✓](https://docs.gitlab.com/ce/user/project/issues/related_issues.html) | ✓ | ✘ | ✘ |
|
||||
| Related issues | ✘ | ✘ | ⁄ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Confidential issues | [✘](https://github.com/go-gitea/gitea/issues/3217) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Comment reactions | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Lock Discussion | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Batch issue handling | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Issue Boards (Kanban) | [✓](https://github.com/go-gitea/gitea/pull/8346) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Create new branches from issues | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Issue Boards (Kanban) | [/](https://github.com/go-gitea/gitea/issues/14710) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Create branch from issue | [✘](https://github.com/go-gitea/gitea/issues/20226) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Convert comment to new issue | ✓ | ✘ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Issue search | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Global issue search | [✘](https://github.com/go-gitea/gitea/issues/2434) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Global issue search | [/](https://github.com/go-gitea/gitea/issues/2434) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Issue dependency | ✓ | ✘ | ✘ | ✘ | ✘ | ✘ | ✘ |
|
||||
| Create issue via email | [✘](https://github.com/go-gitea/gitea/issues/6226) | [✘](https://github.com/gogs/gogs/issues/2602) | ✘ | ✘ | ✓ | ✓ | ✘ |
|
||||
| Service Desk | [✘](https://github.com/go-gitea/gitea/issues/6219) | ✘ | ✘ | [✓](https://gitlab.com/groups/gitlab-org/-/epics/3103) | ✓ | ✘ | ✘ |
|
||||
| Create issue via email | [✘](https://github.com/go-gitea/gitea/issues/6226) | ✘ | ✘ | ✘ | ✓ | ✓ | ✘ |
|
||||
| Service Desk | [✘](https://github.com/go-gitea/gitea/issues/6219) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
|
||||
## Pull/Merge requests
|
||||
|
||||
| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ----------------------------------------------- | -------------------------------------------------- | ---- | --------- | --------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------ | ------------ |
|
||||
| ----------------------------------------------- | -------------------------------------------------- | ---- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| Pull/Merge requests | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Squash merging | ✓ | ✘ | ✓ | [✓](https://docs.gitlab.com/ce/user/project/merge_requests/squash_and_merge.html) | ✓ | ✓ | ✓ |
|
||||
| Rebase merging | ✓ | ✓ | ✓ | ✘ | ⁄ | ✘ | ✓ |
|
||||
| Squash merging | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Rebase merging | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Pull/Merge request inline comments | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Pull/Merge request approval | ✓ | ✘ | ⁄ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Merge conflict resolution | [✘](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Restrict push and merge access to certain users | ✓ | ✘ | ✓ | ⁄ | ✓ | ✓ | ✓ |
|
||||
| Revert specific commits or a merge request | [✘](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Pull/Merge request approval | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Merge conflict resolution | [✘](https://github.com/go-gitea/gitea/issues/9014) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Restrict push and merge access to certain users | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Revert specific commits | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Pull/Merge requests templates | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Cherry-picking changes | [✘](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Download Patch | ✓ | ✘ | ✓ | ✓ | ✓ | [/](https://jira.atlassian.com/plugins/servlet/mobile#issue/BCLOUD-8323) | ✘ |
|
||||
| Cherry-picking changes | ✓ | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Download Patch | ✓ | ✘ | ✓ | ✓ | ✓ | / | ✘ |
|
||||
|
||||
## 3rd-party integrations
|
||||
|
||||
| Feature | Gitea | Gogs | GitHub EE | GitLab CE | GitLab EE | BitBucket | RhodeCode CE |
|
||||
| ---------------------------------------------- | ------------------------------------------------ | ---- | --------- | --------- | --------- | --------- | ------------ |
|
||||
| Webhook support | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Custom Git Hooks | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Webhooks | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Git Hooks | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| AD / LDAP integration | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Multiple LDAP / AD server support | ✓ | ✓ | ✘ | ✘ | ✓ | ✓ | ✓ |
|
||||
| LDAP user synchronization | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| SAML 2.0 service provider | [✘](https://github.com/go-gitea/gitea/issues/5512) | [✘](https://github.com/gogs/gogs/issues/1221) | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| OpenId Connect support | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ✘ |
|
||||
| SAML 2.0 service provider | [✘](https://github.com/go-gitea/gitea/issues/5512) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| OpenID Connect support | ✓ | ✘ | ✓ | ✓ | ✓ | ? | ✘ |
|
||||
| OAuth 2.0 integration (external authorization) | ✓ | ✘ | ⁄ | ✓ | ✓ | ? | ✓ |
|
||||
| Act as OAuth 2.0 provider | [✓](https://github.com/go-gitea/gitea/pull/5378) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Act as OAuth 2.0 provider | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Two factor authentication (2FA) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| Mattermost/Slack integration | ✓ | ✓ | ⁄ | ✓ | ✓ | ⁄ | ✓ |
|
||||
| Discord integration | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
|
||||
| Microsoft Teams integration | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
|
||||
| External CI/CD status display | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Integration with the most common services | ✓ | / | ⁄ | ✓ | ✓ | ⁄ | ✓ |
|
||||
| Incorporate external CI/CD | ✓ | ✘ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
|
|
|
@ -71,7 +71,7 @@ choco install gitea
|
|||
macOS 平台下当前我们仅支持通过 `brew` 来安装。如果你没有安装 [Homebrew](http://brew.sh/),你也可以查看 [从二进制安装]({{< relref "from-binary.zh-cn.md" >}})。在你安装了 `brew` 之后, 你可以执行以下命令:
|
||||
|
||||
```
|
||||
brew tap go-gitea/gitea
|
||||
brew tap gitea/tap https://gitea.com/gitea/homebrew-gitea
|
||||
brew install gitea
|
||||
```
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ menu:
|
|||
|
||||
# NuGet Packages Repository
|
||||
|
||||
Publish [NuGet](https://www.nuget.org/) packages for your user or organization. The package registry supports [NuGet Symbol Packages](https://docs.microsoft.com/en-us/nuget/create-packages/symbol-packages-snupkg) too.
|
||||
Publish [NuGet](https://www.nuget.org/) packages for your user or organization. The package registry supports the V2 and V3 API protocol and you can work with [NuGet Symbol Packages](https://docs.microsoft.com/en-us/nuget/create-packages/symbol-packages-snupkg) too.
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
|
|
@ -13,6 +13,12 @@ menu:
|
|||
identifier: "reverse-proxies"
|
||||
---
|
||||
|
||||
# 反向代理
|
||||
|
||||
**目录**
|
||||
|
||||
{{< toc >}}
|
||||
|
||||
## 使用 Nginx 作为反向代理服务
|
||||
|
||||
如果您想使用 Nginx 作为 Gitea 的反向代理服务,您可以参照以下 `nginx.conf` 配置中 `server` 的 `http` 部分:
|
||||
|
@ -24,6 +30,10 @@ server {
|
|||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -41,6 +51,10 @@ server {
|
|||
location /git/ {
|
||||
# 注意: 反向代理后端 URL 的最后需要有一个路径符号
|
||||
proxy_pass http://localhost:3000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
23
docs/static/open-in-gitpod.svg
vendored
Normal file
23
docs/static/open-in-gitpod.svg
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<svg width="160" height="45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g filter="url(#filter0_d)">
|
||||
<rect x="2" y="2" width="156" height="40" rx="16" fill="#F9F9F9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.425 11.174c.604 1.114.233 2.53-.83 3.164l-6.986 4.166a.378.378 0 00-.18.325v6.748c0 .134.069.258.18.325l5.714 3.407c.11.066.244.066.354 0l5.714-3.407a.378.378 0 00.18-.325V21.29l-4.986 2.936c-1.067.628-2.416.231-3.015-.886-.6-1.118-.22-2.532.846-3.16l7.008-4.127c2.048-1.206 4.576.345 4.576 2.806v6.718c0 1.803-.924 3.467-2.42 4.36l-5.713 3.407a4.596 4.596 0 01-4.734 0l-5.714-3.408C18.924 29.044 18 27.38 18 25.576V18.83c0-1.803.924-3.467 2.42-4.36l6.985-4.165c1.063-.634 2.415-.245 3.02.87z" fill="url(#paint0_linear)"/>
|
||||
<path fill="#F9F9F9" d="M47 12.5h95v-1H47z"/>
|
||||
<path d="M52.538 27.752c2.744 0 4.844-1.876 4.844-5.152 0-3.108-2.1-5.152-4.844-5.152s-4.802 2.002-4.802 5.152c0 3.29 2.058 5.152 4.802 5.152zm0-1.554c-1.736 0-2.912-1.316-2.912-3.598 0-2.226 1.162-3.598 2.912-3.598s2.954 1.4 2.954 3.598c0 2.31-1.218 3.598-2.954 3.598zm7.89 4.158V27.22c0-.196-.013-.378-.055-.658.434.7 1.022 1.19 2.17 1.19 1.736 0 3.066-1.414 3.066-3.626 0-2.17-1.19-3.682-2.996-3.682-1.078 0-1.806.476-2.24 1.204.042-.28.056-.462.056-.672v-.308H58.72v9.688h1.708zm1.695-3.948c-1.036 0-1.764-.938-1.764-2.31 0-1.414.742-2.296 1.764-2.296 1.092 0 1.75.952 1.75 2.296 0 1.372-.7 2.31-1.75 2.31zm7.866 1.344c1.848 0 3.052-1.078 3.192-2.478h-1.736c-.112.714-.714 1.134-1.456 1.134-1.036 0-1.722-.826-1.736-1.904h4.97v-.378c0-2.226-1.204-3.682-3.29-3.682-1.988 0-3.43 1.47-3.43 3.626 0 2.366 1.442 3.682 3.486 3.682zm-1.75-4.48c.098-.896.756-1.554 1.68-1.554.924 0 1.526.63 1.554 1.554H68.24zm8.006 4.228v-4.004c0-.952.616-1.694 1.456-1.694.798 0 1.288.63 1.288 1.638v4.06h1.708v-4.312c0-1.68-.896-2.744-2.408-2.744-1.078 0-1.722.518-2.1 1.204.042-.266.056-.476.056-.672v-.308h-1.708V27.5h1.708zm8.911-7.868h1.792V17.84h-1.792v1.792zm.042 1.036V27.5h1.708v-6.832h-1.708zm5.097 6.832v-4.004c0-.952.616-1.694 1.456-1.694.798 0 1.288.63 1.288 1.638v4.06h1.708v-4.312c0-1.68-.896-2.744-2.408-2.744-1.078 0-1.722.518-2.1 1.204.042-.266.056-.476.056-.672v-.308h-1.708V27.5h1.708zm13.238.252c1.526 0 2.52-.658 2.982-1.54-.07.322-.098.644-.098.98v.308h1.68v-5.222h-4.34v1.554h2.66v.07c0 1.4-1.134 2.296-2.59 2.296-1.792 0-3.024-1.372-3.024-3.598s1.26-3.598 3.066-3.598c1.302 0 2.17.756 2.296 1.736h1.89c-.182-1.904-1.764-3.29-4.214-3.29-2.954 0-4.928 2.128-4.928 5.152 0 3.136 1.848 5.152 4.62 5.152zm6.063-8.12h1.792V17.84h-1.792v1.792zm.042 1.036V27.5h1.708v-6.832h-1.708zm6.413 6.958c.434 0 .84-.07 1.008-.126v-1.288c-.168.028-.35.042-.518.042-.728 0-1.008-.42-1.008-1.134v-3.094h1.68v-1.358h-1.68v-2.464h-1.708v2.464h-1.554v1.358h1.554v3.346c0 1.526.77 2.254 2.226 2.254zm3.961 2.73V27.22c0-.196-.014-.378-.056-.658.434.7 1.022 1.19 2.17 1.19 1.736 0 3.066-1.414 3.066-3.626 0-2.17-1.19-3.682-2.996-3.682-1.078 0-1.806.476-2.24 1.204.042-.28.056-.462.056-.672v-.308h-1.708v9.688h1.708zm1.694-3.948c-1.036 0-1.764-.938-1.764-2.31 0-1.414.742-2.296 1.764-2.296 1.092 0 1.75.952 1.75 2.296 0 1.372-.7 2.31-1.75 2.31zm7.88 1.344c2.058 0 3.514-1.372 3.514-3.64 0-2.24-1.456-3.668-3.514-3.668-2.044 0-3.5 1.428-3.5 3.668 0 2.268 1.442 3.64 3.5 3.64zm0-1.344c-1.064 0-1.764-.84-1.764-2.296 0-1.484.728-2.31 1.764-2.31 1.05 0 1.778.826 1.778 2.31 0 1.456-.714 2.296-1.778 2.296zm7.551 1.344c1.26 0 1.876-.686 2.142-1.19-.056.238-.056.42-.056.658v.28h1.708v-9.8h-1.708v3.276c0 .21 0 .42.056.672-.392-.658-1.05-1.204-2.114-1.204-1.596 0-3.15 1.218-3.15 3.668 0 2.408 1.316 3.64 3.122 3.64zm.406-1.344c-1.022 0-1.792-.896-1.792-2.31 0-1.358.77-2.296 1.792-2.296s1.778.896 1.778 2.296c0 1.372-.756 2.31-1.778 2.31z" fill="#12100C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear" x1="33.806" y1="13.629" x2="22.389" y2="30.86" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFB45B"/>
|
||||
<stop offset="1" stop-color="#FF8A00"/>
|
||||
</linearGradient>
|
||||
<filter id="filter0_d" x="0" y=".5" width="160" height="44" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||
<feOffset dy=".5"/>
|
||||
<feGaussianBlur stdDeviation="1"/>
|
||||
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||
<feBlend in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 4.5 KiB |
2
go.mod
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
|||
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b
|
||||
code.gitea.io/sdk/gitea v0.15.1
|
||||
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
|
||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681
|
||||
gitea.com/go-chi/cache v0.2.0
|
||||
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
|
||||
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
|
||||
|
|
4
go.sum
4
go.sum
|
@ -81,8 +81,8 @@ contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcig
|
|||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
|
||||
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb h1:Yy0Bxzc8R2wxiwXoG/rECGplJUSpXqCsog9PuJFgiHs=
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4=
|
||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||
gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w=
|
||||
gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE=
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
// to run tests with ES6 module, node must run with "--experimental-vm-modules", or see Makefile's "test-frontend" for reference
|
||||
export default {
|
||||
rootDir: 'web_src',
|
||||
setupFilesAfterEnv: ['jest-extended/all'],
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
testMatch: ['<rootDir>/**/*.test.js'],
|
||||
testTimeout: 20000,
|
||||
transform: {
|
||||
'\\.svg$': '<rootDir>/js/testUtils/jestRawLoader.js',
|
||||
},
|
||||
setupFiles: [
|
||||
'./js/testUtils/jestSetup.js', // prepare global variables used by our code (eg: window.config)
|
||||
],
|
||||
verbose: false,
|
||||
};
|
|
@ -806,7 +806,7 @@ func getNotificationByID(ctx context.Context, notificationID int64) (*Notificati
|
|||
}
|
||||
|
||||
if !ok {
|
||||
return nil, db.ErrNotExist{ID: notificationID}
|
||||
return nil, db.ErrNotExist{Resource: "notification", ID: notificationID}
|
||||
}
|
||||
|
||||
return notification, nil
|
||||
|
|
|
@ -1,117 +0,0 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package admin_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/admin"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNotice_TrStr(t *testing.T) {
|
||||
notice := &admin.Notice{
|
||||
Type: admin.NoticeRepository,
|
||||
Description: "test description",
|
||||
}
|
||||
assert.Equal(t, "admin.notices.type_1", notice.TrStr())
|
||||
}
|
||||
|
||||
func TestCreateNotice(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
noticeBean := &admin.Notice{
|
||||
Type: admin.NoticeRepository,
|
||||
Description: "test description",
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, noticeBean)
|
||||
assert.NoError(t, admin.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
|
||||
unittest.AssertExistsAndLoadBean(t, noticeBean)
|
||||
}
|
||||
|
||||
func TestCreateRepositoryNotice(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
noticeBean := &admin.Notice{
|
||||
Type: admin.NoticeRepository,
|
||||
Description: "test description",
|
||||
}
|
||||
unittest.AssertNotExistsBean(t, noticeBean)
|
||||
assert.NoError(t, admin.CreateRepositoryNotice(noticeBean.Description))
|
||||
unittest.AssertExistsAndLoadBean(t, noticeBean)
|
||||
}
|
||||
|
||||
// TODO TestRemoveAllWithNotice
|
||||
|
||||
func TestCountNotices(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
assert.Equal(t, int64(3), admin.CountNotices())
|
||||
}
|
||||
|
||||
func TestNotices(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
notices, err := admin.Notices(1, 2)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, notices, 2) {
|
||||
assert.Equal(t, int64(3), notices[0].ID)
|
||||
assert.Equal(t, int64(2), notices[1].ID)
|
||||
}
|
||||
|
||||
notices, err = admin.Notices(2, 2)
|
||||
assert.NoError(t, err)
|
||||
if assert.Len(t, notices, 1) {
|
||||
assert.Equal(t, int64(1), notices[0].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNotice(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNotice(3))
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
|
||||
}
|
||||
|
||||
func TestDeleteNotices(t *testing.T) {
|
||||
// delete a non-empty range
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNotices(1, 2))
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
}
|
||||
|
||||
func TestDeleteNotices2(t *testing.T) {
|
||||
// delete an empty range
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNotices(3, 2))
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
}
|
||||
|
||||
func TestDeleteNoticesByIDs(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 3})
|
||||
assert.NoError(t, admin.DeleteNoticesByIDs([]int64{1, 3}))
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 1})
|
||||
unittest.AssertExistsAndLoadBean(t, &admin.Notice{ID: 2})
|
||||
unittest.AssertNotExistsBean(t, &admin.Notice{ID: 3})
|
||||
}
|
|
@ -167,6 +167,10 @@ func (err ErrTaskDoesNotExist) Error() string {
|
|||
err.ID, err.RepoID, err.Type)
|
||||
}
|
||||
|
||||
func (err ErrTaskDoesNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// GetMigratingTask returns the migrating task by repo's id
|
||||
func GetMigratingTask(repoID int64) (*Task, error) {
|
||||
task := Task{
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
package asymkey
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrKeyUnableVerify represents a "KeyUnableVerify" kind of error.
|
||||
type ErrKeyUnableVerify struct {
|
||||
|
@ -36,6 +40,10 @@ func (err ErrKeyNotExist) Error() string {
|
|||
return fmt.Sprintf("public key does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
func (err ErrKeyNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrKeyAlreadyExist represents a "KeyAlreadyExist" kind of error.
|
||||
type ErrKeyAlreadyExist struct {
|
||||
OwnerID int64
|
||||
|
@ -54,6 +62,10 @@ func (err ErrKeyAlreadyExist) Error() string {
|
|||
err.OwnerID, err.Fingerprint, err.Content)
|
||||
}
|
||||
|
||||
func (err ErrKeyAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrKeyNameAlreadyUsed represents a "KeyNameAlreadyUsed" kind of error.
|
||||
type ErrKeyNameAlreadyUsed struct {
|
||||
OwnerID int64
|
||||
|
@ -70,6 +82,10 @@ func (err ErrKeyNameAlreadyUsed) Error() string {
|
|||
return fmt.Sprintf("public key already exists [owner_id: %d, name: %s]", err.OwnerID, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrKeyNameAlreadyUsed) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrGPGNoEmailFound represents a "ErrGPGNoEmailFound" kind of error.
|
||||
type ErrGPGNoEmailFound struct {
|
||||
FailedEmails []string
|
||||
|
@ -132,6 +148,10 @@ func (err ErrGPGKeyNotExist) Error() string {
|
|||
return fmt.Sprintf("public gpg key does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
func (err ErrGPGKeyNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrGPGKeyImportNotExist represents a "GPGKeyImportNotExist" kind of error.
|
||||
type ErrGPGKeyImportNotExist struct {
|
||||
ID string
|
||||
|
@ -147,6 +167,10 @@ func (err ErrGPGKeyImportNotExist) Error() string {
|
|||
return fmt.Sprintf("public gpg key import does not exist [id: %s]", err.ID)
|
||||
}
|
||||
|
||||
func (err ErrGPGKeyImportNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrGPGKeyIDAlreadyUsed represents a "GPGKeyIDAlreadyUsed" kind of error.
|
||||
type ErrGPGKeyIDAlreadyUsed struct {
|
||||
KeyID string
|
||||
|
@ -162,6 +186,10 @@ func (err ErrGPGKeyIDAlreadyUsed) Error() string {
|
|||
return fmt.Sprintf("public key already exists [key_id: %s]", err.KeyID)
|
||||
}
|
||||
|
||||
func (err ErrGPGKeyIDAlreadyUsed) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrGPGKeyAccessDenied represents a "GPGKeyAccessDenied" kind of Error.
|
||||
type ErrGPGKeyAccessDenied struct {
|
||||
UserID int64
|
||||
|
@ -180,6 +208,10 @@ func (err ErrGPGKeyAccessDenied) Error() string {
|
|||
err.UserID, err.KeyID)
|
||||
}
|
||||
|
||||
func (err ErrGPGKeyAccessDenied) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error.
|
||||
type ErrKeyAccessDenied struct {
|
||||
UserID int64
|
||||
|
@ -198,6 +230,10 @@ func (err ErrKeyAccessDenied) Error() string {
|
|||
err.UserID, err.KeyID, err.Note)
|
||||
}
|
||||
|
||||
func (err ErrKeyAccessDenied) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrDeployKeyNotExist represents a "DeployKeyNotExist" kind of error.
|
||||
type ErrDeployKeyNotExist struct {
|
||||
ID int64
|
||||
|
@ -215,6 +251,10 @@ func (err ErrDeployKeyNotExist) Error() string {
|
|||
return fmt.Sprintf("Deploy key does not exist [id: %d, key_id: %d, repo_id: %d]", err.ID, err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrDeployKeyAlreadyExist represents a "DeployKeyAlreadyExist" kind of error.
|
||||
type ErrDeployKeyAlreadyExist struct {
|
||||
KeyID int64
|
||||
|
@ -231,6 +271,10 @@ func (err ErrDeployKeyAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("public key already exists [key_id: %d, repo_id: %d]", err.KeyID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrDeployKeyNameAlreadyUsed represents a "DeployKeyNameAlreadyUsed" kind of error.
|
||||
type ErrDeployKeyNameAlreadyUsed struct {
|
||||
RepoID int64
|
||||
|
@ -247,6 +291,10 @@ func (err ErrDeployKeyNameAlreadyUsed) Error() string {
|
|||
return fmt.Sprintf("public key with name already exists [repo_id: %d, name: %s]", err.RepoID, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrDeployKeyNameAlreadyUsed) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrSSHInvalidTokenSignature represents a "ErrSSHInvalidTokenSignature" kind of error.
|
||||
type ErrSSHInvalidTokenSignature struct {
|
||||
Wrapped error
|
||||
|
@ -262,3 +310,7 @@ func IsErrSSHInvalidTokenSignature(err error) bool {
|
|||
func (err ErrSSHInvalidTokenSignature) Error() string {
|
||||
return "the provided signature does not sign the token with the provided key"
|
||||
}
|
||||
|
||||
func (err ErrSSHInvalidTokenSignature) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
|
|
@ -31,6 +31,11 @@ type OAuth2Application struct {
|
|||
Name string
|
||||
ClientID string `xorm:"unique"`
|
||||
ClientSecret string
|
||||
// OAuth defines both Confidential and Public client types
|
||||
// https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
|
||||
// "Authorization servers MUST record the client type in the client registration details"
|
||||
// https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
|
||||
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
|
||||
RedirectURIs []string `xorm:"redirect_uris JSON TEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
|
@ -57,6 +62,7 @@ func (app *OAuth2Application) PrimaryRedirectURI() string {
|
|||
|
||||
// ContainsRedirectURI checks if redirectURI is allowed for app
|
||||
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
|
||||
if !app.ConfidentialClient {
|
||||
uri, err := url.Parse(redirectURI)
|
||||
// ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
|
||||
if err == nil && uri.Scheme == "http" && uri.Port() != "" {
|
||||
|
@ -69,6 +75,7 @@ func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return util.IsStringInSlice(redirectURI, app.RedirectURIs, true)
|
||||
}
|
||||
|
||||
|
@ -163,6 +170,7 @@ func GetOAuth2ApplicationsByUserID(ctx context.Context, userID int64) (apps []*O
|
|||
type CreateOAuth2ApplicationOptions struct {
|
||||
Name string
|
||||
UserID int64
|
||||
ConfidentialClient bool
|
||||
RedirectURIs []string
|
||||
}
|
||||
|
||||
|
@ -174,6 +182,7 @@ func CreateOAuth2Application(ctx context.Context, opts CreateOAuth2ApplicationOp
|
|||
Name: opts.Name,
|
||||
ClientID: clientID,
|
||||
RedirectURIs: opts.RedirectURIs,
|
||||
ConfidentialClient: opts.ConfidentialClient,
|
||||
}
|
||||
if err := db.Insert(ctx, app); err != nil {
|
||||
return nil, err
|
||||
|
@ -186,6 +195,7 @@ type UpdateOAuth2ApplicationOptions struct {
|
|||
ID int64
|
||||
Name string
|
||||
UserID int64
|
||||
ConfidentialClient bool
|
||||
RedirectURIs []string
|
||||
}
|
||||
|
||||
|
@ -207,6 +217,7 @@ func UpdateOAuth2Application(opts UpdateOAuth2ApplicationOptions) (*OAuth2Applic
|
|||
|
||||
app.Name = opts.Name
|
||||
app.RedirectURIs = opts.RedirectURIs
|
||||
app.ConfidentialClient = opts.ConfidentialClient
|
||||
|
||||
if err = updateOAuth2Application(ctx, app); err != nil {
|
||||
return nil, err
|
||||
|
@ -217,7 +228,7 @@ func UpdateOAuth2Application(opts UpdateOAuth2ApplicationOptions) (*OAuth2Applic
|
|||
}
|
||||
|
||||
func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error {
|
||||
if _, err := db.GetEngine(ctx).ID(app.ID).Update(app); err != nil {
|
||||
if _, err := db.GetEngine(ctx).ID(app.ID).UseBool("confidential_client").Update(app); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -486,7 +497,7 @@ type ErrOAuthClientIDInvalid struct {
|
|||
ClientID string
|
||||
}
|
||||
|
||||
// IsErrOauthClientIDInvalid checks if an error is a ErrReviewNotExist.
|
||||
// IsErrOauthClientIDInvalid checks if an error is a ErrOAuthClientIDInvalid.
|
||||
func IsErrOauthClientIDInvalid(err error) bool {
|
||||
_, ok := err.(ErrOAuthClientIDInvalid)
|
||||
return ok
|
||||
|
@ -497,6 +508,11 @@ func (err ErrOAuthClientIDInvalid) Error() string {
|
|||
return fmt.Sprintf("Client ID invalid [Client ID: %s]", err.ClientID)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrOAuthClientIDInvalid) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrOAuthApplicationNotFound will be thrown if id cannot be found
|
||||
type ErrOAuthApplicationNotFound struct {
|
||||
ID int64
|
||||
|
@ -513,6 +529,11 @@ func (err ErrOAuthApplicationNotFound) Error() string {
|
|||
return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrOAuthApplicationNotFound) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// GetActiveOAuth2ProviderSources returns all actived LoginOAuth2 sources
|
||||
func GetActiveOAuth2ProviderSources() ([]*Source, error) {
|
||||
sources := make([]*Source, 0, 1)
|
||||
|
|
|
@ -46,6 +46,7 @@ func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
|
|||
func TestOAuth2Application_ContainsRedirectURI_WithPort(t *testing.T) {
|
||||
app := &auth_model.OAuth2Application{
|
||||
RedirectURIs: []string{"http://127.0.0.1/", "http://::1/", "http://192.168.0.1/", "http://intranet/", "https://127.0.0.1/"},
|
||||
ConfidentialClient: false,
|
||||
}
|
||||
|
||||
// http loopback uris should ignore port
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/xorm"
|
||||
"xorm.io/xorm/convert"
|
||||
|
@ -366,6 +367,11 @@ func (err ErrSourceNotExist) Error() string {
|
|||
return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrSourceNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrSourceAlreadyExist represents a "SourceAlreadyExist" kind of error.
|
||||
type ErrSourceAlreadyExist struct {
|
||||
Name string
|
||||
|
@ -381,6 +387,11 @@ func (err ErrSourceAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrExist err
|
||||
func (err ErrSourceAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrSourceInUse represents a "SourceInUse" kind of error.
|
||||
type ErrSourceInUse struct {
|
||||
ID int64
|
||||
|
|
|
@ -35,6 +35,10 @@ func (err ErrAccessTokenNotExist) Error() string {
|
|||
return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
|
||||
type ErrAccessTokenEmpty struct{}
|
||||
|
||||
|
@ -48,6 +52,10 @@ func (err ErrAccessTokenEmpty) Error() string {
|
|||
return "access token is empty"
|
||||
}
|
||||
|
||||
func (err ErrAccessTokenEmpty) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
var successfulAccessTokenCache *lru.Cache
|
||||
|
||||
// AccessToken represents a personal access token.
|
||||
|
|
|
@ -41,6 +41,11 @@ func (err ErrTwoFactorNotEnrolled) Error() string {
|
|||
return fmt.Sprintf("user not enrolled in 2FA [uid: %d]", err.UID)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrTwoFactorNotEnrolled) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// TwoFactor represents a two-factor authentication token.
|
||||
type TwoFactor struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/duo-labs/webauthn/webauthn"
|
||||
"xorm.io/xorm"
|
||||
|
@ -29,6 +30,11 @@ func (err ErrWebAuthnCredentialNotExist) Error() string {
|
|||
return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %x]", err.CredentialID)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrWebAuthnCredentialNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist.
|
||||
func IsErrWebAuthnCredentialNotExist(err error) bool {
|
||||
_, ok := err.(ErrWebAuthnCredentialNotExist)
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -72,7 +73,7 @@ func GetEmailForHash(md5Sum string) (string, error) {
|
|||
// LibravatarURL returns the URL for the given email. Slow due to the DNS lookup.
|
||||
// This function should only be called if a federated avatar service is enabled.
|
||||
func LibravatarURL(email string) (*url.URL, error) {
|
||||
urlStr, err := setting.LibravatarService.FromEmail(email)
|
||||
urlStr, err := system_model.LibravatarService.FromEmail(email)
|
||||
if err != nil {
|
||||
log.Error("LibravatarService.FromEmail(email=%s): error %v", email, err)
|
||||
return nil, err
|
||||
|
@ -149,8 +150,10 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
|
|||
return DefaultAvatarLink()
|
||||
}
|
||||
|
||||
enableFederatedAvatar, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar)
|
||||
|
||||
var err error
|
||||
if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
|
||||
if enableFederatedAvatar != nil && enableFederatedAvatar.GetValueBool() && system_model.LibravatarService != nil {
|
||||
emailHash := saveEmailHash(email)
|
||||
if final {
|
||||
// for final link, we can spend more time on slow external query
|
||||
|
@ -166,12 +169,16 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
|
|||
urlStr += "?size=" + strconv.Itoa(size)
|
||||
}
|
||||
return urlStr
|
||||
} else if !setting.DisableGravatar {
|
||||
}
|
||||
|
||||
disableGravatar, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar)
|
||||
if disableGravatar != nil && !disableGravatar.GetValueBool() {
|
||||
// copy GravatarSourceURL, because we will modify its Path.
|
||||
avatarURLCopy := *setting.GravatarSourceURL
|
||||
avatarURLCopy := *system_model.GravatarSourceURL
|
||||
avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email))
|
||||
return generateRecognizedAvatarURL(avatarURLCopy, size)
|
||||
}
|
||||
|
||||
return DefaultAvatarLink()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package avatars
|
||||
package avatars_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
avatars_model "code.gitea.io/gitea/models/avatars"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -15,40 +16,43 @@ import (
|
|||
|
||||
const gravatarSource = "https://secure.gravatar.com/avatar/"
|
||||
|
||||
func disableGravatar() {
|
||||
setting.EnableFederatedAvatar = false
|
||||
setting.LibravatarService = nil
|
||||
setting.DisableGravatar = true
|
||||
func disableGravatar(t *testing.T) {
|
||||
err := system_model.SetSettingNoVersion(system_model.KeyPictureEnableFederatedAvatar, "false")
|
||||
assert.NoError(t, err)
|
||||
err = system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "true")
|
||||
assert.NoError(t, err)
|
||||
system_model.LibravatarService = nil
|
||||
}
|
||||
|
||||
func enableGravatar(t *testing.T) {
|
||||
setting.DisableGravatar = false
|
||||
var err error
|
||||
setting.GravatarSourceURL, err = url.Parse(gravatarSource)
|
||||
err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false")
|
||||
assert.NoError(t, err)
|
||||
setting.GravatarSource = gravatarSource
|
||||
err = system_model.Init()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestHashEmail(t *testing.T) {
|
||||
assert.Equal(t,
|
||||
"d41d8cd98f00b204e9800998ecf8427e",
|
||||
HashEmail(""),
|
||||
avatars_model.HashEmail(""),
|
||||
)
|
||||
assert.Equal(t,
|
||||
"353cbad9b58e69c96154ad99f92bedc7",
|
||||
HashEmail("gitea@example.com"),
|
||||
avatars_model.HashEmail("gitea@example.com"),
|
||||
)
|
||||
}
|
||||
|
||||
func TestSizedAvatarLink(t *testing.T) {
|
||||
setting.AppSubURL = "/testsuburl"
|
||||
|
||||
disableGravatar()
|
||||
disableGravatar(t)
|
||||
assert.Equal(t, "/testsuburl/assets/img/avatar_default.png",
|
||||
GenerateEmailAvatarFastLink("gitea@example.com", 100))
|
||||
avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100))
|
||||
|
||||
enableGravatar(t)
|
||||
assert.Equal(t,
|
||||
"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100",
|
||||
GenerateEmailAvatarFastLink("gitea@example.com", 100),
|
||||
avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package admin_test
|
||||
package avatars_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
|
@ -225,7 +225,7 @@ func NamesToBean(names ...string) ([]interface{}, error) {
|
|||
for _, name := range names {
|
||||
bean, ok := beanMap[strings.ToLower(strings.TrimSpace(name))]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("No table found that matches: %s", name)
|
||||
return nil, fmt.Errorf("no table found that matches: %s", name)
|
||||
}
|
||||
if !gotBean[bean] {
|
||||
beans = append(beans, bean)
|
||||
|
|
|
@ -6,6 +6,8 @@ package db
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrCancelled represents an error due to context cancellation
|
||||
|
@ -45,6 +47,7 @@ func (err ErrSSHDisabled) Error() string {
|
|||
|
||||
// ErrNotExist represents a non-exist error.
|
||||
type ErrNotExist struct {
|
||||
Resource string
|
||||
ID int64
|
||||
}
|
||||
|
||||
|
@ -55,5 +58,18 @@ func IsErrNotExist(err error) bool {
|
|||
}
|
||||
|
||||
func (err ErrNotExist) Error() string {
|
||||
return fmt.Sprintf("record does not exist [id: %d]", err.ID)
|
||||
name := "record"
|
||||
if err.Resource != "" {
|
||||
name = err.Resource
|
||||
}
|
||||
|
||||
if err.ID != 0 {
|
||||
return fmt.Sprintf("%s does not exist [id: %d]", name, err.ID)
|
||||
}
|
||||
return fmt.Sprintf("%s does not exist", name)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrNotExist err
|
||||
func (err ErrNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
|
|
@ -8,45 +8,15 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// ResourceIndex represents a resource index which could be used as issue/release and others
|
||||
// We can create different tables i.e. issue_index, release_index and etc.
|
||||
// We can create different tables i.e. issue_index, release_index, etc.
|
||||
type ResourceIndex struct {
|
||||
GroupID int64 `xorm:"pk"`
|
||||
MaxIndex int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
// UpsertResourceIndex the function will not return until it acquires the lock or receives an error.
|
||||
func UpsertResourceIndex(ctx context.Context, tableName string, groupID int64) (err error) {
|
||||
// An atomic UPSERT operation (INSERT/UPDATE) is the only operation
|
||||
// that ensures that the key is actually locked.
|
||||
switch {
|
||||
case setting.Database.UseSQLite3 || setting.Database.UsePostgreSQL:
|
||||
_, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
|
||||
"VALUES (?,1) ON CONFLICT (group_id) DO UPDATE SET max_index = %s.max_index+1",
|
||||
tableName, tableName), groupID)
|
||||
case setting.Database.UseMySQL:
|
||||
_, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
|
||||
"VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", tableName),
|
||||
groupID)
|
||||
case setting.Database.UseMSSQL:
|
||||
// https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/
|
||||
_, err = Exec(ctx, fmt.Sprintf("MERGE %s WITH (HOLDLOCK) as target "+
|
||||
"USING (SELECT ? AS group_id) AS src "+
|
||||
"ON src.group_id = target.group_id "+
|
||||
"WHEN MATCHED THEN UPDATE SET target.max_index = target.max_index+1 "+
|
||||
"WHEN NOT MATCHED THEN INSERT (group_id, max_index) "+
|
||||
"VALUES (src.group_id, 1);", tableName),
|
||||
groupID)
|
||||
default:
|
||||
return fmt.Errorf("database type not supported")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrResouceOutdated represents an error when request resource outdated
|
||||
ErrResouceOutdated = errors.New("resource outdated")
|
||||
|
@ -59,53 +29,85 @@ const (
|
|||
MaxDupIndexAttempts = 3
|
||||
)
|
||||
|
||||
// GetNextResourceIndex retried 3 times to generate a resource index
|
||||
func GetNextResourceIndex(tableName string, groupID int64) (int64, error) {
|
||||
for i := 0; i < MaxDupIndexAttempts; i++ {
|
||||
idx, err := getNextResourceIndex(tableName, groupID)
|
||||
if err == ErrResouceOutdated {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return idx, nil
|
||||
}
|
||||
return 0, ErrGetResourceIndexFailed
|
||||
}
|
||||
// SyncMaxResourceIndex sync the max index with the resource
|
||||
func SyncMaxResourceIndex(ctx context.Context, tableName string, groupID, maxIndex int64) (err error) {
|
||||
e := GetEngine(ctx)
|
||||
|
||||
// DeleteResouceIndex delete resource index
|
||||
func DeleteResouceIndex(ctx context.Context, tableName string, groupID int64) error {
|
||||
_, err := Exec(ctx, fmt.Sprintf("DELETE FROM %s WHERE group_id=?", tableName), groupID)
|
||||
// try to update the max_index and acquire the write-lock for the record
|
||||
res, err := e.Exec(fmt.Sprintf("UPDATE %s SET max_index=? WHERE group_id=? AND max_index<?", tableName), maxIndex, groupID, maxIndex)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
// if nothing is updated, the record might not exist or might be larger, it's safe to try to insert it again and then check whether the record exists
|
||||
_, errIns := e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) VALUES (?, ?)", tableName), groupID, maxIndex)
|
||||
var savedIdx int64
|
||||
has, err := e.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id=?", tableName), groupID).Get(&savedIdx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if the record still doesn't exist, there must be some errors (insert error)
|
||||
if !has {
|
||||
if errIns == nil {
|
||||
return errors.New("impossible error when SyncMaxResourceIndex, insert succeeded but no record is saved")
|
||||
}
|
||||
return errIns
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getNextResourceIndex return the next index
|
||||
func getNextResourceIndex(tableName string, groupID int64) (int64, error) {
|
||||
ctx, commiter, err := TxContext()
|
||||
// GetNextResourceIndex generates a resource index, it must run in the same transaction where the resource is created
|
||||
func GetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) {
|
||||
e := GetEngine(ctx)
|
||||
|
||||
// try to update the max_index to next value, and acquire the write-lock for the record
|
||||
res, err := e.Exec(fmt.Sprintf("UPDATE %s SET max_index=max_index+1 WHERE group_id=?", tableName), groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer commiter.Close()
|
||||
var preIdx int64
|
||||
if _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&preIdx); err != nil {
|
||||
affected, err := res.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := UpsertResourceIndex(ctx, tableName, groupID); err != nil {
|
||||
if affected == 0 {
|
||||
// this slow path is only for the first time of creating a resource index
|
||||
_, errIns := e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) VALUES (?, 0)", tableName), groupID)
|
||||
res, err = e.Exec(fmt.Sprintf("UPDATE %s SET max_index=max_index+1 WHERE group_id=?", tableName), groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
affected, err = res.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// if the update still can not update any records, the record must not exist and there must be some errors (insert error)
|
||||
if affected == 0 {
|
||||
if errIns == nil {
|
||||
return 0, errors.New("impossible error when GetNextResourceIndex, insert and update both succeeded but no record is updated")
|
||||
}
|
||||
return 0, errIns
|
||||
}
|
||||
}
|
||||
|
||||
var curIdx int64
|
||||
has, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ? AND max_index=?", tableName), groupID, preIdx+1).Get(&curIdx)
|
||||
// now, the new index is in database (protected by the transaction and write-lock)
|
||||
var newIdx int64
|
||||
has, err := e.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id=?", tableName), groupID).Get(&newIdx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !has {
|
||||
return 0, ErrResouceOutdated
|
||||
return 0, errors.New("impossible error when GetNextResourceIndex, upsert succeeded but no record can be selected")
|
||||
}
|
||||
if err := commiter.Commit(); err != nil {
|
||||
return 0, err
|
||||
return newIdx, nil
|
||||
}
|
||||
return curIdx, nil
|
||||
|
||||
// DeleteResourceIndex delete resource index
|
||||
func DeleteResourceIndex(ctx context.Context, tableName string, groupID int64) error {
|
||||
_, err := Exec(ctx, fmt.Sprintf("DELETE FROM %s WHERE group_id=?", tableName), groupID)
|
||||
return err
|
||||
}
|
||||
|
|
127
models/db/index_test.go
Normal file
127
models/db/index_test.go
Normal file
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package db_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestIndex db.ResourceIndex
|
||||
|
||||
func getCurrentResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
var idx int64
|
||||
has, err := e.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id=?", tableName), groupID).Get(&idx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !has {
|
||||
return 0, errors.New("no record")
|
||||
}
|
||||
return idx, nil
|
||||
}
|
||||
|
||||
func TestSyncMaxResourceIndex(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
xe := unittest.GetXORMEngine()
|
||||
assert.NoError(t, xe.Sync(&TestIndex{}))
|
||||
|
||||
err := db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 51)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// sync new max index
|
||||
maxIndex, err := getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 51, maxIndex)
|
||||
|
||||
// smaller index doesn't change
|
||||
err = db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 30)
|
||||
assert.NoError(t, err)
|
||||
maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 51, maxIndex)
|
||||
|
||||
// larger index changes
|
||||
err = db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 62)
|
||||
assert.NoError(t, err)
|
||||
maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 62, maxIndex)
|
||||
|
||||
// commit transaction
|
||||
err = db.WithTx(func(ctx context.Context) error {
|
||||
err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 73)
|
||||
assert.NoError(t, err)
|
||||
maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 73, maxIndex)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 73, maxIndex)
|
||||
|
||||
// rollback transaction
|
||||
err = db.WithTx(func(ctx context.Context) error {
|
||||
err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 84)
|
||||
maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 84, maxIndex)
|
||||
return errors.New("test rollback")
|
||||
})
|
||||
assert.Error(t, err)
|
||||
maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 73, maxIndex) // the max index doesn't change because the transaction was rolled back
|
||||
}
|
||||
|
||||
func TestGetNextResourceIndex(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
xe := unittest.GetXORMEngine()
|
||||
assert.NoError(t, xe.Sync(&TestIndex{}))
|
||||
|
||||
// create a new record
|
||||
maxIndex, err := db.GetNextResourceIndex(db.DefaultContext, "test_index", 20)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, maxIndex)
|
||||
|
||||
// increase the existing record
|
||||
maxIndex, err = db.GetNextResourceIndex(db.DefaultContext, "test_index", 20)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 2, maxIndex)
|
||||
|
||||
// commit transaction
|
||||
err = db.WithTx(func(ctx context.Context) error {
|
||||
maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, maxIndex)
|
||||
return nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 20)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, maxIndex)
|
||||
|
||||
// rollback transaction
|
||||
err = db.WithTx(func(ctx context.Context) error {
|
||||
maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 4, maxIndex)
|
||||
return errors.New("test rollback")
|
||||
})
|
||||
assert.Error(t, err)
|
||||
maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 20)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, maxIndex) // the max index doesn't change because the transaction was rolled back
|
||||
}
|
|
@ -5,16 +5,17 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNameEmpty name is empty error
|
||||
ErrNameEmpty = errors.New("Name is empty")
|
||||
ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
|
||||
|
||||
// AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-)
|
||||
AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
|
||||
|
@ -35,6 +36,11 @@ func (err ErrNameReserved) Error() string {
|
|||
return fmt.Sprintf("name is reserved [name: %s]", err.Name)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrInvalid err
|
||||
func (err ErrNameReserved) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrNamePatternNotAllowed represents a "pattern not allowed" error.
|
||||
type ErrNamePatternNotAllowed struct {
|
||||
Pattern string
|
||||
|
@ -50,6 +56,11 @@ func (err ErrNamePatternNotAllowed) Error() string {
|
|||
return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrInvalid err
|
||||
func (err ErrNamePatternNotAllowed) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrNameCharsNotAllowed represents a "character not allowed in name" error.
|
||||
type ErrNameCharsNotAllowed struct {
|
||||
Name string
|
||||
|
@ -62,7 +73,12 @@ func IsErrNameCharsNotAllowed(err error) bool {
|
|||
}
|
||||
|
||||
func (err ErrNameCharsNotAllowed) Error() string {
|
||||
return fmt.Sprintf("User name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name)
|
||||
return fmt.Sprintf("name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name)
|
||||
}
|
||||
|
||||
// Unwrap unwraps this as a ErrInvalid err
|
||||
func (err ErrNameCharsNotAllowed) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// IsUsableName checks if name is reserved or pattern of name is not allowed
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
|
||||
|
@ -63,8 +64,8 @@ type ErrNoPendingRepoTransfer struct {
|
|||
RepoID int64
|
||||
}
|
||||
|
||||
func (e ErrNoPendingRepoTransfer) Error() string {
|
||||
return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", e.RepoID)
|
||||
func (err ErrNoPendingRepoTransfer) Error() string {
|
||||
return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID)
|
||||
}
|
||||
|
||||
// IsErrNoPendingTransfer is an error type when a repository has no pending
|
||||
|
@ -74,6 +75,10 @@ func IsErrNoPendingTransfer(err error) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
func (err ErrNoPendingRepoTransfer) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrRepoTransferInProgress represents the state of a repository that has an
|
||||
// ongoing transfer
|
||||
type ErrRepoTransferInProgress struct {
|
||||
|
@ -91,6 +96,10 @@ func (err ErrRepoTransferInProgress) Error() string {
|
|||
return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrRepoTransferInProgress) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||
type ErrInvalidCloneAddr struct {
|
||||
Host string
|
||||
|
@ -124,6 +133,10 @@ func (err *ErrInvalidCloneAddr) Error() string {
|
|||
return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host)
|
||||
}
|
||||
|
||||
func (err *ErrInvalidCloneAddr) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error.
|
||||
type ErrUpdateTaskNotExist struct {
|
||||
UUID string
|
||||
|
@ -139,6 +152,10 @@ func (err ErrUpdateTaskNotExist) Error() string {
|
|||
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
|
||||
}
|
||||
|
||||
func (err ErrUpdateTaskNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrInvalidTagName represents a "InvalidTagName" kind of error.
|
||||
type ErrInvalidTagName struct {
|
||||
TagName string
|
||||
|
@ -154,6 +171,10 @@ func (err ErrInvalidTagName) Error() string {
|
|||
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrInvalidTagName) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrProtectedTagName represents a "ProtectedTagName" kind of error.
|
||||
type ErrProtectedTagName struct {
|
||||
TagName string
|
||||
|
@ -169,6 +190,10 @@ func (err ErrProtectedTagName) Error() string {
|
|||
return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrProtectedTagName) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error.
|
||||
type ErrRepoFileAlreadyExists struct {
|
||||
Path string
|
||||
|
@ -184,6 +209,10 @@ func (err ErrRepoFileAlreadyExists) Error() string {
|
|||
return fmt.Sprintf("repository file already exists [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrRepoFileAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error.
|
||||
type ErrRepoFileDoesNotExist struct {
|
||||
Path string
|
||||
|
@ -200,6 +229,10 @@ func (err ErrRepoFileDoesNotExist) Error() string {
|
|||
return fmt.Sprintf("repository file does not exist [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrRepoFileDoesNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
|
||||
type ErrFilenameInvalid struct {
|
||||
Path string
|
||||
|
@ -215,6 +248,10 @@ func (err ErrFilenameInvalid) Error() string {
|
|||
return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrFilenameInvalid) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrUserCannotCommit represents "UserCannotCommit" kind of error.
|
||||
type ErrUserCannotCommit struct {
|
||||
UserName string
|
||||
|
@ -230,6 +267,10 @@ func (err ErrUserCannotCommit) Error() string {
|
|||
return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName)
|
||||
}
|
||||
|
||||
func (err ErrUserCannotCommit) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrFilePathInvalid represents a "FilePathInvalid" kind of error.
|
||||
type ErrFilePathInvalid struct {
|
||||
Message string
|
||||
|
@ -251,6 +292,10 @@ func (err ErrFilePathInvalid) Error() string {
|
|||
return fmt.Sprintf("path is invalid [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrFilePathInvalid) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrFilePathProtected represents a "FilePathProtected" kind of error.
|
||||
type ErrFilePathProtected struct {
|
||||
Message string
|
||||
|
@ -270,6 +315,10 @@ func (err ErrFilePathProtected) Error() string {
|
|||
return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
|
||||
}
|
||||
|
||||
func (err ErrFilePathProtected) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// __________ .__
|
||||
// \______ \____________ ____ ____ | |__
|
||||
// | | _/\_ __ \__ \ / \_/ ___\| | \
|
||||
|
@ -292,6 +341,10 @@ func (err ErrBranchDoesNotExist) Error() string {
|
|||
return fmt.Sprintf("branch does not exist [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchDoesNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrBranchAlreadyExists represents an error that branch with such name already exists.
|
||||
type ErrBranchAlreadyExists struct {
|
||||
BranchName string
|
||||
|
@ -307,6 +360,10 @@ func (err ErrBranchAlreadyExists) Error() string {
|
|||
return fmt.Sprintf("branch already exists [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrBranchNameConflict represents an error that branch name conflicts with other branch.
|
||||
type ErrBranchNameConflict struct {
|
||||
BranchName string
|
||||
|
@ -322,6 +379,10 @@ func (err ErrBranchNameConflict) Error() string {
|
|||
return fmt.Sprintf("branch conflicts with existing branch [name: %s]", err.BranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchNameConflict) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrBranchesEqual represents an error that branch name conflicts with other branch.
|
||||
type ErrBranchesEqual struct {
|
||||
BaseBranchName string
|
||||
|
@ -338,6 +399,10 @@ func (err ErrBranchesEqual) Error() string {
|
|||
return fmt.Sprintf("branches are equal [head: %sm base: %s]", err.HeadBranchName, err.BaseBranchName)
|
||||
}
|
||||
|
||||
func (err ErrBranchesEqual) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
|
||||
type ErrDisallowedToMerge struct {
|
||||
Reason string
|
||||
|
@ -353,6 +418,10 @@ func (err ErrDisallowedToMerge) Error() string {
|
|||
return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
|
||||
}
|
||||
|
||||
func (err ErrDisallowedToMerge) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrTagAlreadyExists represents an error that tag with such name already exists.
|
||||
type ErrTagAlreadyExists struct {
|
||||
TagName string
|
||||
|
@ -368,6 +437,10 @@ func (err ErrTagAlreadyExists) Error() string {
|
|||
return fmt.Sprintf("tag already exists [name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrTagAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
|
||||
type ErrSHADoesNotMatch struct {
|
||||
Path string
|
||||
|
@ -400,6 +473,10 @@ func (err ErrSHANotFound) Error() string {
|
|||
return fmt.Sprintf("sha not found [%s]", err.SHA)
|
||||
}
|
||||
|
||||
func (err ErrSHANotFound) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error.
|
||||
type ErrCommitIDDoesNotMatch struct {
|
||||
GivenCommitID string
|
||||
|
@ -446,6 +523,10 @@ func (err ErrInvalidMergeStyle) Error() string {
|
|||
err.ID, err.Style)
|
||||
}
|
||||
|
||||
func (err ErrInvalidMergeStyle) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrMergeConflicts represents an error if merging fails with a conflict
|
||||
type ErrMergeConflicts struct {
|
||||
Style repo_model.MergeStyle
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
|
||||
repo_id: 1
|
||||
issue_id: 1
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 0
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
|
@ -13,9 +16,12 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12
|
||||
repo_id: 2
|
||||
issue_id: 4
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 0
|
||||
name: attach2
|
||||
download_count: 1
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
|
@ -23,9 +29,12 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13
|
||||
repo_id: 1
|
||||
issue_id: 2
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 1
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
|
@ -33,9 +42,12 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14
|
||||
repo_id: 1
|
||||
issue_id: 3
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 1
|
||||
name: attach2
|
||||
download_count: 1
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
|
@ -43,9 +55,12 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15
|
||||
repo_id: 2
|
||||
issue_id: 4
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 0
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
|
@ -53,9 +68,12 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16
|
||||
repo_id: 1
|
||||
issue_id: 5
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 2
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
|
@ -63,9 +81,12 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17
|
||||
repo_id: 1
|
||||
issue_id: 5
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 2
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
|
@ -73,34 +94,49 @@
|
|||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18
|
||||
repo_id: 3
|
||||
issue_id: 6
|
||||
release_id: 0
|
||||
uploader_id: 0
|
||||
comment_id: 0
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
id: 9
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a19
|
||||
repo_id: 1
|
||||
issue_id: 0
|
||||
release_id: 1
|
||||
uploader_id: 0
|
||||
comment_id: 0
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
id: 10
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20
|
||||
repo_id: 0 # TestGetAttachment/NotLinked
|
||||
issue_id: 0
|
||||
release_id: 0
|
||||
uploader_id: 8
|
||||
comment_id: 0
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
||||
-
|
||||
id: 11
|
||||
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21
|
||||
repo_id: 40
|
||||
issue_id: 0
|
||||
release_id: 2
|
||||
uploader_id: 0
|
||||
comment_id: 0
|
||||
name: attach1
|
||||
download_count: 0
|
||||
size: 0
|
||||
created_unix: 946684800
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
hook_id: 1
|
||||
uuid: uuid1
|
||||
is_delivered: true
|
||||
|
|
|
@ -3,208 +3,287 @@
|
|||
repo_id: 1
|
||||
index: 1
|
||||
poster_id: 1
|
||||
original_author_id: 0
|
||||
name: issue1
|
||||
content: content for the first issue
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 2
|
||||
created_unix: 946684800
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 2
|
||||
repo_id: 1
|
||||
index: 2
|
||||
poster_id: 1
|
||||
original_author_id: 0
|
||||
name: issue2
|
||||
content: content for the second issue
|
||||
milestone_id: 1
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: true
|
||||
num_comments: 0
|
||||
created_unix: 946684810
|
||||
updated_unix: 978307190
|
||||
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 3
|
||||
repo_id: 1
|
||||
index: 3
|
||||
poster_id: 1
|
||||
original_author_id: 0
|
||||
name: issue3
|
||||
content: content for the third issue
|
||||
milestone_id: 3
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: true
|
||||
num_comments: 0
|
||||
created_unix: 946684820
|
||||
updated_unix: 978307180
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 4
|
||||
repo_id: 2
|
||||
index: 1
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: issue4
|
||||
content: content for the fourth issue
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: true
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 946684830
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 5
|
||||
repo_id: 1
|
||||
index: 4
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: issue5
|
||||
content: content for the fifth issue
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: true
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 946684840
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 6
|
||||
repo_id: 3
|
||||
index: 1
|
||||
poster_id: 1
|
||||
original_author_id: 0
|
||||
name: issue6
|
||||
content: content6
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 946684850
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 7
|
||||
repo_id: 2
|
||||
index: 2
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: issue7
|
||||
content: content for the seventh issue
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 946684830
|
||||
updated_unix: 978307200
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 8
|
||||
repo_id: 10
|
||||
index: 1
|
||||
poster_id: 11
|
||||
original_author_id: 0
|
||||
name: pr2
|
||||
content: a pull request
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: true
|
||||
num_comments: 0
|
||||
created_unix: 946684820
|
||||
updated_unix: 978307180
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 9
|
||||
repo_id: 48
|
||||
index: 1
|
||||
poster_id: 11
|
||||
original_author_id: 0
|
||||
name: pr1
|
||||
content: a pull request
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: true
|
||||
num_comments: 0
|
||||
created_unix: 946684820
|
||||
updated_unix: 978307180
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 10
|
||||
repo_id: 42
|
||||
index: 1
|
||||
poster_id: 500
|
||||
original_author_id: 0
|
||||
name: issue from deleted account
|
||||
content: content from deleted account
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
deadline_unix: 1019307200
|
||||
created_unix: 946684830
|
||||
updated_unix: 999307200
|
||||
deadline_unix: 1019307200
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 11
|
||||
repo_id: 1
|
||||
index: 5
|
||||
poster_id: 1
|
||||
original_author_id: 0
|
||||
name: pull5
|
||||
content: content for the a pull request
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: true
|
||||
num_comments: 0
|
||||
created_unix: 1579194806
|
||||
updated_unix: 1579194806
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 12
|
||||
repo_id: 3
|
||||
index: 2
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: pull6
|
||||
content: content for the a pull request
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: true
|
||||
num_comments: 0
|
||||
created_unix: 1602935696
|
||||
updated_unix: 1602935696
|
||||
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 13
|
||||
repo_id: 50
|
||||
index: 1
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: issue in active repo
|
||||
content: we'll be testing github issue 13171 with this.
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 1602935696
|
||||
updated_unix: 1602935696
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 14
|
||||
repo_id: 51
|
||||
index: 1
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: issue in archived repo
|
||||
content: we'll be testing github issue 13171 with this.
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 1602935696
|
||||
updated_unix: 1602935696
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 15
|
||||
repo_id: 5
|
||||
index: 1
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: issue in repo not linked to team1
|
||||
content: content
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 1602935696
|
||||
updated_unix: 1602935696
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 16
|
||||
repo_id: 32
|
||||
index: 1
|
||||
poster_id: 2
|
||||
original_author_id: 0
|
||||
name: just a normal issue
|
||||
content: content
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 1602935696
|
||||
updated_unix: 1602935696
|
||||
is_locked: false
|
||||
|
||||
-
|
||||
id: 17
|
||||
repo_id: 32
|
||||
index: 2
|
||||
poster_id: 15
|
||||
original_author_id: 0
|
||||
name: a issue with a assignment
|
||||
content: content
|
||||
milestone_id: 0
|
||||
priority: 0
|
||||
is_closed: false
|
||||
is_pull: false
|
||||
num_comments: 0
|
||||
created_unix: 1602935696
|
||||
updated_unix: 1602935696
|
||||
is_locked: false
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
color: '#000000'
|
||||
num_issues: 1
|
||||
num_closed_issues: 1
|
||||
|
||||
-
|
||||
id: 3
|
||||
repo_id: 0
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
is_closed: false
|
||||
num_issues: 1
|
||||
num_closed_issues: 0
|
||||
completeness: 0
|
||||
deadline_unix: 253370764800
|
||||
|
||||
-
|
||||
|
@ -16,6 +17,7 @@
|
|||
is_closed: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
completeness: 0
|
||||
deadline_unix: 253370764800
|
||||
|
||||
-
|
||||
|
@ -26,6 +28,7 @@
|
|||
is_closed: true
|
||||
num_issues: 1
|
||||
num_closed_issues: 0
|
||||
completeness: 0
|
||||
deadline_unix: 253370764800
|
||||
|
||||
-
|
||||
|
@ -36,6 +39,7 @@
|
|||
is_closed: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
completeness: 0
|
||||
deadline_unix: 253370764800
|
||||
|
||||
-
|
||||
|
@ -46,4 +50,5 @@
|
|||
is_closed: false
|
||||
num_issues: 0
|
||||
num_closed_issues: 0
|
||||
completeness: 0
|
||||
deadline_unix: 253370764800
|
||||
|
|
|
@ -7,3 +7,14 @@
|
|||
redirect_uris: '["a", "https://example.com/xyzzy"]'
|
||||
created_unix: 1546869730
|
||||
updated_unix: 1546869730
|
||||
confidential_client: true
|
||||
-
|
||||
id: 2
|
||||
uid: 2
|
||||
name: "Test native app"
|
||||
client_id: "ce5a1322-42a7-11ed-b878-0242ac120002"
|
||||
client_secret: "$2a$10$UYRgUSgekzBp6hYe8pAdc.cgB4Gn06QRKsORUnIYTYQADs.YR/uvi" # bcrypt of "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=
|
||||
redirect_uris: '["http://127.0.0.1"]'
|
||||
created_unix: 1546869730
|
||||
updated_unix: 1546869730
|
||||
confidential_client: false
|
||||
|
|
|
@ -6,3 +6,10 @@
|
|||
redirect_uri: "a"
|
||||
valid_until: 3546869730
|
||||
|
||||
- id: 2
|
||||
grant_id: 4
|
||||
code: "authcodepublic"
|
||||
code_challenge: "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg" # Code Verifier: N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt
|
||||
code_challenge_method: "S256"
|
||||
redirect_uri: "http://127.0.0.1/"
|
||||
valid_until: 3546869730
|
||||
|
|
|
@ -21,3 +21,11 @@
|
|||
scope: "openid profile email"
|
||||
created_unix: 1546869730
|
||||
updated_unix: 1546869730
|
||||
|
||||
- id: 4
|
||||
user_id: 99
|
||||
application_id: 2
|
||||
counter: 1
|
||||
scope: "whatever"
|
||||
created_unix: 1546869730
|
||||
updated_unix: 1546869730
|
||||
|
|
File diff suppressed because it is too large
Load diff
15
models/fixtures/system_setting.yml
Normal file
15
models/fixtures/system_setting.yml
Normal file
|
@ -0,0 +1,15 @@
|
|||
-
|
||||
id: 1
|
||||
setting_key: 'disable_gravatar'
|
||||
setting_value: 'false'
|
||||
version: 1
|
||||
created: 1653533198
|
||||
updated: 1653533198
|
||||
|
||||
-
|
||||
id: 2
|
||||
setting_key: 'enable_federated_avatar'
|
||||
setting_value: 'false'
|
||||
version: 1
|
||||
created: 1653533198
|
||||
updated: 1653533198
|
|
@ -6,6 +6,7 @@
|
|||
authorize: 4 # owner
|
||||
num_repos: 3
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
|
@ -16,6 +17,7 @@
|
|||
authorize: 2 # write
|
||||
num_repos: 1
|
||||
num_members: 2
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: false
|
||||
|
||||
-
|
||||
|
@ -26,6 +28,7 @@
|
|||
authorize: 4 # owner
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
|
@ -36,6 +39,7 @@
|
|||
authorize: 4 # owner
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
|
@ -46,6 +50,7 @@
|
|||
authorize: 4 # owner
|
||||
num_repos: 2
|
||||
num_members: 2
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
|
@ -56,6 +61,7 @@
|
|||
authorize: 4 # owner
|
||||
num_repos: 2
|
||||
num_members: 2
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
|
@ -66,6 +72,7 @@
|
|||
authorize: 2 # write
|
||||
num_repos: 1
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: false
|
||||
|
||||
-
|
||||
|
@ -76,6 +83,7 @@
|
|||
authorize: 2 # write
|
||||
num_repos: 1
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: false
|
||||
|
||||
-
|
||||
|
@ -86,6 +94,7 @@
|
|||
authorize: 1 # read
|
||||
num_repos: 1
|
||||
num_members: 2
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: false
|
||||
|
||||
-
|
||||
|
@ -93,9 +102,10 @@
|
|||
org_id: 25
|
||||
lower_name: notowners
|
||||
name: NotOwners
|
||||
authorize: 1 # owner
|
||||
authorize: 1 # read
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: false
|
||||
|
||||
-
|
||||
|
@ -106,6 +116,7 @@
|
|||
authorize: 1 # read
|
||||
num_repos: 0
|
||||
num_members: 0
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: false
|
||||
|
||||
-
|
||||
|
@ -116,6 +127,7 @@
|
|||
authorize: 3 # admin
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: true
|
||||
|
||||
-
|
||||
|
@ -126,4 +138,5 @@
|
|||
authorize: 3 # admin
|
||||
num_repos: 0
|
||||
num_members: 1
|
||||
includes_all_repositories: false
|
||||
can_create_org_repo: false
|
||||
|
|
|
@ -8,18 +8,22 @@
|
|||
name: database
|
||||
repo_count: 1
|
||||
|
||||
- id: 3
|
||||
-
|
||||
id: 3
|
||||
name: SQL
|
||||
repo_count: 1
|
||||
|
||||
- id: 4
|
||||
-
|
||||
id: 4
|
||||
name: graphql
|
||||
repo_count: 1
|
||||
|
||||
- id: 5
|
||||
-
|
||||
id: 5
|
||||
name: topicname1
|
||||
repo_count: 1
|
||||
|
||||
- id: 6
|
||||
-
|
||||
id: 6
|
||||
name: topicname2
|
||||
repo_count: 2
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,6 @@
|
|||
- id: 1
|
||||
name: "WebAuthn credential"
|
||||
-
|
||||
id: 1
|
||||
name: WebAuthn credential
|
||||
user_id: 32
|
||||
attestation_type: none
|
||||
sign_count: 0
|
||||
|
|
|
@ -6,6 +6,8 @@ package foreignreference
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrLocalIndexNotExist represents a "LocalIndexNotExist" kind of error.
|
||||
|
@ -25,6 +27,10 @@ func (err ErrLocalIndexNotExist) Error() string {
|
|||
return fmt.Sprintf("repository %d has no LocalIndex for ForeignIndex %d of type %s", err.RepoID, err.ForeignIndex, err.Type)
|
||||
}
|
||||
|
||||
func (err ErrLocalIndexNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrForeignIndexNotExist represents a "ForeignIndexNotExist" kind of error.
|
||||
type ErrForeignIndexNotExist struct {
|
||||
RepoID int64
|
||||
|
@ -41,3 +47,7 @@ func IsErrForeignIndexNotExist(err error) bool {
|
|||
func (err ErrForeignIndexNotExist) Error() string {
|
||||
return fmt.Sprintf("repository %d has no ForeignIndex for LocalIndex %d of type %s", err.RepoID, err.LocalIndex, err.Type)
|
||||
}
|
||||
|
||||
func (err ErrForeignIndexNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ package git
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
@ -17,6 +16,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -38,6 +38,10 @@ func (err ErrLFSLockNotExist) Error() string {
|
|||
return fmt.Sprintf("lfs lock does not exist [id: %d, rid: %d, path: %s]", err.ID, err.RepoID, err.Path)
|
||||
}
|
||||
|
||||
func (err ErrLFSLockNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrLFSUnauthorizedAction represents a "LFSUnauthorizedAction" kind of error.
|
||||
type ErrLFSUnauthorizedAction struct {
|
||||
RepoID int64
|
||||
|
@ -58,6 +62,10 @@ func (err ErrLFSUnauthorizedAction) Error() string {
|
|||
return fmt.Sprintf("User %s doesn't have read access for lfs lock [rid: %d]", err.UserName, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrLFSUnauthorizedAction) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error.
|
||||
type ErrLFSLockAlreadyExist struct {
|
||||
RepoID int64
|
||||
|
@ -74,6 +82,10 @@ func (err ErrLFSLockAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("lfs lock already exists [rid: %d, path: %s]", err.RepoID, err.Path)
|
||||
}
|
||||
|
||||
func (err ErrLFSLockAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrLFSFileLocked represents a "LFSFileLocked" kind of error.
|
||||
type ErrLFSFileLocked struct {
|
||||
RepoID int64
|
||||
|
@ -91,6 +103,10 @@ func (err ErrLFSFileLocked) Error() string {
|
|||
return fmt.Sprintf("File is lfs locked [repo: %d, locked by: %s, path: %s]", err.RepoID, err.UserName, err.Path)
|
||||
}
|
||||
|
||||
func (err ErrLFSFileLocked) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// LFSMetaObject stores metadata for LFS tracked files.
|
||||
type LFSMetaObject struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
@ -114,7 +130,7 @@ type LFSTokenResponse struct {
|
|||
|
||||
// ErrLFSObjectNotExist is returned from lfs models functions in order
|
||||
// to differentiate between database and missing object errors.
|
||||
var ErrLFSObjectNotExist = errors.New("LFS Meta object does not exist")
|
||||
var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
|
||||
|
||||
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
|
||||
// if it is not already present.
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/references"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
|
@ -49,6 +50,10 @@ func (err ErrCommentNotExist) Error() string {
|
|||
return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID)
|
||||
}
|
||||
|
||||
func (err ErrCommentNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
|
||||
type CommentType int
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -201,6 +202,10 @@ func (err ErrIssueContentHistoryNotExist) Error() string {
|
|||
return fmt.Sprintf("issue content history does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
func (err ErrIssueContentHistoryNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// GetIssueContentHistoryByID get issue content history
|
||||
func GetIssueContentHistoryByID(dbCtx context.Context, id int64) (*ContentHistory, error) {
|
||||
h := &ContentHistory{}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrDependencyExists represents a "DependencyAlreadyExists" kind of error.
|
||||
|
@ -29,6 +30,10 @@ func (err ErrDependencyExists) Error() string {
|
|||
return fmt.Sprintf("issue dependency does already exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
|
||||
}
|
||||
|
||||
func (err ErrDependencyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrDependencyNotExists represents a "DependencyAlreadyExists" kind of error.
|
||||
type ErrDependencyNotExists struct {
|
||||
IssueID int64
|
||||
|
@ -45,6 +50,10 @@ func (err ErrDependencyNotExists) Error() string {
|
|||
return fmt.Sprintf("issue dependency does not exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
|
||||
}
|
||||
|
||||
func (err ErrDependencyNotExists) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrCircularDependency represents a "DependencyCircular" kind of error.
|
||||
type ErrCircularDependency struct {
|
||||
IssueID int64
|
||||
|
@ -91,6 +100,10 @@ func (err ErrUnknownDependencyType) Error() string {
|
|||
return fmt.Sprintf("unknown dependency type [type: %d]", err.Type)
|
||||
}
|
||||
|
||||
func (err ErrUnknownDependencyType) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// IssueDependency represents an issue dependency
|
||||
type IssueDependency struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
admin_model "code.gitea.io/gitea/models/admin"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/foreignreference"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
|
@ -21,6 +20,7 @@ import (
|
|||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
|
@ -52,6 +52,10 @@ func (err ErrIssueNotExist) Error() string {
|
|||
return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
|
||||
}
|
||||
|
||||
func (err ErrIssueNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
|
||||
type ErrIssueIsClosed struct {
|
||||
ID int64
|
||||
|
@ -1064,19 +1068,19 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
|
|||
|
||||
// NewIssue creates new issue with labels for repository.
|
||||
func NewIssue(repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
|
||||
idx, err := db.GetNextResourceIndex("issue_index", repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate issue index failed: %v", err)
|
||||
}
|
||||
|
||||
issue.Index = idx
|
||||
|
||||
ctx, committer, err := db.TxContext()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate issue index failed: %w", err)
|
||||
}
|
||||
|
||||
issue.Index = idx
|
||||
|
||||
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
||||
Repo: repo,
|
||||
Issue: issue,
|
||||
|
@ -1492,7 +1496,8 @@ func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Sess
|
|||
builder.In("issue.repo_id", builder.
|
||||
Select("id").
|
||||
From("watch").
|
||||
Where(builder.Eq{"user_id": subscriberID, "mode": true}),
|
||||
Where(builder.And(builder.Eq{"user_id": subscriberID},
|
||||
builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -2470,7 +2475,7 @@ func DeleteOrphanedIssues() error {
|
|||
|
||||
// Remove issue attachment files.
|
||||
for i := range attachmentPaths {
|
||||
admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
|
||||
system_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -15,16 +15,12 @@ func RecalculateIssueIndexForRepo(repoID int64) error {
|
|||
}
|
||||
defer committer.Close()
|
||||
|
||||
if err := db.UpsertResourceIndex(ctx, "issue_index", repoID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var max int64
|
||||
if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
|
||||
if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(ctx).Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil {
|
||||
if err = db.SyncMaxResourceIndex(ctx, "issue_index", repoID, max); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -131,7 +131,11 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu
|
|||
r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo})
|
||||
d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer})
|
||||
|
||||
idx, err := db.GetNextResourceIndex("issue_index", r.ID)
|
||||
ctx, committer, err := db.TxContext()
|
||||
assert.NoError(t, err)
|
||||
defer committer.Close()
|
||||
|
||||
idx, err := db.GetNextResourceIndex(ctx, "issue_index", r.ID)
|
||||
assert.NoError(t, err)
|
||||
i := &issues_model.Issue{
|
||||
RepoID: r.ID,
|
||||
|
@ -143,9 +147,6 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu
|
|||
Index: idx,
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext()
|
||||
assert.NoError(t, err)
|
||||
defer committer.Close()
|
||||
err = issues_model.NewIssueWithIndex(ctx, d, issues_model.NewIssueOptions{
|
||||
Repo: r,
|
||||
Issue: i,
|
||||
|
|
|
@ -38,6 +38,10 @@ func (err ErrRepoLabelNotExist) Error() string {
|
|||
return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrRepoLabelNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrOrgLabelNotExist represents a "OrgLabelNotExist" kind of error.
|
||||
type ErrOrgLabelNotExist struct {
|
||||
LabelID int64
|
||||
|
@ -54,6 +58,10 @@ func (err ErrOrgLabelNotExist) Error() string {
|
|||
return fmt.Sprintf("label does not exist [label_id: %d, org_id: %d]", err.LabelID, err.OrgID)
|
||||
}
|
||||
|
||||
func (err ErrOrgLabelNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrLabelNotExist represents a "LabelNotExist" kind of error.
|
||||
type ErrLabelNotExist struct {
|
||||
LabelID int64
|
||||
|
@ -69,6 +77,10 @@ func (err ErrLabelNotExist) Error() string {
|
|||
return fmt.Sprintf("label does not exist [label_id: %d]", err.LabelID)
|
||||
}
|
||||
|
||||
func (err ErrLabelNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// LabelColorPattern is a regexp witch can validate LabelColor
|
||||
var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$")
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -39,6 +40,10 @@ func (err ErrMilestoneNotExist) Error() string {
|
|||
return fmt.Sprintf("milestone does not exist [id: %d, repo_id: %d]", err.ID, err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrMilestoneNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// Milestone represents a milestone of repository.
|
||||
type Milestone struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
|
@ -46,6 +46,10 @@ func (err ErrPullRequestNotExist) Error() string {
|
|||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
|
||||
}
|
||||
|
||||
func (err ErrPullRequestNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error
|
||||
type ErrPullRequestAlreadyExists struct {
|
||||
ID int64
|
||||
|
@ -68,6 +72,10 @@ func (err ErrPullRequestAlreadyExists) Error() string {
|
|||
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
|
||||
}
|
||||
|
||||
func (err ErrPullRequestAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrPullRequestHeadRepoMissing represents a "ErrPullRequestHeadRepoMissing" error
|
||||
type ErrPullRequestHeadRepoMissing struct {
|
||||
ID int64
|
||||
|
@ -490,13 +498,6 @@ func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
|
|||
|
||||
// NewPullRequest creates new pull request with labels for repository.
|
||||
func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
|
||||
idx, err := db.GetNextResourceIndex("issue_index", repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate pull request index failed: %v", err)
|
||||
}
|
||||
|
||||
issue.Index = idx
|
||||
|
||||
ctx, committer, err := db.TxContext()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -504,6 +505,13 @@ func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue
|
|||
defer committer.Close()
|
||||
ctx.WithContext(outerCtx)
|
||||
|
||||
idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("generate pull request index failed: %v", err)
|
||||
}
|
||||
|
||||
issue.Index = idx
|
||||
|
||||
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
|
||||
Repo: repo,
|
||||
Issue: issue,
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -34,6 +35,10 @@ func (err ErrForbiddenIssueReaction) Error() string {
|
|||
return fmt.Sprintf("'%s' is not an allowed reaction", err.Reaction)
|
||||
}
|
||||
|
||||
func (err ErrForbiddenIssueReaction) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrReactionAlreadyExist is used when a existing reaction was try to created
|
||||
type ErrReactionAlreadyExist struct {
|
||||
Reaction string
|
||||
|
@ -49,6 +54,10 @@ func (err ErrReactionAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("reaction '%s' already exists", err.Reaction)
|
||||
}
|
||||
|
||||
func (err ErrReactionAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// Reaction represents a reactions on issues and comments.
|
||||
type Reaction struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
|
@ -39,6 +39,10 @@ func (err ErrReviewNotExist) Error() string {
|
|||
return fmt.Sprintf("review does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
func (err ErrReviewNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrNotValidReviewRequest an not allowed review request modify
|
||||
type ErrNotValidReviewRequest struct {
|
||||
Reason string
|
||||
|
@ -59,6 +63,10 @@ func (err ErrNotValidReviewRequest) Error() string {
|
|||
err.RepoID)
|
||||
}
|
||||
|
||||
func (err ErrNotValidReviewRequest) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ReviewType defines the sort of feedback a review gives
|
||||
type ReviewType int
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ func (err ErrIssueStopwatchNotExist) Error() string {
|
|||
return fmt.Sprintf("issue stopwatch doesn't exist[uid: %d, issue_id: %d", err.UserID, err.IssueID)
|
||||
}
|
||||
|
||||
func (err ErrIssueStopwatchNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrIssueStopwatchAlreadyExist represents an error that stopwatch is already exist
|
||||
type ErrIssueStopwatchAlreadyExist struct {
|
||||
UserID int64
|
||||
|
@ -35,6 +39,10 @@ func (err ErrIssueStopwatchAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("issue stopwatch already exists[uid: %d, issue_id: %d", err.UserID, err.IssueID)
|
||||
}
|
||||
|
||||
func (err ErrIssueStopwatchAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// Stopwatch represents a stopwatch for time tracking.
|
||||
type Stopwatch struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
|
@ -236,7 +236,7 @@ func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error {
|
|||
return err
|
||||
}
|
||||
if removedTime == 0 {
|
||||
return db.ErrNotExist{}
|
||||
return db.ErrNotExist{Resource: "tracked_time"}
|
||||
}
|
||||
|
||||
if err := issue.LoadRepo(ctx); err != nil {
|
||||
|
@ -296,7 +296,7 @@ func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime
|
|||
|
||||
func deleteTime(ctx context.Context, t *TrackedTime) error {
|
||||
if t.Deleted {
|
||||
return db.ErrNotExist{ID: t.ID}
|
||||
return db.ErrNotExist{Resource: "tracked_time", ID: t.ID}
|
||||
}
|
||||
t.Deleted = true
|
||||
_, err := db.GetEngine(ctx).ID(t.ID).Cols("deleted").Update(t)
|
||||
|
@ -310,7 +310,7 @@ func GetTrackedTimeByID(id int64) (*TrackedTime, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
} else if !has {
|
||||
return nil, db.ErrNotExist{ID: id}
|
||||
return nil, db.ErrNotExist{Resource: "tracked_time", ID: id}
|
||||
}
|
||||
return time, nil
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import (
|
|||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
_ "code.gitea.io/gitea/models/system"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-
|
||||
id: 1
|
|
@ -0,0 +1,19 @@
|
|||
# type Milestone struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# IsClosed bool
|
||||
# NumIssues int
|
||||
# NumClosedIssues int
|
||||
# Completeness int // Percentage(1-100).
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
is_closed: false
|
||||
num_issues: 3
|
||||
num_closed_issues: 1
|
||||
completeness: 33
|
||||
-
|
||||
id: 2
|
||||
is_closed: true
|
||||
num_issues: 5
|
||||
num_closed_issues: 5
|
||||
completeness: 100
|
|
@ -0,0 +1,25 @@
|
|||
# type Issue struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# RepoID int64 `xorm:"INDEX UNIQUE(repo_index)"`
|
||||
# Index int64 `xorm:"UNIQUE(repo_index)"` // Index in one repository.
|
||||
# MilestoneID int64 `xorm:"INDEX"`
|
||||
# IsClosed bool `xorm:"INDEX"`
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
repo_id: 1
|
||||
index: 1
|
||||
milestone_id: 1
|
||||
is_closed: false
|
||||
-
|
||||
id: 2
|
||||
repo_id: 1
|
||||
index: 2
|
||||
milestone_id: 1
|
||||
is_closed: true
|
||||
-
|
||||
id: 4
|
||||
repo_id: 1
|
||||
index: 3
|
||||
milestone_id: 1
|
||||
is_closed: false
|
|
@ -0,0 +1,19 @@
|
|||
# type Milestone struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# IsClosed bool
|
||||
# NumIssues int
|
||||
# NumClosedIssues int
|
||||
# Completeness int // Percentage(1-100).
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
is_closed: false
|
||||
num_issues: 4
|
||||
num_closed_issues: 2
|
||||
completeness: 50
|
||||
-
|
||||
id: 2
|
||||
is_closed: true
|
||||
num_issues: 5
|
||||
num_closed_issues: 5
|
||||
completeness: 100
|
|
@ -415,6 +415,14 @@ var migrations = []Migration{
|
|||
NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText),
|
||||
// v226 -> v227
|
||||
NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField),
|
||||
// v227 -> v228
|
||||
NewMigration("Create key/value table for system settings", createSystemSettingsTable),
|
||||
// v228 -> v229
|
||||
NewMigration("Add TeamInvite table", addTeamInviteTable),
|
||||
// v229 -> v230
|
||||
NewMigration("Update counts of all open milestones", updateOpenMilestoneCounts),
|
||||
// v230 -> v231
|
||||
NewMigration("Add ConfidentialClient column (default true) to OAuth2Application table", addConfidentialClientColumnToOAuth2ApplicationTable),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
|
@ -83,17 +83,17 @@ func fixMergeBase(x *xorm.Engine) error {
|
|||
|
||||
if !pr.HasMerged {
|
||||
var err error
|
||||
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base", "--", pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(pr.BaseBranch, gitRefName).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
var err2 error
|
||||
pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse", git.BranchPrefix+pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
pr.MergeBase, _, err2 = git.NewCommand(git.DefaultContext, "rev-parse").AddDynamicArguments(git.BranchPrefix + pr.BaseBranch).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err2 != nil {
|
||||
log.Error("Unable to get merge base for PR ID %d, Index %d in %s/%s. Error: %v & %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err, err2)
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
|
@ -103,10 +103,11 @@ func fixMergeBase(x *xorm.Engine) error {
|
|||
continue
|
||||
}
|
||||
|
||||
args := append([]string{"merge-base", "--"}, parents[1:]...)
|
||||
args = append(args, gitRefName)
|
||||
refs := append([]string{}, parents[1:]...)
|
||||
refs = append(refs, gitRefName)
|
||||
cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
|
||||
|
||||
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, args...).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
|
|
|
@ -80,7 +80,7 @@ func refixMergeBase(x *xorm.Engine) error {
|
|||
|
||||
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
|
||||
|
||||
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1", pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
|
@ -91,10 +91,11 @@ func refixMergeBase(x *xorm.Engine) error {
|
|||
}
|
||||
|
||||
// we should recalculate
|
||||
args := append([]string{"merge-base", "--"}, parents[1:]...)
|
||||
args = append(args, gitRefName)
|
||||
refs := append([]string{}, parents[1:]...)
|
||||
refs = append(refs, gitRefName)
|
||||
cmd := git.NewCommand(git.DefaultContext, "merge-base").AddDashesAndList(refs...)
|
||||
|
||||
pr.MergeBase, _, err = git.NewCommand(git.DefaultContext, args...).RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
pr.MergeBase, _, err = cmd.RunStdString(&git.RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
log.Error("Unable to get merge base for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err)
|
||||
continue
|
||||
|
|
64
models/migrations/v227.go
Normal file
64
models/migrations/v227.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type SystemSetting struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
SettingKey string `xorm:"varchar(255) unique"` // ensure key is always lowercase
|
||||
SettingValue string `xorm:"text"`
|
||||
Version int `xorm:"version"` // prevent to override
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
func insertSettingsIfNotExist(x *xorm.Engine, sysSettings []*SystemSetting) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, setting := range sysSettings {
|
||||
exist, err := sess.Table("system_setting").Where("setting_key=?", setting.SettingKey).Exist()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
if _, err := sess.Insert(setting); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return sess.Commit()
|
||||
}
|
||||
|
||||
func createSystemSettingsTable(x *xorm.Engine) error {
|
||||
if err := x.Sync2(new(SystemSetting)); err != nil {
|
||||
return fmt.Errorf("sync2: %v", err)
|
||||
}
|
||||
|
||||
// migrate xx to database
|
||||
sysSettings := []*SystemSetting{
|
||||
{
|
||||
SettingKey: "picture.disable_gravatar",
|
||||
SettingValue: strconv.FormatBool(setting.DisableGravatar),
|
||||
},
|
||||
{
|
||||
SettingKey: "picture.enable_federated_avatar",
|
||||
SettingValue: strconv.FormatBool(setting.EnableFederatedAvatar),
|
||||
},
|
||||
}
|
||||
|
||||
return insertSettingsIfNotExist(x, sysSettings)
|
||||
}
|
26
models/migrations/v228.go
Normal file
26
models/migrations/v228.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addTeamInviteTable(x *xorm.Engine) error {
|
||||
type TeamInvite struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Token string `xorm:"UNIQUE(token) INDEX NOT NULL DEFAULT ''"`
|
||||
InviterID int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
|
||||
TeamID int64 `xorm:"UNIQUE(team_mail) INDEX NOT NULL DEFAULT 0"`
|
||||
Email string `xorm:"UNIQUE(team_mail) NOT NULL DEFAULT ''"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
}
|
||||
|
||||
return x.Sync2(new(TeamInvite))
|
||||
}
|
47
models/migrations/v229.go
Normal file
47
models/migrations/v229.go
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/issues"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func updateOpenMilestoneCounts(x *xorm.Engine) error {
|
||||
var openMilestoneIDs []int64
|
||||
err := x.Table("milestone").Select("id").Where(builder.Neq{"is_closed": 1}).Find(&openMilestoneIDs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error selecting open milestone IDs: %w", err)
|
||||
}
|
||||
|
||||
for _, id := range openMilestoneIDs {
|
||||
_, err := x.ID(id).
|
||||
SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
|
||||
builder.Eq{"milestone_id": id},
|
||||
)).
|
||||
SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
|
||||
builder.Eq{
|
||||
"milestone_id": id,
|
||||
"is_closed": true,
|
||||
},
|
||||
)).
|
||||
Update(&issues.Milestone{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating issue counts in milestone %d: %w", id, err)
|
||||
}
|
||||
_, err = x.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
|
||||
id,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting completeness on milestone %d: %w", id, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
46
models/migrations/v229_test.go
Normal file
46
models/migrations/v229_test.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/issues"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_updateOpenMilestoneCounts(t *testing.T) {
|
||||
type ExpectedMilestone issues.Milestone
|
||||
|
||||
// Prepare and load the testing database
|
||||
x, deferable := prepareTestEnv(t, 0, new(issues.Milestone), new(ExpectedMilestone), new(issues.Issue))
|
||||
defer deferable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := updateOpenMilestoneCounts(x); err != nil {
|
||||
assert.NoError(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
expected := []ExpectedMilestone{}
|
||||
if err := x.Table("expected_milestone").Asc("id").Find(&expected); !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
got := []issues.Milestone{}
|
||||
if err := x.Table("milestone").Asc("id").Find(&got); !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
for i, e := range expected {
|
||||
got := got[i]
|
||||
assert.Equal(t, e.ID, got.ID)
|
||||
assert.Equal(t, e.NumIssues, got.NumIssues)
|
||||
assert.Equal(t, e.NumClosedIssues, got.NumClosedIssues)
|
||||
}
|
||||
}
|
18
models/migrations/v230.go
Normal file
18
models/migrations/v230.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// addConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
|
||||
func addConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
|
||||
type OAuth2Application struct {
|
||||
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
|
||||
}
|
||||
|
||||
return x.Sync(new(OAuth2Application))
|
||||
}
|
46
models/migrations/v230_test.go
Normal file
46
models/migrations/v230_test.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_addConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
|
||||
// premigration
|
||||
type OAuth2Application struct {
|
||||
ID int64
|
||||
}
|
||||
|
||||
// Prepare and load the testing database
|
||||
x, deferable := prepareTestEnv(t, 0, new(OAuth2Application))
|
||||
defer deferable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := addConfidentialClientColumnToOAuth2ApplicationTable(x); err != nil {
|
||||
assert.NoError(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
// postmigration
|
||||
type ExpectedOAuth2Application struct {
|
||||
ID int64
|
||||
ConfidentialClient bool
|
||||
}
|
||||
|
||||
got := []ExpectedOAuth2Application{}
|
||||
if err := x.Table("o_auth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
assert.NotEmpty(t, got)
|
||||
for _, e := range got {
|
||||
assert.True(t, e.ConfidentialClient)
|
||||
}
|
||||
}
|
|
@ -431,25 +431,15 @@ func DeleteTeam(t *organization.Team) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Delete team-user.
|
||||
if _, err := sess.
|
||||
Where("org_id=?", t.OrgID).
|
||||
Where("team_id=?", t.ID).
|
||||
Delete(new(organization.TeamUser)); err != nil {
|
||||
if err := db.DeleteBeans(ctx,
|
||||
&organization.Team{ID: t.ID},
|
||||
&organization.TeamUser{OrgID: t.OrgID, TeamID: t.ID},
|
||||
&organization.TeamUnit{TeamID: t.ID},
|
||||
&organization.TeamInvite{TeamID: t.ID},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete team-unit.
|
||||
if _, err := sess.
|
||||
Where("team_id=?", t.ID).
|
||||
Delete(new(organization.TeamUnit)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete team.
|
||||
if _, err := sess.ID(t.ID).Delete(new(organization.Team)); err != nil {
|
||||
return err
|
||||
}
|
||||
// Update organization number of teams.
|
||||
if _, err := sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
|
||||
return err
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -45,6 +46,10 @@ func (err ErrOrgNotExist) Error() string {
|
|||
return fmt.Sprintf("org does not exist [id: %d, name: %s]", err.ID, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrOrgNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
|
||||
type ErrLastOrgOwner struct {
|
||||
UID int64
|
||||
|
@ -73,6 +78,10 @@ func (err ErrUserNotAllowedCreateOrg) Error() string {
|
|||
return "user is not allowed to create organizations"
|
||||
}
|
||||
|
||||
func (err ErrUserNotAllowedCreateOrg) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// Organization represents an organization
|
||||
type Organization user_model.User
|
||||
|
||||
|
@ -361,8 +370,9 @@ func DeleteOrganization(ctx context.Context, org *Organization) error {
|
|||
&OrgUser{OrgID: org.ID},
|
||||
&TeamUser{OrgID: org.ID},
|
||||
&TeamUnit{OrgID: org.ID},
|
||||
&TeamInvite{OrgID: org.ID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("deleteBeans: %v", err)
|
||||
return fmt.Errorf("DeleteBeans: %v", err)
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(ctx).ID(org.ID).Delete(new(user_model.User)); err != nil {
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -43,6 +44,10 @@ func (err ErrTeamAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrTeamAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrTeamNotExist represents a "TeamNotExist" error
|
||||
type ErrTeamNotExist struct {
|
||||
OrgID int64
|
||||
|
@ -60,6 +65,10 @@ func (err ErrTeamNotExist) Error() string {
|
|||
return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
|
||||
}
|
||||
|
||||
func (err ErrTeamNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// OwnerTeamName return the owner team name
|
||||
const OwnerTeamName = "Owners"
|
||||
|
||||
|
@ -85,6 +94,7 @@ func init() {
|
|||
db.RegisterModel(new(TeamUser))
|
||||
db.RegisterModel(new(TeamRepo))
|
||||
db.RegisterModel(new(TeamUnit))
|
||||
db.RegisterModel(new(TeamInvite))
|
||||
}
|
||||
|
||||
// SearchTeamOptions holds the search options
|
||||
|
|
162
models/organization/team_invite.go
Normal file
162
models/organization/team_invite.go
Normal file
|
@ -0,0 +1,162 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package organization
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
type ErrTeamInviteAlreadyExist struct {
|
||||
TeamID int64
|
||||
Email string
|
||||
}
|
||||
|
||||
func IsErrTeamInviteAlreadyExist(err error) bool {
|
||||
_, ok := err.(ErrTeamInviteAlreadyExist)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTeamInviteAlreadyExist) Error() string {
|
||||
return fmt.Sprintf("team invite already exists [team_id: %d, email: %s]", err.TeamID, err.Email)
|
||||
}
|
||||
|
||||
func (err ErrTeamInviteAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
type ErrTeamInviteNotFound struct {
|
||||
Token string
|
||||
}
|
||||
|
||||
func IsErrTeamInviteNotFound(err error) bool {
|
||||
_, ok := err.(ErrTeamInviteNotFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrTeamInviteNotFound) Error() string {
|
||||
return fmt.Sprintf("team invite was not found [token: %s]", err.Token)
|
||||
}
|
||||
|
||||
func (err ErrTeamInviteNotFound) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrUserEmailAlreadyAdded represents a "user by email already added to team" error.
|
||||
type ErrUserEmailAlreadyAdded struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
// IsErrUserEmailAlreadyAdded checks if an error is a ErrUserEmailAlreadyAdded.
|
||||
func IsErrUserEmailAlreadyAdded(err error) bool {
|
||||
_, ok := err.(ErrUserEmailAlreadyAdded)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrUserEmailAlreadyAdded) Error() string {
|
||||
return fmt.Sprintf("user with email already added [email: %s]", err.Email)
|
||||
}
|
||||
|
||||
func (err ErrUserEmailAlreadyAdded) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// TeamInvite represents an invite to a team
|
||||
type TeamInvite struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Token string `xorm:"UNIQUE(token) INDEX NOT NULL DEFAULT ''"`
|
||||
InviterID int64 `xorm:"NOT NULL DEFAULT 0"`
|
||||
OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
|
||||
TeamID int64 `xorm:"UNIQUE(team_mail) INDEX NOT NULL DEFAULT 0"`
|
||||
Email string `xorm:"UNIQUE(team_mail) NOT NULL DEFAULT ''"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
}
|
||||
|
||||
func CreateTeamInvite(ctx context.Context, doer *user_model.User, team *Team, email string) (*TeamInvite, error) {
|
||||
has, err := db.GetEngine(ctx).Exist(&TeamInvite{
|
||||
TeamID: team.ID,
|
||||
Email: email,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
return nil, ErrTeamInviteAlreadyExist{
|
||||
TeamID: team.ID,
|
||||
Email: email,
|
||||
}
|
||||
}
|
||||
|
||||
// check if the user is already a team member by email
|
||||
exist, err := db.GetEngine(ctx).
|
||||
Where(builder.Eq{
|
||||
"team_user.org_id": team.OrgID,
|
||||
"team_user.team_id": team.ID,
|
||||
"`user`.email": email,
|
||||
}).
|
||||
Join("INNER", "`user`", "`user`.id = team_user.uid").
|
||||
Table("team_user").
|
||||
Exist()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exist {
|
||||
return nil, ErrUserEmailAlreadyAdded{
|
||||
Email: email,
|
||||
}
|
||||
}
|
||||
|
||||
token, err := util.CryptoRandomString(25)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
invite := &TeamInvite{
|
||||
Token: token,
|
||||
InviterID: doer.ID,
|
||||
OrgID: team.OrgID,
|
||||
TeamID: team.ID,
|
||||
Email: email,
|
||||
}
|
||||
|
||||
return invite, db.Insert(ctx, invite)
|
||||
}
|
||||
|
||||
func RemoveInviteByID(ctx context.Context, inviteID, teamID int64) error {
|
||||
_, err := db.DeleteByBean(ctx, &TeamInvite{
|
||||
ID: inviteID,
|
||||
TeamID: teamID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func GetInvitesByTeamID(ctx context.Context, teamID int64) ([]*TeamInvite, error) {
|
||||
invites := make([]*TeamInvite, 0, 10)
|
||||
return invites, db.GetEngine(ctx).
|
||||
Where("team_id=?", teamID).
|
||||
Find(&invites)
|
||||
}
|
||||
|
||||
func GetInviteByToken(ctx context.Context, token string) (*TeamInvite, error) {
|
||||
invite := &TeamInvite{}
|
||||
|
||||
has, err := db.GetEngine(ctx).Where("token=?", token).Get(invite)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, ErrTeamInviteNotFound{Token: token}
|
||||
}
|
||||
return invite, nil
|
||||
}
|
49
models/organization/team_invite_test.go
Normal file
49
models/organization/team_invite_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package organization_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTeamInvite(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
|
||||
|
||||
t.Run("MailExistsInTeam", func(t *testing.T) {
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
|
||||
// user 2 already added to team 2, should result in error
|
||||
_, err := organization.CreateTeamInvite(db.DefaultContext, user2, team, user2.Email)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("CreateAndRemove", func(t *testing.T) {
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
|
||||
invite, err := organization.CreateTeamInvite(db.DefaultContext, user1, team, "user3@example.com")
|
||||
assert.NotNil(t, invite)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Shouldn't allow duplicate invite
|
||||
_, err = organization.CreateTeamInvite(db.DefaultContext, user1, team, "user3@example.com")
|
||||
assert.Error(t, err)
|
||||
|
||||
// should remove invite
|
||||
assert.NoError(t, organization.RemoveInviteByID(db.DefaultContext, invite.ID, invite.TeamID))
|
||||
|
||||
// invite should not exist
|
||||
_, err = organization.GetInviteByToken(db.DefaultContext, invite.Token)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
|
@ -165,6 +165,7 @@ type ImageTagsSearchOptions struct {
|
|||
PackageID int64
|
||||
Query string
|
||||
IsTagged bool
|
||||
Sort packages.VersionSort
|
||||
db.Paginator
|
||||
}
|
||||
|
||||
|
@ -195,12 +196,26 @@ func (opts *ImageTagsSearchOptions) toConds() builder.Cond {
|
|||
return cond
|
||||
}
|
||||
|
||||
func (opts *ImageTagsSearchOptions) configureOrderBy(e db.Engine) {
|
||||
switch opts.Sort {
|
||||
case packages.SortVersionDesc:
|
||||
e.Desc("package_version.version")
|
||||
case packages.SortVersionAsc:
|
||||
e.Asc("package_version.version")
|
||||
case packages.SortCreatedAsc:
|
||||
e.Asc("package_version.created_unix")
|
||||
default:
|
||||
e.Desc("package_version.created_unix")
|
||||
}
|
||||
}
|
||||
|
||||
// SearchImageTags gets a sorted list of the tags of an image
|
||||
func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*packages.PackageVersion, int64, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Join("INNER", "package", "package.id = package_version.package_id").
|
||||
Where(opts.toConds()).
|
||||
Desc("package_version.created_unix")
|
||||
Where(opts.toConds())
|
||||
|
||||
opts.configureOrderBy(sess)
|
||||
|
||||
if opts.Paginator != nil {
|
||||
sess = db.SetSessionPagination(sess, opts)
|
||||
|
|
|
@ -163,6 +163,17 @@ type SearchValue struct {
|
|||
ExactMatch bool
|
||||
}
|
||||
|
||||
type VersionSort = string
|
||||
|
||||
const (
|
||||
SortNameAsc VersionSort = "name_asc"
|
||||
SortNameDesc VersionSort = "name_desc"
|
||||
SortVersionAsc VersionSort = "version_asc"
|
||||
SortVersionDesc VersionSort = "version_desc"
|
||||
SortCreatedAsc VersionSort = "created_asc"
|
||||
SortCreatedDesc VersionSort = "created_desc"
|
||||
)
|
||||
|
||||
// PackageSearchOptions are options for SearchXXX methods
|
||||
// Besides IsInternal are all fields optional and are not used if they have their default value (nil, "", 0)
|
||||
type PackageSearchOptions struct {
|
||||
|
@ -176,7 +187,7 @@ type PackageSearchOptions struct {
|
|||
IsInternal util.OptionalBool
|
||||
HasFileWithName string // only results are found which are associated with a file with the specific name
|
||||
HasFiles util.OptionalBool // only results are found which have associated files
|
||||
Sort string
|
||||
Sort VersionSort
|
||||
db.Paginator
|
||||
}
|
||||
|
||||
|
@ -254,15 +265,15 @@ func (opts *PackageSearchOptions) toConds() builder.Cond {
|
|||
|
||||
func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
|
||||
switch opts.Sort {
|
||||
case "alphabetically":
|
||||
case SortNameAsc:
|
||||
e.Asc("package.name")
|
||||
case "reversealphabetically":
|
||||
case SortNameDesc:
|
||||
e.Desc("package.name")
|
||||
case "highestversion":
|
||||
case SortVersionDesc:
|
||||
e.Desc("package_version.version")
|
||||
case "lowestversion":
|
||||
case SortVersionAsc:
|
||||
e.Asc("package_version.version")
|
||||
case "oldest":
|
||||
case SortCreatedAsc:
|
||||
e.Asc("package_version.created_unix")
|
||||
default:
|
||||
e.Desc("package_version.created_unix")
|
||||
|
|
|
@ -55,6 +55,10 @@ func (err ErrProjectNotExist) Error() string {
|
|||
return fmt.Sprintf("projects does not exist [id: %d]", err.ID)
|
||||
}
|
||||
|
||||
func (err ErrProjectNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrProjectBoardNotExist represents a "ProjectBoardNotExist" kind of error.
|
||||
type ErrProjectBoardNotExist struct {
|
||||
BoardID int64
|
||||
|
@ -70,6 +74,10 @@ func (err ErrProjectBoardNotExist) Error() string {
|
|||
return fmt.Sprintf("project board does not exist [id: %d]", err.BoardID)
|
||||
}
|
||||
|
||||
func (err ErrProjectBoardNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// Project represents a project board
|
||||
type Project struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
|
@ -90,7 +90,7 @@ func DeleteScheduledAutoMerge(ctx context.Context, pullID int64) error {
|
|||
if err != nil {
|
||||
return err
|
||||
} else if !exist {
|
||||
return db.ErrNotExist{ID: pullID}
|
||||
return db.ErrNotExist{Resource: "auto_merge", ID: pullID}
|
||||
}
|
||||
|
||||
_, err = db.GetEngine(ctx).ID(scheduledPRM.ID).Delete(&AutoMerge{})
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
project_model "code.gitea.io/gitea/models/project"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/models/webhook"
|
||||
|
@ -32,9 +33,13 @@ import (
|
|||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// NewRepoContext creates a new repository context
|
||||
func NewRepoContext() {
|
||||
// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
|
||||
var ItemsPerPage = 40
|
||||
|
||||
// Init initialize model
|
||||
func Init() error {
|
||||
unit.LoadUnitConfig()
|
||||
return system_model.Init()
|
||||
}
|
||||
|
||||
// DeleteRepository deletes a repository for a user or organization.
|
||||
|
@ -118,6 +123,11 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(ctx).In("hook_id", builder.Select("id").From("webhook").Where(builder.Eq{"webhook.repo_id": repo.ID})).
|
||||
Delete(&webhook.HookTask{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := db.DeleteBeans(ctx,
|
||||
&access_model.Access{RepoID: repo.ID},
|
||||
&activities_model.Action{RepoID: repo.ID},
|
||||
|
@ -125,7 +135,6 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||
&issues_model.Comment{RefRepoID: repoID},
|
||||
&git_model.CommitStatus{RepoID: repoID},
|
||||
&git_model.DeletedBranch{RepoID: repoID},
|
||||
&webhook.HookTask{RepoID: repoID},
|
||||
&git_model.LFSLock{RepoID: repoID},
|
||||
&repo_model.LanguageStat{RepoID: repoID},
|
||||
&issues_model.Milestone{RepoID: repoID},
|
||||
|
@ -163,7 +172,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||
}
|
||||
|
||||
// Delete issue index
|
||||
if err := db.DeleteResouceIndex(ctx, "issue_index", repoID); err != nil {
|
||||
if err := db.DeleteResourceIndex(ctx, "issue_index", repoID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -267,36 +276,36 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
|
|||
|
||||
// Remove repository files.
|
||||
repoPath := repo.RepoPath()
|
||||
admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath)
|
||||
system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath)
|
||||
|
||||
// Remove wiki files
|
||||
if repo.HasWiki() {
|
||||
admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath())
|
||||
system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath())
|
||||
}
|
||||
|
||||
// Remove archives
|
||||
for _, archive := range archivePaths {
|
||||
admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive)
|
||||
system_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive)
|
||||
}
|
||||
|
||||
// Remove lfs objects
|
||||
for _, lfsObj := range lfsPaths {
|
||||
admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj)
|
||||
system_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj)
|
||||
}
|
||||
|
||||
// Remove issue attachment files.
|
||||
for _, attachment := range attachmentPaths {
|
||||
admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment)
|
||||
system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment)
|
||||
}
|
||||
|
||||
// Remove release attachment files.
|
||||
for _, releaseAttachment := range releaseAttachments {
|
||||
admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment)
|
||||
system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment)
|
||||
}
|
||||
|
||||
// Remove attachment with no issue_id and release_id.
|
||||
for _, newAttachment := range newAttachmentPaths {
|
||||
admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment)
|
||||
system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment)
|
||||
}
|
||||
|
||||
if len(repo.Avatar) > 0 {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// Attachment represent a attachment of issue/comment/release.
|
||||
|
@ -83,6 +84,10 @@ func (err ErrAttachmentNotExist) Error() string {
|
|||
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
|
||||
}
|
||||
|
||||
func (err ErrAttachmentNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// GetAttachmentByID returns attachment by given id
|
||||
func GetAttachmentByID(ctx context.Context, id int64) (*Attachment, error) {
|
||||
attach := &Attachment{}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrRedirectNotExist represents a "RedirectNotExist" kind of error.
|
||||
|
@ -28,6 +29,10 @@ func (err ErrRedirectNotExist) Error() string {
|
|||
return fmt.Sprintf("repository redirect does not exist [uid: %d, name: %s]", err.OwnerID, err.RepoName)
|
||||
}
|
||||
|
||||
func (err ErrRedirectNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// Redirect represents that a repo name should be redirected to another
|
||||
type Redirect struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
|
@ -37,6 +37,10 @@ func (err ErrReleaseAlreadyExist) Error() string {
|
|||
return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrReleaseAlreadyExist) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
|
||||
type ErrReleaseNotExist struct {
|
||||
ID int64
|
||||
|
@ -53,6 +57,10 @@ func (err ErrReleaseNotExist) Error() string {
|
|||
return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
|
||||
}
|
||||
|
||||
func (err ErrReleaseNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// Release represents a release of repository.
|
||||
type Release struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue