mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'master' into fix-6409
This commit is contained in:
+73
-7
@@ -1,4 +1,5 @@
|
||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2019 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.
|
||||
|
||||
@@ -6,6 +7,7 @@ package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -18,6 +20,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/cron"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
@@ -124,6 +128,7 @@ const (
|
||||
reinitMissingRepository
|
||||
syncExternalUsers
|
||||
gitFsck
|
||||
deleteGeneratedRepositoryAvatars
|
||||
)
|
||||
|
||||
// Dashboard show admin panel dashboard
|
||||
@@ -166,6 +171,9 @@ func Dashboard(ctx *context.Context) {
|
||||
case gitFsck:
|
||||
success = ctx.Tr("admin.dashboard.git_fsck_started")
|
||||
go models.GitFsck()
|
||||
case deleteGeneratedRepositoryAvatars:
|
||||
success = ctx.Tr("admin.dashboard.delete_generated_repository_avatars_success")
|
||||
err = models.RemoveRandomAvatars()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -197,6 +205,63 @@ func SendTestMail(ctx *context.Context) {
|
||||
ctx.Redirect(setting.AppSubURL + "/admin/config")
|
||||
}
|
||||
|
||||
func shadownPasswordKV(cfgItem, splitter string) string {
|
||||
fields := strings.Split(cfgItem, splitter)
|
||||
for i := 0; i < len(fields); i++ {
|
||||
if strings.HasPrefix(fields[i], "password=") {
|
||||
fields[i] = "password=******"
|
||||
break
|
||||
}
|
||||
}
|
||||
return strings.Join(fields, splitter)
|
||||
}
|
||||
|
||||
func shadownURL(provider, cfgItem string) string {
|
||||
u, err := url.Parse(cfgItem)
|
||||
if err != nil {
|
||||
log.Error("shodowPassword %v failed: %v", provider, err)
|
||||
return cfgItem
|
||||
}
|
||||
if u.User != nil {
|
||||
atIdx := strings.Index(cfgItem, "@")
|
||||
if atIdx > 0 {
|
||||
colonIdx := strings.LastIndex(cfgItem[:atIdx], ":")
|
||||
if colonIdx > 0 {
|
||||
return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
|
||||
}
|
||||
}
|
||||
}
|
||||
return cfgItem
|
||||
}
|
||||
|
||||
func shadowPassword(provider, cfgItem string) string {
|
||||
switch provider {
|
||||
case "redis":
|
||||
return shadownPasswordKV(cfgItem, ",")
|
||||
case "mysql":
|
||||
//root:@tcp(localhost:3306)/macaron?charset=utf8
|
||||
atIdx := strings.Index(cfgItem, "@")
|
||||
if atIdx > 0 {
|
||||
colonIdx := strings.Index(cfgItem[:atIdx], ":")
|
||||
if colonIdx > 0 {
|
||||
return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
|
||||
}
|
||||
}
|
||||
return cfgItem
|
||||
case "postgres":
|
||||
// user=jiahuachen dbname=macaron port=5432 sslmode=disable
|
||||
if !strings.HasPrefix(cfgItem, "postgres://") {
|
||||
return shadownPasswordKV(cfgItem, " ")
|
||||
}
|
||||
|
||||
// postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
|
||||
// Notice: use shadwonURL
|
||||
}
|
||||
|
||||
// "couchbase"
|
||||
return shadownURL(provider, cfgItem)
|
||||
}
|
||||
|
||||
// Config show admin config page
|
||||
func Config(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.config")
|
||||
@@ -210,7 +275,7 @@ func Config(ctx *context.Context) {
|
||||
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
|
||||
ctx.Data["RunUser"] = setting.RunUser
|
||||
ctx.Data["RunMode"] = strings.Title(macaron.Env)
|
||||
ctx.Data["GitVersion"] = setting.Git.Version
|
||||
ctx.Data["GitVersion"], _ = git.BinVersion()
|
||||
ctx.Data["RepoRootPath"] = setting.RepoRootPath
|
||||
ctx.Data["CustomRootPath"] = setting.CustomPath
|
||||
ctx.Data["StaticRootPath"] = setting.StaticRootPath
|
||||
@@ -220,6 +285,7 @@ func Config(ctx *context.Context) {
|
||||
ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
|
||||
|
||||
ctx.Data["SSH"] = setting.SSH
|
||||
ctx.Data["LFS"] = setting.LFS
|
||||
|
||||
ctx.Data["Service"] = setting.Service
|
||||
ctx.Data["DbCfg"] = models.DbCfg
|
||||
@@ -233,10 +299,14 @@ func Config(ctx *context.Context) {
|
||||
|
||||
ctx.Data["CacheAdapter"] = setting.CacheService.Adapter
|
||||
ctx.Data["CacheInterval"] = setting.CacheService.Interval
|
||||
ctx.Data["CacheConn"] = setting.CacheService.Conn
|
||||
|
||||
ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn)
|
||||
ctx.Data["CacheItemTTL"] = setting.CacheService.TTL
|
||||
|
||||
ctx.Data["SessionConfig"] = setting.SessionConfig
|
||||
sessionCfg := setting.SessionConfig
|
||||
sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig)
|
||||
|
||||
ctx.Data["SessionConfig"] = sessionCfg
|
||||
|
||||
ctx.Data["DisableGravatar"] = setting.DisableGravatar
|
||||
ctx.Data["EnableFederatedAvatar"] = setting.EnableFederatedAvatar
|
||||
@@ -256,10 +326,6 @@ func Config(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Data["EnvVars"] = envVars
|
||||
|
||||
type logger struct {
|
||||
Mode, Config string
|
||||
}
|
||||
ctx.Data["Loggers"] = setting.LogDescriptions
|
||||
ctx.Data["RedirectMacaronLog"] = setting.RedirectMacaronLog
|
||||
ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2019 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
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShadowPassword(t *testing.T) {
|
||||
var kases = []struct {
|
||||
Provider string
|
||||
CfgItem string
|
||||
Result string
|
||||
}{
|
||||
{
|
||||
Provider: "redis",
|
||||
CfgItem: "network=tcp,addr=:6379,password=gitea,db=0,pool_size=100,idle_timeout=180",
|
||||
Result: "network=tcp,addr=:6379,password=******,db=0,pool_size=100,idle_timeout=180",
|
||||
},
|
||||
{
|
||||
Provider: "mysql",
|
||||
CfgItem: "root:@tcp(localhost:3306)/gitea?charset=utf8",
|
||||
Result: "root:******@tcp(localhost:3306)/gitea?charset=utf8",
|
||||
},
|
||||
{
|
||||
Provider: "mysql",
|
||||
CfgItem: "/gitea?charset=utf8",
|
||||
Result: "/gitea?charset=utf8",
|
||||
},
|
||||
{
|
||||
Provider: "mysql",
|
||||
CfgItem: "user:mypassword@/dbname",
|
||||
Result: "user:******@/dbname",
|
||||
},
|
||||
{
|
||||
Provider: "postgres",
|
||||
CfgItem: "user=pqgotest dbname=pqgotest sslmode=verify-full",
|
||||
Result: "user=pqgotest dbname=pqgotest sslmode=verify-full",
|
||||
},
|
||||
{
|
||||
Provider: "postgres",
|
||||
CfgItem: "user=pqgotest password= dbname=pqgotest sslmode=verify-full",
|
||||
Result: "user=pqgotest password=****** dbname=pqgotest sslmode=verify-full",
|
||||
},
|
||||
{
|
||||
Provider: "postgres",
|
||||
CfgItem: "postgres://user:pass@hostname/dbname",
|
||||
Result: "postgres://user:******@hostname/dbname",
|
||||
},
|
||||
{
|
||||
Provider: "couchbase",
|
||||
CfgItem: "http://dev-couchbase.example.com:8091/",
|
||||
Result: "http://dev-couchbase.example.com:8091/",
|
||||
},
|
||||
{
|
||||
Provider: "couchbase",
|
||||
CfgItem: "http://user:the_password@dev-couchbase.example.com:8091/",
|
||||
Result: "http://user:******@dev-couchbase.example.com:8091/",
|
||||
},
|
||||
}
|
||||
|
||||
for _, k := range kases {
|
||||
assert.EqualValues(t, k.Result, shadowPassword(k.Provider, k.CfgItem))
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"github.com/go-xorm/core"
|
||||
"xorm.io/core"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -45,6 +45,11 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
return
|
||||
}
|
||||
|
||||
visibility := api.VisibleTypePublic
|
||||
if form.Visibility != "" {
|
||||
visibility = api.VisibilityModes[form.Visibility]
|
||||
}
|
||||
|
||||
org := &models.User{
|
||||
Name: form.UserName,
|
||||
FullName: form.FullName,
|
||||
@@ -53,7 +58,9 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
Location: form.Location,
|
||||
IsActive: true,
|
||||
Type: models.UserTypeOrganization,
|
||||
Visibility: visibility,
|
||||
}
|
||||
|
||||
if err := models.CreateOrganization(org, u); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
models.IsErrNameReserved(err) ||
|
||||
|
||||
+29
-8
@@ -74,7 +74,8 @@ import (
|
||||
"code.gitea.io/gitea/routers/api/v1/user"
|
||||
|
||||
"github.com/go-macaron/binding"
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/go-macaron/cors"
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func sudo() macaron.Handler {
|
||||
@@ -500,6 +501,12 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Get("/swagger", misc.Swagger) //Render V1 by default
|
||||
}
|
||||
|
||||
var handlers []macaron.Handler
|
||||
if setting.EnableCORS {
|
||||
handlers = append(handlers, cors.CORS(setting.CORSConfig))
|
||||
}
|
||||
handlers = append(handlers, securityHeaders(), context.APIContexter(), sudo())
|
||||
|
||||
m.Group("/v1", func() {
|
||||
// Miscellaneous
|
||||
if setting.API.EnableSwagger {
|
||||
@@ -601,7 +608,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
||||
Delete(reqToken(), reqOwner(), repo.Delete)
|
||||
Delete(reqToken(), reqOwner(), repo.Delete).
|
||||
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)
|
||||
m.Group("/hooks", func() {
|
||||
m.Combo("").Get(repo.ListHooks).
|
||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||
@@ -743,6 +751,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
m.Group("/commits/:ref", func() {
|
||||
// TODO: Add m.Get("") for single commit (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
|
||||
m.Get("/status", repo.GetCombinedCommitStatusByRef)
|
||||
m.Get("/statuses", repo.GetCommitStatusesByRef)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
@@ -754,9 +763,11 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Get("/refs/*", repo.GetGitRefs)
|
||||
m.Get("/trees/:sha", context.RepoRef(), repo.GetTree)
|
||||
m.Get("/blobs/:sha", context.RepoRef(), repo.GetBlob)
|
||||
m.Get("/tags/:sha", context.RepoRef(), repo.GetTag)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
m.Group("/contents", func() {
|
||||
m.Get("/*", repo.GetFileContents)
|
||||
m.Get("", repo.GetContentsList)
|
||||
m.Get("/*", repo.GetContents)
|
||||
m.Group("/*", func() {
|
||||
m.Post("", bind(api.CreateFileOptions{}), repo.CreateFile)
|
||||
m.Put("", bind(api.UpdateFileOptions{}), repo.UpdateFile)
|
||||
@@ -787,14 +798,14 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
Delete(reqToken(), reqOrgMembership(), org.ConcealMember)
|
||||
})
|
||||
m.Combo("/teams", reqToken(), reqOrgMembership()).Get(org.ListTeams).
|
||||
Post(bind(api.CreateTeamOption{}), org.CreateTeam)
|
||||
Post(reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam)
|
||||
m.Group("/hooks", func() {
|
||||
m.Combo("").Get(org.ListHooks).
|
||||
Post(bind(api.CreateHookOption{}), org.CreateHook)
|
||||
m.Combo("/:id").Get(org.GetHook).
|
||||
Patch(reqOrgOwnership(), bind(api.EditHookOption{}), org.EditHook).
|
||||
Delete(reqOrgOwnership(), org.DeleteHook)
|
||||
}, reqToken(), reqOrgMembership())
|
||||
Patch(bind(api.EditHookOption{}), org.EditHook).
|
||||
Delete(org.DeleteHook)
|
||||
}, reqToken(), reqOrgOwnership())
|
||||
}, orgAssignment(true))
|
||||
m.Group("/teams/:teamid", func() {
|
||||
m.Combo("").Get(org.GetTeam).
|
||||
@@ -841,5 +852,15 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/topics", func() {
|
||||
m.Get("/search", repo.TopicSearch)
|
||||
})
|
||||
}, context.APIContexter(), sudo())
|
||||
}, handlers...)
|
||||
}
|
||||
|
||||
func securityHeaders() macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ctx.Resp.Before(func(w macaron.ResponseWriter) {
|
||||
// CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers
|
||||
// http://stackoverflow.com/a/3146618/244009
|
||||
w.Header().Set("x-content-type-options", "nosniff")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package convert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@@ -26,7 +27,7 @@ func ToEmail(email *models.EmailAddress) *api.Email {
|
||||
}
|
||||
}
|
||||
|
||||
// ToBranch convert a commit and branch to an api.Branch
|
||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit) *api.Branch {
|
||||
return &api.Branch{
|
||||
Name: b.Name,
|
||||
@@ -34,23 +35,18 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit) *api.Branch
|
||||
}
|
||||
}
|
||||
|
||||
// ToTag convert a tag to an api.Tag
|
||||
// ToTag convert a git.Tag to an api.Tag
|
||||
func ToTag(repo *models.Repository, t *git.Tag) *api.Tag {
|
||||
return &api.Tag{
|
||||
Name: t.Name,
|
||||
Commit: struct {
|
||||
SHA string `json:"sha"`
|
||||
URL string `json:"url"`
|
||||
}{
|
||||
SHA: t.ID.String(),
|
||||
URL: util.URLJoin(repo.Link(), "commit", t.ID.String()),
|
||||
},
|
||||
ZipballURL: util.URLJoin(repo.Link(), "archive", t.Name+".zip"),
|
||||
TarballURL: util.URLJoin(repo.Link(), "archive", t.Name+".tar.gz"),
|
||||
Name: t.Name,
|
||||
ID: t.ID.String(),
|
||||
Commit: ToCommitMeta(repo, t),
|
||||
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
|
||||
TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCommit convert a commit to api.PayloadCommit
|
||||
// ToCommit convert a git.Commit to api.PayloadCommit
|
||||
func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
|
||||
authorUsername := ""
|
||||
if author, err := models.GetUserByEmail(c.Author.Email); err == nil {
|
||||
@@ -66,17 +62,10 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
}
|
||||
|
||||
verif := models.ParseCommitWithSignature(c)
|
||||
var signature, payload string
|
||||
if c.Signature != nil {
|
||||
signature = c.Signature.Signature
|
||||
payload = c.Signature.Payload
|
||||
}
|
||||
|
||||
return &api.PayloadCommit{
|
||||
ID: c.ID.String(),
|
||||
Message: c.Message(),
|
||||
URL: util.URLJoin(repo.Link(), "commit", c.ID.String()),
|
||||
URL: util.URLJoin(repo.HTMLURL(), "commit", c.ID.String()),
|
||||
Author: &api.PayloadUser{
|
||||
Name: c.Author.Name,
|
||||
Email: c.Author.Email,
|
||||
@@ -87,13 +76,24 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
|
||||
Email: c.Committer.Email,
|
||||
UserName: committerUsername,
|
||||
},
|
||||
Timestamp: c.Author.When,
|
||||
Verification: &api.PayloadCommitVerification{
|
||||
Verified: verif.Verified,
|
||||
Reason: verif.Reason,
|
||||
Signature: signature,
|
||||
Payload: payload,
|
||||
},
|
||||
Timestamp: c.Author.When,
|
||||
Verification: ToVerification(c),
|
||||
}
|
||||
}
|
||||
|
||||
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
|
||||
func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
|
||||
verif := models.ParseCommitWithSignature(c)
|
||||
var signature, payload string
|
||||
if c.Signature != nil {
|
||||
signature = c.Signature.Signature
|
||||
payload = c.Signature.Payload
|
||||
}
|
||||
return &api.PayloadCommitVerification{
|
||||
Verified: verif.Verified,
|
||||
Reason: verif.Reason,
|
||||
Signature: signature,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,7 @@ func ToOrganization(org *models.User) *api.Organization {
|
||||
Description: org.Description,
|
||||
Website: org.Website,
|
||||
Location: org.Location,
|
||||
Visibility: org.Visibility.String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,9 +237,53 @@ func ToUser(user *models.User, signed, admin bool) *api.User {
|
||||
AvatarURL: user.AvatarLink(),
|
||||
FullName: markup.Sanitize(user.FullName),
|
||||
IsAdmin: user.IsAdmin,
|
||||
LastLogin: user.LastLoginUnix.AsTime(),
|
||||
Created: user.CreatedUnix.AsTime(),
|
||||
}
|
||||
if signed && (!user.KeepEmailPrivate || admin) {
|
||||
result.Email = user.Email
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToAnnotatedTag convert git.Tag to api.AnnotatedTag
|
||||
func ToAnnotatedTag(repo *models.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag {
|
||||
return &api.AnnotatedTag{
|
||||
Tag: t.Name,
|
||||
SHA: t.ID.String(),
|
||||
Object: ToAnnotatedTagObject(repo, c),
|
||||
Message: t.Message,
|
||||
URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()),
|
||||
Tagger: ToCommitUser(t.Tagger),
|
||||
Verification: ToVerification(c),
|
||||
}
|
||||
}
|
||||
|
||||
// ToAnnotatedTagObject convert a git.Commit to an api.AnnotatedTagObject
|
||||
func ToAnnotatedTagObject(repo *models.Repository, commit *git.Commit) *api.AnnotatedTagObject {
|
||||
return &api.AnnotatedTagObject{
|
||||
SHA: commit.ID.String(),
|
||||
Type: string(git.ObjectCommit),
|
||||
URL: util.URLJoin(repo.APIURL(), "git/commits", commit.ID.String()),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCommitUser convert a git.Signature to an api.CommitUser
|
||||
func ToCommitUser(sig *git.Signature) *api.CommitUser {
|
||||
return &api.CommitUser{
|
||||
Identity: api.Identity{
|
||||
Name: sig.Name,
|
||||
Email: sig.Email,
|
||||
},
|
||||
Date: sig.When.UTC().Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCommitMeta convert a git.Tag to an api.CommitMeta
|
||||
func ToCommitMeta(repo *models.Repository, tag *git.Tag) *api.CommitMeta {
|
||||
return &api.CommitMeta{
|
||||
SHA: tag.Object.String(),
|
||||
// TODO: Add the /commits API endpoint and use it here (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
|
||||
URL: util.URLJoin(repo.APIURL(), "git/commits", tag.ID.String()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
@@ -42,7 +43,7 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
||||
}
|
||||
|
||||
if len(form.Text) == 0 {
|
||||
ctx.Write([]byte(""))
|
||||
_, _ = ctx.Write([]byte(""))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -63,12 +64,24 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
||||
meta = ctx.Repo.Repository.ComposeMetas()
|
||||
}
|
||||
if form.Wiki {
|
||||
ctx.Write([]byte(markdown.RenderWiki(md, urlPrefix, meta)))
|
||||
_, err := ctx.Write([]byte(markdown.RenderWiki(md, urlPrefix, meta)))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Write(markdown.Render(md, urlPrefix, meta))
|
||||
_, err := ctx.Write(markdown.Render(md, urlPrefix, meta))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
ctx.Write(markdown.RenderRaw([]byte(form.Text), "", false))
|
||||
_, err := ctx.Write(markdown.RenderRaw([]byte(form.Text), "", false))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,5 +111,9 @@ func MarkdownRaw(ctx *context.APIContext) {
|
||||
ctx.Error(422, "", err)
|
||||
return
|
||||
}
|
||||
ctx.Write(markdown.RenderRaw(body, "", false))
|
||||
_, err = ctx.Write(markdown.RenderRaw(body, "", false))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,11 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
return
|
||||
}
|
||||
|
||||
visibility := api.VisibleTypePublic
|
||||
if form.Visibility != "" {
|
||||
visibility = api.VisibilityModes[form.Visibility]
|
||||
}
|
||||
|
||||
org := &models.User{
|
||||
Name: form.UserName,
|
||||
FullName: form.FullName,
|
||||
@@ -98,6 +103,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
Location: form.Location,
|
||||
IsActive: true,
|
||||
Type: models.UserTypeOrganization,
|
||||
Visibility: visibility,
|
||||
}
|
||||
if err := models.CreateOrganization(org, ctx.User); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
@@ -153,6 +159,7 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/EditOrgOption"
|
||||
// responses:
|
||||
@@ -163,8 +170,11 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) {
|
||||
org.Description = form.Description
|
||||
org.Website = form.Website
|
||||
org.Location = form.Location
|
||||
if err := models.UpdateUserCols(org, "full_name", "description", "website", "location"); err != nil {
|
||||
ctx.Error(500, "UpdateUser", err)
|
||||
if form.Visibility != "" {
|
||||
org.Visibility = api.VisibilityModes[form.Visibility]
|
||||
}
|
||||
if err := models.UpdateUserCols(org, "full_name", "description", "website", "location", "visibility"); err != nil {
|
||||
ctx.Error(500, "EditOrganization", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+58
-14
@@ -181,7 +181,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "'content' must be base64 encoded\n\n 'author' and 'committer' are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)\n\n If 'branch' is not given, default branch will be used\n\n 'sha' is the SHA for the file that already exists\n\n 'new_branch' (optional) will make a new branch from 'branch' before creating the file"
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/CreateFileOptions"
|
||||
// responses:
|
||||
@@ -204,6 +204,11 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
|
||||
Email: apiOpts.Author.Email,
|
||||
},
|
||||
}
|
||||
|
||||
if opts.Message == "" {
|
||||
opts.Message = ctx.Tr("repo.editor.add", opts.TreePath)
|
||||
}
|
||||
|
||||
if fileResponse, err := createOrUpdateFile(ctx, opts); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateFile", err)
|
||||
} else {
|
||||
@@ -238,7 +243,7 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "'content' must be base64 encoded\n\n 'sha' is the SHA for the file that already exists\n\n 'author' and 'committer' are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)\n\n If 'branch' is not given, default branch will be used\n\n 'new_branch' (optional) will make a new branch from 'branch' before updating the file"
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UpdateFileOptions"
|
||||
// responses:
|
||||
@@ -264,6 +269,10 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) {
|
||||
},
|
||||
}
|
||||
|
||||
if opts.Message == "" {
|
||||
opts.Message = ctx.Tr("repo.editor.update", opts.TreePath)
|
||||
}
|
||||
|
||||
if fileResponse, err := createOrUpdateFile(ctx, opts); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateFile", err)
|
||||
} else {
|
||||
@@ -316,7 +325,7 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "'sha' is the SHA for the file to be deleted\n\n 'author' and 'committer' are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)\n\n If 'branch' is not given, default branch will be used\n\n 'new_branch' (optional) will make a new branch from 'branch' before deleting the file"
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/DeleteFileOptions"
|
||||
// responses:
|
||||
@@ -346,6 +355,10 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
||||
},
|
||||
}
|
||||
|
||||
if opts.Message == "" {
|
||||
opts.Message = ctx.Tr("repo.editor.delete", opts.TreePath)
|
||||
}
|
||||
|
||||
if fileResponse, err := repofiles.DeleteRepoFile(ctx.Repo.Repository, ctx.User, opts); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteFile", err)
|
||||
} else {
|
||||
@@ -353,11 +366,11 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetFileContents Get the contents of a fle in a repository
|
||||
func GetFileContents(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetFileContents
|
||||
// GetContents Get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
|
||||
func GetContents(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetContents
|
||||
// ---
|
||||
// summary: Gets the contents of a file or directory in a repository
|
||||
// summary: Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
@@ -373,20 +386,20 @@ func GetFileContents(ctx *context.APIContext) {
|
||||
// required: true
|
||||
// - name: filepath
|
||||
// in: path
|
||||
// description: path of the file to delete
|
||||
// description: path of the dir, file, symlink or submodule in the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: ref
|
||||
// in: query
|
||||
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
|
||||
// required: false
|
||||
// type: string
|
||||
// required: false
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/FileContentResponse"
|
||||
// "$ref": "#/responses/ContentsResponse"
|
||||
|
||||
if !CanReadFiles(ctx.Repo) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFileContents", models.ErrUserDoesNotHaveAccessToRepo{
|
||||
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", models.ErrUserDoesNotHaveAccessToRepo{
|
||||
UserID: ctx.User.ID,
|
||||
RepoName: ctx.Repo.Repository.LowerName,
|
||||
})
|
||||
@@ -396,9 +409,40 @@ func GetFileContents(ctx *context.APIContext) {
|
||||
treePath := ctx.Params("*")
|
||||
ref := ctx.QueryTrim("ref")
|
||||
|
||||
if fileContents, err := repofiles.GetFileContents(ctx.Repo.Repository, treePath, ref); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFileContents", err)
|
||||
if fileList, err := repofiles.GetContentsOrList(ctx.Repo.Repository, treePath, ref); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", err)
|
||||
} else {
|
||||
ctx.JSON(http.StatusOK, fileContents)
|
||||
ctx.JSON(http.StatusOK, fileList)
|
||||
}
|
||||
}
|
||||
|
||||
// GetContentsList Get the metadata of all the entries of the root dir
|
||||
func GetContentsList(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/contents repository repoGetContentsList
|
||||
// ---
|
||||
// summary: Gets the metadata of all the entries of the root dir
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: ref
|
||||
// in: query
|
||||
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
|
||||
// type: string
|
||||
// required: false
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ContentsListResponse"
|
||||
|
||||
// same as GetContents(), this function is here because swagger fails if path is empty in GetContents() interface
|
||||
GetContents(ctx)
|
||||
}
|
||||
|
||||
@@ -100,8 +100,7 @@ func getGitRefsInternal(ctx *context.APIContext, filter string) {
|
||||
Object: &api.GitObject{
|
||||
SHA: refs[i].Object.String(),
|
||||
Type: refs[i].Type,
|
||||
// TODO: Add commit/tag info URL
|
||||
//URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
|
||||
URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,9 +125,10 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) {
|
||||
// "201":
|
||||
// "$ref": "#/responses/Label"
|
||||
label := &models.Label{
|
||||
Name: form.Name,
|
||||
Color: form.Color,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Name: form.Name,
|
||||
Color: form.Color,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Description: form.Description,
|
||||
}
|
||||
if err := models.NewLabel(label); err != nil {
|
||||
ctx.Error(500, "NewLabel", err)
|
||||
@@ -185,6 +186,9 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) {
|
||||
if form.Color != nil {
|
||||
label.Color = *form.Color
|
||||
}
|
||||
if form.Description != nil {
|
||||
label.Description = *form.Description
|
||||
}
|
||||
if err := models.UpdateLabel(label); err != nil {
|
||||
ctx.ServerError("UpdateLabel", err)
|
||||
return
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ListMilestones list all the opened milestones for a repository
|
||||
// ListMilestones list milestones for a repository
|
||||
func ListMilestones(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/milestones issue issueGetMilestonesList
|
||||
// ---
|
||||
@@ -32,10 +32,14 @@ func ListMilestones(ctx *context.APIContext) {
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: state
|
||||
// in: query
|
||||
// description: Milestone state, Recognised values are open, closed and all. Defaults to "open"
|
||||
// type: string
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/MilestoneList"
|
||||
milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID)
|
||||
milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")))
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetMilestonesByRepoID", err)
|
||||
return
|
||||
|
||||
+26
-11
@@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/pull"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
@@ -188,7 +189,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
)
|
||||
|
||||
// Get repo/branch information
|
||||
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
|
||||
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -240,7 +241,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
milestoneID = milestone.ID
|
||||
}
|
||||
|
||||
patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch)
|
||||
patch, err := headGitRepo.GetPatch(compareInfo.MergeBase, headBranch)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetPatch", err)
|
||||
return
|
||||
@@ -251,9 +252,15 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
|
||||
}
|
||||
|
||||
maxIndex, err := models.GetMaxIndexOfIssue(repo.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPatch", err)
|
||||
return
|
||||
}
|
||||
|
||||
prIssue := &models.Issue{
|
||||
RepoID: repo.ID,
|
||||
Index: repo.NextIssueIndex(),
|
||||
Index: maxIndex + 1,
|
||||
Title: form.Title,
|
||||
PosterID: ctx.User.ID,
|
||||
Poster: ctx.User,
|
||||
@@ -271,7 +278,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
BaseBranch: baseBranch,
|
||||
HeadRepo: headRepo,
|
||||
BaseRepo: repo,
|
||||
MergeBase: prInfo.MergeBase,
|
||||
MergeBase: compareInfo.MergeBase,
|
||||
Type: models.PullRequestGitea,
|
||||
}
|
||||
|
||||
@@ -347,7 +354,11 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
||||
return
|
||||
}
|
||||
|
||||
pr.LoadIssue()
|
||||
err = pr.LoadIssue()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
issue := pr.Issue
|
||||
issue.Repo = ctx.Repo.Repository
|
||||
|
||||
@@ -541,7 +552,11 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
return
|
||||
}
|
||||
|
||||
pr.LoadIssue()
|
||||
err = pr.LoadIssue()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
pr.Issue.Repo = ctx.Repo.Repository
|
||||
|
||||
if ctx.IsSigned {
|
||||
@@ -581,7 +596,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
message += "\n\n" + form.MergeMessageField
|
||||
}
|
||||
|
||||
if err := pr.Merge(ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
|
||||
if err := pull.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Status(405)
|
||||
return
|
||||
@@ -594,7 +609,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
ctx.Status(200)
|
||||
}
|
||||
|
||||
func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
|
||||
func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
|
||||
baseRepo := ctx.Repo.Repository
|
||||
|
||||
// Get compared branches information
|
||||
@@ -706,11 +721,11 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
|
||||
compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetPullRequestInfo", err)
|
||||
ctx.Error(500, "GetCompareInfo", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch
|
||||
return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/upload"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
@@ -177,20 +176,9 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// Check if the filetype is allowed by the settings
|
||||
fileType := http.DetectContentType(buf)
|
||||
|
||||
allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",")
|
||||
allowed := false
|
||||
for _, t := range allowedTypes {
|
||||
t := strings.Trim(t, " ")
|
||||
if t == "*/*" || t == fileType {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
ctx.Error(400, "DetectContentType", errors.New("File type is not allowed"))
|
||||
err = upload.VerifyAllowedContentType(buf, strings.Split(setting.AttachmentAllowedTypes, ","))
|
||||
if err != nil {
|
||||
ctx.Error(400, "DetectContentType", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+282
-36
@@ -56,6 +56,15 @@ func Search(ctx *context.APIContext) {
|
||||
// description: search only for repos that the user with the given id owns or contributes to
|
||||
// type: integer
|
||||
// format: int64
|
||||
// - name: starredBy
|
||||
// in: query
|
||||
// description: search only for repos that the user with the given id has starred
|
||||
// type: integer
|
||||
// format: int64
|
||||
// - name: private
|
||||
// in: query
|
||||
// description: include private repositories this user has access to (defaults to true)
|
||||
// type: boolean
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
@@ -96,6 +105,10 @@ func Search(ctx *context.APIContext) {
|
||||
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
|
||||
TopicOnly: ctx.QueryBool("topic"),
|
||||
Collaborate: util.OptionalBoolNone,
|
||||
Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
|
||||
UserIsAdmin: ctx.IsUserSiteAdmin(),
|
||||
UserID: ctx.Data["SignedUserID"].(int64),
|
||||
StarredByID: ctx.QueryInt64("starredBy"),
|
||||
}
|
||||
|
||||
if ctx.QueryBool("exclusive") {
|
||||
@@ -140,42 +153,6 @@ func Search(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
var err error
|
||||
if opts.OwnerID > 0 {
|
||||
var repoOwner *models.User
|
||||
if ctx.User != nil && ctx.User.ID == opts.OwnerID {
|
||||
repoOwner = ctx.User
|
||||
} else {
|
||||
repoOwner, err = models.GetUserByID(opts.OwnerID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, api.SearchError{
|
||||
OK: false,
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if repoOwner.IsOrganization() {
|
||||
opts.Collaborate = util.OptionalBoolFalse
|
||||
}
|
||||
|
||||
// Check visibility.
|
||||
if ctx.IsSigned {
|
||||
if ctx.User.ID == repoOwner.ID {
|
||||
opts.Private = true
|
||||
} else if repoOwner.IsOrganization() {
|
||||
opts.Private, err = repoOwner.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, api.SearchError{
|
||||
OK: false,
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repos, count, err := models.SearchRepositoryByName(opts)
|
||||
if err != nil {
|
||||
ctx.JSON(500, api.SearchError{
|
||||
@@ -263,6 +240,10 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/Repository"
|
||||
// "409":
|
||||
// description: The repository with the same name already exists.
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
if ctx.User.IsOrganization() {
|
||||
// Shouldn't reach this condition, but just in case.
|
||||
ctx.Error(422, "", "not allowed creating repository for organization")
|
||||
@@ -523,6 +504,271 @@ func GetByID(ctx *context.APIContext) {
|
||||
ctx.JSON(200, repo.APIFormat(perm.AccessMode))
|
||||
}
|
||||
|
||||
// Edit edit repository properties
|
||||
func Edit(ctx *context.APIContext, opts api.EditRepoOption) {
|
||||
// swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit
|
||||
// ---
|
||||
// summary: Edit a repository's properties. Only fields that are set will be changed.
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo to edit
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo to edit
|
||||
// type: string
|
||||
// required: true
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "Properties of a repo that you can edit"
|
||||
// schema:
|
||||
// "$ref": "#/definitions/EditRepoOption"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Repository"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
if err := updateBasicProperties(ctx, opts); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := updateRepoUnits(ctx, opts); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Archived != nil {
|
||||
if err := updateRepoArchivedState(ctx, opts); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
|
||||
}
|
||||
|
||||
// updateBasicProperties updates the basic properties of a repo: Name, Description, Website and Visibility
|
||||
func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
owner := ctx.Repo.Owner
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
oldRepoName := repo.Name
|
||||
newRepoName := repo.Name
|
||||
if opts.Name != nil {
|
||||
newRepoName = *opts.Name
|
||||
}
|
||||
// Check if repository name has been changed and not just a case change
|
||||
if repo.LowerName != strings.ToLower(newRepoName) {
|
||||
if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil {
|
||||
switch {
|
||||
case models.IsErrRepoAlreadyExist(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
|
||||
case models.IsErrNameReserved(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err)
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(models.ErrNamePatternNotAllowed).Pattern), err)
|
||||
default:
|
||||
ctx.Error(http.StatusUnprocessableEntity, "ChangeRepositoryName", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err := models.NewRepoRedirect(ctx.Repo.Owner.ID, repo.ID, repo.Name, newRepoName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "NewRepoRedirect", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil {
|
||||
log.Error("RenameRepoAction: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError, "RenameRepoActions", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
|
||||
}
|
||||
// Update the name in the repo object for the response
|
||||
repo.Name = newRepoName
|
||||
repo.LowerName = strings.ToLower(newRepoName)
|
||||
|
||||
if opts.Description != nil {
|
||||
repo.Description = *opts.Description
|
||||
}
|
||||
|
||||
if opts.Website != nil {
|
||||
repo.Website = *opts.Website
|
||||
}
|
||||
|
||||
visibilityChanged := false
|
||||
if opts.Private != nil {
|
||||
// Visibility of forked repository is forced sync with base repository.
|
||||
if repo.IsFork {
|
||||
*opts.Private = repo.BaseRepo.IsPrivate
|
||||
}
|
||||
|
||||
visibilityChanged = repo.IsPrivate != *opts.Private
|
||||
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
|
||||
if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.User.IsAdmin {
|
||||
err := fmt.Errorf("cannot change private repository to public")
|
||||
ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err)
|
||||
return err
|
||||
}
|
||||
|
||||
repo.IsPrivate = *opts.Private
|
||||
}
|
||||
|
||||
if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Repository basic settings updated: %s/%s", owner.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings
|
||||
func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
owner := ctx.Repo.Owner
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
var units []models.RepoUnit
|
||||
|
||||
for _, tp := range models.MustRepoUnits {
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: tp,
|
||||
Config: new(models.UnitConfig),
|
||||
})
|
||||
}
|
||||
|
||||
if opts.HasIssues != nil {
|
||||
if *opts.HasIssues {
|
||||
// We don't currently allow setting individual issue settings through the API,
|
||||
// only can enable/disable issues, so when enabling issues,
|
||||
// we either get the existing config which means it was already enabled,
|
||||
// or create a new config since it doesn't exist.
|
||||
unit, err := repo.GetUnit(models.UnitTypeIssues)
|
||||
var config *models.IssuesConfig
|
||||
if err != nil {
|
||||
// Unit type doesn't exist so we make a new config file with default values
|
||||
config = &models.IssuesConfig{
|
||||
EnableTimetracker: true,
|
||||
AllowOnlyContributorsToTrackTime: true,
|
||||
EnableDependencies: true,
|
||||
}
|
||||
} else {
|
||||
config = unit.IssuesConfig()
|
||||
}
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: models.UnitTypeIssues,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if opts.HasWiki != nil {
|
||||
if *opts.HasWiki {
|
||||
// We don't currently allow setting individual wiki settings through the API,
|
||||
// only can enable/disable the wiki, so when enabling the wiki,
|
||||
// we either get the existing config which means it was already enabled,
|
||||
// or create a new config since it doesn't exist.
|
||||
config := &models.UnitConfig{}
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: models.UnitTypeWiki,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if opts.HasPullRequests != nil {
|
||||
if *opts.HasPullRequests {
|
||||
// We do allow setting individual PR settings through the API, so
|
||||
// we get the config settings and then set them
|
||||
// if those settings were provided in the opts.
|
||||
unit, err := repo.GetUnit(models.UnitTypePullRequests)
|
||||
var config *models.PullRequestsConfig
|
||||
if err != nil {
|
||||
// Unit type doesn't exist so we make a new config file with default values
|
||||
config = &models.PullRequestsConfig{
|
||||
IgnoreWhitespaceConflicts: false,
|
||||
AllowMerge: true,
|
||||
AllowRebase: true,
|
||||
AllowRebaseMerge: true,
|
||||
AllowSquash: true,
|
||||
}
|
||||
} else {
|
||||
config = unit.PullRequestsConfig()
|
||||
}
|
||||
|
||||
if opts.IgnoreWhitespaceConflicts != nil {
|
||||
config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts
|
||||
}
|
||||
if opts.AllowMerge != nil {
|
||||
config.AllowMerge = *opts.AllowMerge
|
||||
}
|
||||
if opts.AllowRebase != nil {
|
||||
config.AllowRebase = *opts.AllowRebase
|
||||
}
|
||||
if opts.AllowRebaseMerge != nil {
|
||||
config.AllowRebaseMerge = *opts.AllowRebaseMerge
|
||||
}
|
||||
if opts.AllowSquash != nil {
|
||||
config.AllowSquash = *opts.AllowSquash
|
||||
}
|
||||
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: models.UnitTypePullRequests,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := models.UpdateRepositoryUnits(repo, units); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Repository advanced settings updated: %s/%s", owner.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRepoArchivedState updates repo's archive state
|
||||
func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
repo := ctx.Repo.Repository
|
||||
// archive / un-archive
|
||||
if opts.Archived != nil {
|
||||
if repo.IsMirror {
|
||||
err := fmt.Errorf("repo is a mirror, cannot archive/un-archive")
|
||||
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
|
||||
return err
|
||||
}
|
||||
if *opts.Archived {
|
||||
if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
|
||||
log.Error("Tried to archive a repo: %s", err)
|
||||
ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
|
||||
return err
|
||||
}
|
||||
log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
} else {
|
||||
if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
|
||||
log.Error("Tried to un-archive a repo: %s", err)
|
||||
ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
|
||||
return err
|
||||
}
|
||||
log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete one repository
|
||||
func Delete(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2019 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 repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepoEdit(t *testing.T) {
|
||||
models.PrepareTestEnv(t)
|
||||
|
||||
ctx := test.MockContext(t, "user2/repo1")
|
||||
test.LoadRepo(t, ctx, 1)
|
||||
test.LoadUser(t, ctx, 2)
|
||||
ctx.Repo.Owner = ctx.User
|
||||
description := "new description"
|
||||
website := "http://wwww.newwebsite.com"
|
||||
private := true
|
||||
hasIssues := false
|
||||
hasWiki := false
|
||||
defaultBranch := "master"
|
||||
hasPullRequests := true
|
||||
ignoreWhitespaceConflicts := true
|
||||
allowMerge := false
|
||||
allowRebase := false
|
||||
allowRebaseMerge := false
|
||||
allowSquashMerge := false
|
||||
archived := true
|
||||
opts := api.EditRepoOption{
|
||||
Name: &ctx.Repo.Repository.Name,
|
||||
Description: &description,
|
||||
Website: &website,
|
||||
Private: &private,
|
||||
HasIssues: &hasIssues,
|
||||
HasWiki: &hasWiki,
|
||||
DefaultBranch: &defaultBranch,
|
||||
HasPullRequests: &hasPullRequests,
|
||||
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
||||
AllowMerge: &allowMerge,
|
||||
AllowRebase: &allowRebase,
|
||||
AllowRebaseMerge: &allowRebaseMerge,
|
||||
AllowSquash: &allowSquashMerge,
|
||||
Archived: &archived,
|
||||
}
|
||||
|
||||
Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
|
||||
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
models.AssertExistsAndLoadBean(t, &models.Repository{
|
||||
ID: 1,
|
||||
}, models.Cond("name = ? AND is_archived = 1", *opts.Name))
|
||||
}
|
||||
|
||||
func TestRepoEditNameChange(t *testing.T) {
|
||||
models.PrepareTestEnv(t)
|
||||
|
||||
ctx := test.MockContext(t, "user2/repo1")
|
||||
test.LoadRepo(t, ctx, 1)
|
||||
test.LoadUser(t, ctx, 2)
|
||||
ctx.Repo.Owner = ctx.User
|
||||
name := "newname"
|
||||
opts := api.EditRepoOption{
|
||||
Name: &name,
|
||||
}
|
||||
|
||||
Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
|
||||
models.AssertExistsAndLoadBean(t, &models.Repository{
|
||||
ID: 1,
|
||||
}, models.Cond("name = ?", opts.Name))
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
@@ -57,17 +58,12 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) {
|
||||
Description: form.Description,
|
||||
Context: form.Context,
|
||||
}
|
||||
if err := models.NewCommitStatus(ctx.Repo.Repository, ctx.User, sha, status); err != nil {
|
||||
ctx.Error(500, "NewCommitStatus", err)
|
||||
if err := repofiles.CreateCommitStatus(ctx.Repo.Repository, ctx.User, sha, status); err != nil {
|
||||
ctx.Error(500, "CreateCommitStatus", err)
|
||||
return
|
||||
}
|
||||
|
||||
newStatus, err := models.GetCommitStatus(ctx.Repo.Repository, sha, status)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetCommitStatus", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(201, newStatus.APIFormat())
|
||||
ctx.JSON(201, status.APIFormat())
|
||||
}
|
||||
|
||||
// GetCommitStatuses returns all statuses for any given commit hash
|
||||
@@ -140,6 +136,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
|
||||
statuses, err := models.GetCommitStatuses(repo, sha, page)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetCommitStatuses", fmt.Errorf("GetCommitStatuses[%s, %s, %d]: %v", repo.FullName(), sha, page, err))
|
||||
return
|
||||
}
|
||||
|
||||
apiStatuses := make([]*api.Status, 0, len(statuses))
|
||||
|
||||
@@ -7,6 +7,7 @@ package repo
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||
"net/http"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
@@ -45,3 +46,47 @@ func ListTags(ctx *context.APIContext) {
|
||||
|
||||
ctx.JSON(200, &apiTags)
|
||||
}
|
||||
|
||||
// GetTag get the tag of a repository.
|
||||
func GetTag(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/git/tags/{sha} repository GetTag
|
||||
// ---
|
||||
// summary: Gets the tag of a repository.
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: sha
|
||||
// in: path
|
||||
// description: sha of the tag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/AnnotatedTag"
|
||||
|
||||
sha := ctx.Params("sha")
|
||||
if len(sha) == 0 {
|
||||
ctx.Error(http.StatusBadRequest, "", "SHA not provided")
|
||||
return
|
||||
}
|
||||
|
||||
if tag, err := ctx.Repo.GitRepo.GetAnnotatedTag(sha); err != nil {
|
||||
ctx.Error(http.StatusBadRequest, "GetTag", err)
|
||||
} else {
|
||||
commit, err := tag.Commit()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusBadRequest, "GetTag", err)
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx.Repo.Repository, tag, commit))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,8 @@ type swaggerParameterBodies struct {
|
||||
// in:body
|
||||
CreateRepoOption api.CreateRepoOption
|
||||
// in:body
|
||||
EditRepoOption api.EditRepoOption
|
||||
// in:body
|
||||
CreateForkOption api.CreateForkOption
|
||||
|
||||
// in:body
|
||||
|
||||
@@ -38,11 +38,25 @@ type swaggerResponseBranchList struct {
|
||||
|
||||
// TagList
|
||||
// swagger:response TagList
|
||||
type swaggerReponseTagList struct {
|
||||
type swaggerResponseTagList struct {
|
||||
// in:body
|
||||
Body []api.Tag `json:"body"`
|
||||
}
|
||||
|
||||
// Tag
|
||||
// swagger:response Tag
|
||||
type swaggerResponseTag struct {
|
||||
// in:body
|
||||
Body api.Tag `json:"body"`
|
||||
}
|
||||
|
||||
// AnnotatedTag
|
||||
// swagger:response AnnotatedTag
|
||||
type swaggerResponseAnnotatedTag struct {
|
||||
// in:body
|
||||
Body api.AnnotatedTag `json:"body"`
|
||||
}
|
||||
|
||||
// Reference
|
||||
// swagger:response Reference
|
||||
type swaggerResponseReference struct {
|
||||
@@ -183,11 +197,18 @@ type swaggerFileResponse struct {
|
||||
Body api.FileResponse `json:"body"`
|
||||
}
|
||||
|
||||
// FileContentResponse
|
||||
// swagger:response FileContentResponse
|
||||
type swaggerFileContentResponse struct {
|
||||
// ContentsResponse
|
||||
// swagger:response ContentsResponse
|
||||
type swaggerContentsResponse struct {
|
||||
//in: body
|
||||
Body api.FileContentResponse `json:"body"`
|
||||
Body api.ContentsResponse `json:"body"`
|
||||
}
|
||||
|
||||
// ContentsListResponse
|
||||
// swagger:response ContentsListResponse
|
||||
type swaggerContentsListResponse struct {
|
||||
// in:body
|
||||
Body []api.ContentsResponse `json:"body"`
|
||||
}
|
||||
|
||||
// FileDeleteResponse
|
||||
|
||||
@@ -36,6 +36,9 @@ func ListAccessTokens(ctx *context.APIContext) {
|
||||
|
||||
apiTokens := make([]*api.AccessToken, len(tokens))
|
||||
for i := range tokens {
|
||||
if tokens[i].Name == "drone" {
|
||||
tokens[i].Name = "drone-legacy-use-oauth2-instead"
|
||||
}
|
||||
apiTokens[i] = &api.AccessToken{
|
||||
ID: tokens[i].ID,
|
||||
Name: tokens[i].Name,
|
||||
@@ -76,14 +79,18 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption
|
||||
UID: ctx.User.ID,
|
||||
Name: form.Name,
|
||||
}
|
||||
if t.Name == "drone" {
|
||||
t.Name = "drone-legacy-use-oauth2-instead"
|
||||
}
|
||||
if err := models.NewAccessToken(t); err != nil {
|
||||
ctx.Error(500, "NewAccessToken", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(201, &api.AccessToken{
|
||||
Name: t.Name,
|
||||
Token: t.Token,
|
||||
ID: t.ID,
|
||||
Name: t.Name,
|
||||
Token: t.Token,
|
||||
ID: t.ID,
|
||||
TokenLastEight: t.TokenLastEight,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,9 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||
)
|
||||
|
||||
func composePublicGPGKeysAPILink() string {
|
||||
return setting.AppURL + "api/v1/user/gpg_keys/"
|
||||
}
|
||||
|
||||
func listGPGKeys(ctx *context.APIContext, uid int64) {
|
||||
keys, err := models.ListGPGKeys(uid)
|
||||
if err != nil {
|
||||
|
||||
@@ -98,6 +98,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
|
||||
URL: form.Config["url"],
|
||||
ContentType: models.ToHookContentType(form.Config["content_type"]),
|
||||
Secret: form.Config["secret"],
|
||||
HTTPMethod: "POST",
|
||||
HookEvent: &models.HookEvent{
|
||||
ChooseEvents: true,
|
||||
HookEvents: models.HookEvents{
|
||||
|
||||
+6
-4
@@ -5,7 +5,6 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/mailer"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/external"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/ssh"
|
||||
|
||||
@@ -41,7 +41,7 @@ func checkRunMode() {
|
||||
func NewServices() {
|
||||
setting.NewServices()
|
||||
mailer.NewContext()
|
||||
cache.NewContext()
|
||||
_ = cache.NewContext()
|
||||
}
|
||||
|
||||
// In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology
|
||||
@@ -65,6 +65,9 @@ func initDBEngine() (err error) {
|
||||
// GlobalInit is for global configuration reload-able.
|
||||
func GlobalInit() {
|
||||
setting.NewContext()
|
||||
if err := git.Init(); err != nil {
|
||||
log.Fatal("Git module init failed: %v", err)
|
||||
}
|
||||
setting.CheckLFSVersion()
|
||||
log.Trace("AppPath: %s", setting.AppPath)
|
||||
log.Trace("AppWorkPath: %s", setting.AppWorkPath)
|
||||
@@ -75,6 +78,7 @@ func GlobalInit() {
|
||||
|
||||
if setting.InstallLock {
|
||||
highlight.NewContext()
|
||||
external.RegisterParsers()
|
||||
markup.Init()
|
||||
if err := initDBEngine(); err == nil {
|
||||
log.Info("ORM engine initialization successful!")
|
||||
@@ -86,7 +90,6 @@ func GlobalInit() {
|
||||
log.Fatal("Failed to initialize OAuth2 support: %v", err)
|
||||
}
|
||||
|
||||
models.LoadRepoConfig()
|
||||
models.NewRepoContext()
|
||||
|
||||
// Booting long running goroutines.
|
||||
@@ -98,7 +101,6 @@ func GlobalInit() {
|
||||
models.InitSyncMirrors()
|
||||
models.InitDeliverHooks()
|
||||
models.InitTestPullRequests()
|
||||
log.NewGitLogger(path.Join(setting.LogRootPath, "http.log"))
|
||||
}
|
||||
if models.EnableSQLite3 {
|
||||
log.Info("SQLite3 Supported")
|
||||
|
||||
+39
-12
@@ -57,6 +57,7 @@ func Install(ctx *context.Context) {
|
||||
form.DbPasswd = models.DbCfg.Passwd
|
||||
form.DbName = models.DbCfg.Name
|
||||
form.DbPath = models.DbCfg.Path
|
||||
form.Charset = models.DbCfg.Charset
|
||||
|
||||
ctx.Data["CurDbOption"] = "MySQL"
|
||||
switch models.DbCfg.Type {
|
||||
@@ -150,6 +151,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
|
||||
models.DbCfg.Passwd = form.DbPasswd
|
||||
models.DbCfg.Name = form.DbName
|
||||
models.DbCfg.SSLMode = form.SSLMode
|
||||
models.DbCfg.Charset = form.Charset
|
||||
models.DbCfg.Path = form.DbPath
|
||||
|
||||
if (models.DbCfg.Type == "sqlite3") &&
|
||||
@@ -213,18 +215,42 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check admin password.
|
||||
if len(form.AdminName) > 0 && len(form.AdminPasswd) == 0 {
|
||||
ctx.Data["Err_Admin"] = true
|
||||
ctx.Data["Err_AdminPasswd"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("install.err_empty_admin_password"), tplInstall, form)
|
||||
return
|
||||
}
|
||||
if form.AdminPasswd != form.AdminConfirmPasswd {
|
||||
ctx.Data["Err_Admin"] = true
|
||||
ctx.Data["Err_AdminPasswd"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplInstall, form)
|
||||
return
|
||||
// Check admin user creation
|
||||
if len(form.AdminName) > 0 {
|
||||
// Ensure AdminName is valid
|
||||
if err := models.IsUsableUsername(form.AdminName); err != nil {
|
||||
ctx.Data["Err_Admin"] = true
|
||||
ctx.Data["Err_AdminName"] = true
|
||||
if models.IsErrNameReserved(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("install.err_admin_name_is_reserved"), tplInstall, form)
|
||||
return
|
||||
} else if models.IsErrNamePatternNotAllowed(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("install.err_admin_name_pattern_not_allowed"), tplInstall, form)
|
||||
return
|
||||
}
|
||||
ctx.RenderWithErr(ctx.Tr("install.err_admin_name_is_invalid"), tplInstall, form)
|
||||
return
|
||||
}
|
||||
// Check Admin email
|
||||
if len(form.AdminEmail) == 0 {
|
||||
ctx.Data["Err_Admin"] = true
|
||||
ctx.Data["Err_AdminEmail"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("install.err_empty_admin_email"), tplInstall, form)
|
||||
return
|
||||
}
|
||||
// Check admin password.
|
||||
if len(form.AdminPasswd) == 0 {
|
||||
ctx.Data["Err_Admin"] = true
|
||||
ctx.Data["Err_AdminPasswd"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("install.err_empty_admin_password"), tplInstall, form)
|
||||
return
|
||||
}
|
||||
if form.AdminPasswd != form.AdminConfirmPasswd {
|
||||
ctx.Data["Err_Admin"] = true
|
||||
ctx.Data["Err_AdminPasswd"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplInstall, form)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if form.AppURL[len(form.AppURL)-1] != '/' {
|
||||
@@ -245,6 +271,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
|
||||
cfg.Section("database").Key("USER").SetValue(models.DbCfg.User)
|
||||
cfg.Section("database").Key("PASSWD").SetValue(models.DbCfg.Passwd)
|
||||
cfg.Section("database").Key("SSL_MODE").SetValue(models.DbCfg.SSLMode)
|
||||
cfg.Section("database").Key("CHARSET").SetValue(models.DbCfg.Charset)
|
||||
cfg.Section("database").Key("PATH").SetValue(models.DbCfg.Path)
|
||||
|
||||
cfg.Section("").Key("APP_NAME").SetValue(form.AppName)
|
||||
|
||||
+5
-1
@@ -5,6 +5,8 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@@ -22,7 +24,9 @@ func Metrics(ctx *context.Context) {
|
||||
ctx.Error(401)
|
||||
return
|
||||
}
|
||||
if header != "Bearer "+setting.Metrics.Token {
|
||||
got := []byte(header)
|
||||
want := []byte("Bearer " + setting.Metrics.Token)
|
||||
if subtle.ConstantTimeCompare(got, want) != 1 {
|
||||
ctx.Error(401)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ func Settings(ctx *context.Context) {
|
||||
func SettingsPost(ctx *context.Context, form auth.UpdateOrgSettingForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("org.settings")
|
||||
ctx.Data["PageIsSettingsOptions"] = true
|
||||
ctx.Data["CurrentVisibility"] = structs.VisibleType(ctx.Org.Organization.Visibility)
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.HTML(200, tplSettingsOptions)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package org
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@@ -291,7 +292,11 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
|
||||
Type: tp,
|
||||
})
|
||||
}
|
||||
models.UpdateTeamUnits(t, units)
|
||||
err := models.UpdateTeamUnits(t, units)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
|
||||
@@ -1,52 +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 private
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// GetProtectedBranchBy get protected branch information
|
||||
func GetProtectedBranchBy(ctx *macaron.Context) {
|
||||
repoID := ctx.ParamsInt64(":id")
|
||||
branchName := ctx.Params("*")
|
||||
protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
} else if protectBranch != nil {
|
||||
ctx.JSON(200, protectBranch)
|
||||
} else {
|
||||
ctx.JSON(200, &models.ProtectedBranch{
|
||||
ID: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// CanUserPush returns if user push
|
||||
func CanUserPush(ctx *macaron.Context) {
|
||||
pbID := ctx.ParamsInt64(":pbid")
|
||||
userID := ctx.ParamsInt64(":userid")
|
||||
|
||||
protectBranch, err := models.GetProtectedBranchByID(pbID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
} else if protectBranch != nil {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"can_push": protectBranch.CanUserPush(userID),
|
||||
})
|
||||
} else {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"can_push": false,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
// Copyright 2019 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 private includes all internal routes. The package name internal is ideal but Golang is not allowed, so we use private as package name instead.
|
||||
package private
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// HookPreReceive checks whether a individual commit is acceptable
|
||||
func HookPreReceive(ctx *macaron.Context) {
|
||||
ownerName := ctx.Params(":owner")
|
||||
repoName := ctx.Params(":repo")
|
||||
oldCommitID := ctx.QueryTrim("old")
|
||||
newCommitID := ctx.QueryTrim("new")
|
||||
refFullName := ctx.QueryTrim("ref")
|
||||
userID := ctx.QueryInt64("userID")
|
||||
gitObjectDirectory := ctx.QueryTrim("gitObjectDirectory")
|
||||
gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories")
|
||||
prID := ctx.QueryInt64("prID")
|
||||
|
||||
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||
if err != nil {
|
||||
log.Error("Unable to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
repo.OwnerName = ownerName
|
||||
protectBranch, err := models.GetProtectedBranchBy(repo.ID, branchName)
|
||||
if err != nil {
|
||||
log.Error("Unable to get protected branch: %s in %-v Error: %v", branchName, repo, err)
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
if protectBranch != nil && protectBranch.IsProtected() {
|
||||
// check and deletion
|
||||
if newCommitID == git.EmptySHA {
|
||||
log.Warn("Forbidden: Branch: %s in %-v is protected from deletion", branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("branch %s is protected from deletion", branchName),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// detect force push
|
||||
if git.EmptySHA != oldCommitID {
|
||||
env := append(os.Environ(),
|
||||
private.GitAlternativeObjectDirectories+"="+gitAlternativeObjectDirectories,
|
||||
private.GitObjectDirectory+"="+gitObjectDirectory,
|
||||
private.GitQuarantinePath+"="+gitObjectDirectory,
|
||||
)
|
||||
|
||||
output, err := git.NewCommand("rev-list", "--max-count=1", oldCommitID, "^"+newCommitID).RunInDirWithEnv(repo.RepoPath(), env)
|
||||
if err != nil {
|
||||
log.Error("Unable to detect force push between: %s and %s in %-v Error: %v", oldCommitID, newCommitID, repo, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Fail to detect force push: %v", err),
|
||||
})
|
||||
return
|
||||
} else if len(output) > 0 {
|
||||
log.Warn("Forbidden: Branch: %s in %-v is protected from force push", branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("branch %s is protected from force push", branchName),
|
||||
})
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
canPush := protectBranch.CanUserPush(userID)
|
||||
if !canPush && prID > 0 {
|
||||
pr, err := models.GetPullRequestByID(prID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get PullRequest %d Error: %v", prID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Unable to get PullRequest %d Error: %v", prID, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if !protectBranch.HasEnoughApprovals(pr) {
|
||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d does not have enough approvals", userID, branchName, repo, pr.Index)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d does not have enough approvals", branchName, prID),
|
||||
})
|
||||
return
|
||||
}
|
||||
} else if !canPush {
|
||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v", userID, branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("protected branch %s can not be pushed to", branchName),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.PlainText(http.StatusOK, []byte("ok"))
|
||||
}
|
||||
|
||||
// HookPostReceive updates services and users
|
||||
func HookPostReceive(ctx *macaron.Context) {
|
||||
ownerName := ctx.Params(":owner")
|
||||
repoName := ctx.Params(":repo")
|
||||
oldCommitID := ctx.Query("old")
|
||||
newCommitID := ctx.Query("new")
|
||||
refFullName := ctx.Query("ref")
|
||||
userID := ctx.QueryInt64("userID")
|
||||
userName := ctx.Query("username")
|
||||
|
||||
branch := refFullName
|
||||
if strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||
branch = strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||
} else if strings.HasPrefix(refFullName, git.TagPrefix) {
|
||||
branch = strings.TrimPrefix(refFullName, git.TagPrefix)
|
||||
}
|
||||
|
||||
// Only trigger activity updates for changes to branches or
|
||||
// tags. Updates to other refs (eg, refs/notes, refs/changes,
|
||||
// or other less-standard refs spaces are ignored since there
|
||||
// may be a very large number of them).
|
||||
if strings.HasPrefix(refFullName, git.BranchPrefix) || strings.HasPrefix(refFullName, git.TagPrefix) {
|
||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||
if err != nil {
|
||||
log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if err := repofiles.PushUpdate(repo, branch, models.PushUpdateOptions{
|
||||
RefFullName: refFullName,
|
||||
OldCommitID: oldCommitID,
|
||||
NewCommitID: newCommitID,
|
||||
PusherID: userID,
|
||||
PusherName: userName,
|
||||
RepoUserName: ownerName,
|
||||
RepoName: repoName,
|
||||
}); err != nil {
|
||||
log.Error("Failed to Update: %s/%s Branch: %s Error: %v", ownerName, repoName, branch, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Failed to Update: %s/%s Branch: %s Error: %v", ownerName, repoName, branch, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if newCommitID != git.EmptySHA && strings.HasPrefix(refFullName, git.BranchPrefix) {
|
||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||
if err != nil {
|
||||
log.Error("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Failed to get repository: %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
repo.OwnerName = ownerName
|
||||
|
||||
pullRequestAllowed := repo.AllowsPulls()
|
||||
if !pullRequestAllowed {
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"message": false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
baseRepo := repo
|
||||
if repo.IsFork {
|
||||
if err := repo.GetBaseRepo(); err != nil {
|
||||
log.Error("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Failed to get Base Repository of Forked repository: %-v Error: %v", repo, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
baseRepo = repo.BaseRepo
|
||||
}
|
||||
|
||||
if !repo.IsFork && branch == baseRepo.DefaultBranch {
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"message": false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := models.GetUnmergedPullRequest(repo.ID, baseRepo.ID, branch, baseRepo.DefaultBranch)
|
||||
if err != nil && !models.IsErrPullRequestNotExist(err) {
|
||||
log.Error("Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf(
|
||||
"Failed to get active PR in: %-v Branch: %s to: %-v Branch: %s Error: %v", repo, branch, baseRepo, baseRepo.DefaultBranch, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if pr == nil {
|
||||
if repo.IsFork {
|
||||
branch = fmt.Sprintf("%s:%s", repo.OwnerName, branch)
|
||||
}
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"message": true,
|
||||
"create": true,
|
||||
"branch": branch,
|
||||
"url": fmt.Sprintf("%s/compare/%s...%s", baseRepo.HTMLURL(), util.PathEscapeSegments(baseRepo.DefaultBranch), util.PathEscapeSegments(branch)),
|
||||
})
|
||||
} else {
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"message": true,
|
||||
"create": false,
|
||||
"branch": branch,
|
||||
"url": fmt.Sprintf("%s/pulls/%d", baseRepo.HTMLURL(), pr.Index),
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, map[string]interface{}{
|
||||
"message": false,
|
||||
})
|
||||
}
|
||||
@@ -76,19 +76,10 @@ func CheckUnitUser(ctx *macaron.Context) {
|
||||
// These APIs will be invoked by internal commands for example `gitea serv` and etc.
|
||||
func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/", func() {
|
||||
m.Get("/ssh/:id", GetPublicKeyByID)
|
||||
m.Get("/ssh/:id/user", GetUserByKeyID)
|
||||
m.Post("/ssh/:id/update", UpdatePublicKey)
|
||||
m.Post("/repositories/:repoid/keys/:keyid/update", UpdateDeployKey)
|
||||
m.Get("/repositories/:repoid/user/:userid/checkunituser", CheckUnitUser)
|
||||
m.Get("/repositories/:repoid/has-keys/:keyid", HasDeployKey)
|
||||
m.Get("/repositories/:repoid/keys/:keyid", GetDeployKey)
|
||||
m.Get("/repositories/:repoid/wiki/init", InitWiki)
|
||||
m.Post("/push/update", PushUpdate)
|
||||
m.Get("/protectedbranch/:pbid/:userid", CanUserPush)
|
||||
m.Get("/repo/:owner/:repo", GetRepositoryByOwnerAndName)
|
||||
m.Get("/branch/:id/*", GetProtectedBranchBy)
|
||||
m.Get("/repository/:rid", GetRepository)
|
||||
m.Get("/active-pull-request", GetActivePullRequest)
|
||||
m.Post("/ssh/:id/update/:repoid", UpdatePublicKeyInRepo)
|
||||
m.Get("/hook/pre-receive/:owner/:repo", HookPreReceive)
|
||||
m.Get("/hook/post-receive/:owner/:repo", HookPostReceive)
|
||||
m.Get("/serv/none/:keyid", ServNoCommand)
|
||||
m.Get("/serv/command/:keyid/:owner/:repo", ServCommand)
|
||||
}, CheckInternalToken)
|
||||
}
|
||||
|
||||
+14
-70
@@ -12,12 +12,23 @@ import (
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// UpdateDeployKey update deploy key updates
|
||||
func UpdateDeployKey(ctx *macaron.Context) {
|
||||
// UpdatePublicKeyInRepo update public key and deploy key updates
|
||||
func UpdatePublicKeyInRepo(ctx *macaron.Context) {
|
||||
keyID := ctx.ParamsInt64(":id")
|
||||
repoID := ctx.ParamsInt64(":repoid")
|
||||
keyID := ctx.ParamsInt64(":keyid")
|
||||
if err := models.UpdatePublicKeyUpdated(keyID); err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
deployKey, err := models.GetDeployKeyByRepo(keyID, repoID)
|
||||
if err != nil {
|
||||
if models.IsErrDeployKeyNotExist(err) {
|
||||
ctx.PlainText(200, []byte("success"))
|
||||
return
|
||||
}
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
@@ -30,73 +41,6 @@ func UpdateDeployKey(ctx *macaron.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
ctx.PlainText(200, []byte("success"))
|
||||
}
|
||||
|
||||
// UpdatePublicKey update publick key updates
|
||||
func UpdatePublicKey(ctx *macaron.Context) {
|
||||
keyID := ctx.ParamsInt64(":id")
|
||||
if err := models.UpdatePublicKeyUpdated(keyID); err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.PlainText(200, []byte("success"))
|
||||
}
|
||||
|
||||
//GetPublicKeyByID chainload to models.GetPublicKeyByID
|
||||
func GetPublicKeyByID(ctx *macaron.Context) {
|
||||
keyID := ctx.ParamsInt64(":id")
|
||||
key, err := models.GetPublicKeyByID(keyID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, key)
|
||||
}
|
||||
|
||||
//GetUserByKeyID chainload to models.GetUserByKeyID
|
||||
func GetUserByKeyID(ctx *macaron.Context) {
|
||||
keyID := ctx.ParamsInt64(":id")
|
||||
user, err := models.GetUserByKeyID(keyID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, user)
|
||||
}
|
||||
|
||||
//GetDeployKey chainload to models.GetDeployKey
|
||||
func GetDeployKey(ctx *macaron.Context) {
|
||||
repoID := ctx.ParamsInt64(":repoid")
|
||||
keyID := ctx.ParamsInt64(":keyid")
|
||||
dKey, err := models.GetDeployKeyByRepo(keyID, repoID)
|
||||
if err != nil {
|
||||
if models.IsErrDeployKeyNotExist(err) {
|
||||
ctx.JSON(404, []byte("not found"))
|
||||
return
|
||||
}
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, dKey)
|
||||
}
|
||||
|
||||
//HasDeployKey chainload to models.HasDeployKey
|
||||
func HasDeployKey(ctx *macaron.Context) {
|
||||
repoID := ctx.ParamsInt64(":repoid")
|
||||
keyID := ctx.ParamsInt64(":keyid")
|
||||
if models.HasDeployKey(keyID, repoID) {
|
||||
ctx.PlainText(200, []byte("success"))
|
||||
return
|
||||
}
|
||||
ctx.PlainText(404, []byte("not found"))
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
@@ -32,7 +33,15 @@ func PushUpdate(ctx *macaron.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err := models.PushUpdate(branch, opt)
|
||||
repo, err := models.GetRepositoryByOwnerAndName(opt.RepoUserName, opt.RepoName)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = repofiles.PushUpdate(repo, branch, opt)
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
ctx.Error(404)
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright 2018 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 private
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// GetRepository return the default branch of a repository
|
||||
func GetRepository(ctx *macaron.Context) {
|
||||
repoID := ctx.ParamsInt64(":rid")
|
||||
repository, err := models.GetRepositoryByID(repoID)
|
||||
repository.MustOwnerName()
|
||||
allowPulls := repository.AllowsPulls()
|
||||
// put it back to nil because json unmarshal can't unmarshal it
|
||||
repository.Units = nil
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if repository.IsFork {
|
||||
repository.GetBaseRepo()
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
repository.BaseRepo.MustOwnerName()
|
||||
allowPulls = repository.BaseRepo.AllowsPulls()
|
||||
// put it back to nil because json unmarshal can't unmarshal it
|
||||
repository.BaseRepo.Units = nil
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, struct {
|
||||
Repository *models.Repository
|
||||
AllowPullRequest bool
|
||||
}{
|
||||
Repository: repository,
|
||||
AllowPullRequest: allowPulls,
|
||||
})
|
||||
}
|
||||
|
||||
// GetActivePullRequest return an active pull request when it exists or an empty object
|
||||
func GetActivePullRequest(ctx *macaron.Context) {
|
||||
baseRepoID := ctx.QueryInt64("baseRepoID")
|
||||
headRepoID := ctx.QueryInt64("headRepoID")
|
||||
baseBranch := ctx.QueryTrim("baseBranch")
|
||||
if len(baseBranch) == 0 {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": "QueryTrim failed",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
headBranch := ctx.QueryTrim("headBranch")
|
||||
if len(headBranch) == 0 {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": "QueryTrim failed",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := models.GetUnmergedPullRequest(headRepoID, baseRepoID, headBranch, baseBranch)
|
||||
if err != nil && !models.IsErrPullRequestNotExist(err) {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, pr)
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright 2019 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 private includes all internal routes. The package name internal is ideal but Golang is not allowed, so we use private as package name instead.
|
||||
package private
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// ServNoCommand returns information about the provided keyid
|
||||
func ServNoCommand(ctx *macaron.Context) {
|
||||
keyID := ctx.ParamsInt64(":keyid")
|
||||
if keyID <= 0 {
|
||||
ctx.JSON(http.StatusBadRequest, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Bad key id: %d", keyID),
|
||||
})
|
||||
}
|
||||
results := private.KeyAndOwner{}
|
||||
|
||||
key, err := models.GetPublicKeyByID(keyID)
|
||||
if err != nil {
|
||||
if models.IsErrKeyNotExist(err) {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Cannot find key: %d", keyID),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to get public key: %d Error: %v", keyID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
results.Key = key
|
||||
|
||||
if key.Type == models.KeyTypeUser {
|
||||
user, err := models.GetUserByID(key.OwnerID)
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Cannot find owner with id: %d for key: %d", key.OwnerID, keyID),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to get owner with id: %d for public key: %d Error: %v", key.OwnerID, keyID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
results.Owner = user
|
||||
}
|
||||
ctx.JSON(http.StatusOK, &results)
|
||||
}
|
||||
|
||||
// ServCommand returns information about the provided keyid
|
||||
func ServCommand(ctx *macaron.Context) {
|
||||
// Although we provide the verbs we don't need them at present they're just for logging purposes
|
||||
keyID := ctx.ParamsInt64(":keyid")
|
||||
ownerName := ctx.Params(":owner")
|
||||
repoName := ctx.Params(":repo")
|
||||
mode := models.AccessMode(ctx.QueryInt("mode"))
|
||||
|
||||
// Set the basic parts of the results to return
|
||||
results := private.ServCommandResults{
|
||||
RepoName: repoName,
|
||||
OwnerName: ownerName,
|
||||
KeyID: keyID,
|
||||
}
|
||||
|
||||
// Now because we're not translating things properly let's just default some Engish strings here
|
||||
modeString := "read"
|
||||
if mode > models.AccessModeRead {
|
||||
modeString = "write to"
|
||||
}
|
||||
|
||||
// The default unit we're trying to look at is code
|
||||
unitType := models.UnitTypeCode
|
||||
|
||||
// Unless we're a wiki...
|
||||
if strings.HasSuffix(repoName, ".wiki") {
|
||||
// in which case we need to look at the wiki
|
||||
unitType = models.UnitTypeWiki
|
||||
// And we'd better munge the reponame and tell downstream we're looking at a wiki
|
||||
results.IsWiki = true
|
||||
results.RepoName = repoName[:len(repoName)-5]
|
||||
}
|
||||
|
||||
// Now get the Repository and set the results section
|
||||
repo, err := models.GetRepositoryByOwnerAndName(results.OwnerName, results.RepoName)
|
||||
if err != nil {
|
||||
if models.IsErrRepoNotExist(err) {
|
||||
ctx.JSON(http.StatusNotFound, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrRepoNotExist",
|
||||
"err": fmt.Sprintf("Cannot find repository %s/%s", results.OwnerName, results.RepoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to get repository: %s/%s Error: %v", results.OwnerName, results.RepoName, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": fmt.Sprintf("Unable to get repository: %s/%s %v", results.OwnerName, results.RepoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
repo.OwnerName = ownerName
|
||||
results.RepoID = repo.ID
|
||||
|
||||
// We can shortcut at this point if the repo is a mirror
|
||||
if mode > models.AccessModeRead && repo.IsMirror {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrMirrorReadOnly",
|
||||
"err": fmt.Sprintf("Mirror Repository %s/%s is read-only", results.OwnerName, results.RepoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get the Public Key represented by the keyID
|
||||
key, err := models.GetPublicKeyByID(keyID)
|
||||
if err != nil {
|
||||
if models.IsErrKeyNotExist(err) {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrKeyNotExist",
|
||||
"err": fmt.Sprintf("Cannot find key: %d", keyID),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to get public key: %d Error: %v", keyID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": fmt.Sprintf("Unable to get key: %d Error: %v", keyID, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
results.KeyName = key.Name
|
||||
results.KeyID = key.ID
|
||||
results.UserID = key.OwnerID
|
||||
|
||||
// Deploy Keys have ownerID set to 0 therefore we can't use the owner
|
||||
// So now we need to check if the key is a deploy key
|
||||
// We'll keep hold of the deploy key here for permissions checking
|
||||
var deployKey *models.DeployKey
|
||||
var user *models.User
|
||||
if key.Type == models.KeyTypeDeploy {
|
||||
results.IsDeployKey = true
|
||||
|
||||
var err error
|
||||
deployKey, err = models.GetDeployKeyByRepo(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
if models.IsErrDeployKeyNotExist(err) {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrDeployKeyNotExist",
|
||||
"err": fmt.Sprintf("Public (Deploy) Key: %d:%s is not authorized to %s %s/%s.", key.ID, key.Name, modeString, results.OwnerName, results.RepoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to get deploy for public (deploy) key: %d in %-v Error: %v", key.ID, repo, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": fmt.Sprintf("Unable to get Deploy Key for Public Key: %d:%s in %s/%s.", key.ID, key.Name, results.OwnerName, results.RepoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
results.KeyName = deployKey.Name
|
||||
|
||||
// FIXME: Deploy keys aren't really the owner of the repo pushing changes
|
||||
// however we don't have good way of representing deploy keys in hook.go
|
||||
// so for now use the owner of the repository
|
||||
results.UserName = results.OwnerName
|
||||
results.UserID = repo.OwnerID
|
||||
} else {
|
||||
// Get the user represented by the Key
|
||||
var err error
|
||||
user, err = models.GetUserByID(key.OwnerID)
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrUserNotExist",
|
||||
"err": fmt.Sprintf("Public Key: %d:%s owner %d does not exist.", key.ID, key.Name, key.OwnerID),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to get owner: %d for public key: %d:%s Error: %v", key.OwnerID, key.ID, key.Name, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": fmt.Sprintf("Unable to get Owner: %d for Deploy Key: %d:%s in %s/%s.", key.OwnerID, key.ID, key.Name, ownerName, repoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
results.UserName = user.Name
|
||||
}
|
||||
|
||||
// Don't allow pushing if the repo is archived
|
||||
if mode > models.AccessModeRead && repo.IsArchived {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrRepoIsArchived",
|
||||
"err": fmt.Sprintf("Repo: %s/%s is archived.", results.OwnerName, results.RepoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Permissions checking:
|
||||
if mode > models.AccessModeRead || repo.IsPrivate || setting.Service.RequireSignInView {
|
||||
if key.Type == models.KeyTypeDeploy {
|
||||
if deployKey.Mode < mode {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrUnauthorized",
|
||||
"err": fmt.Sprintf("Deploy Key: %d:%s is not authorized to %s %s/%s.", key.ID, key.Name, modeString, results.OwnerName, results.RepoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
perm, err := models.GetUserRepoPermission(repo, user)
|
||||
if err != nil {
|
||||
log.Error("Unable to get permissions for %-v with key %d in %-v Error: %v", user, key.ID, repo, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": fmt.Sprintf("Unable to get permissions for user %d:%s with key %d in %s/%s Error: %v", user.ID, user.Name, key.ID, results.OwnerName, results.RepoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
userMode := perm.UnitAccessMode(unitType)
|
||||
|
||||
if userMode < mode {
|
||||
ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "ErrUnauthorized",
|
||||
"err": fmt.Sprintf("User: %d:%s with Key: %d:%s is not authorized to %s %s/%s.", user.ID, user.Name, key.ID, key.Name, modeString, ownerName, repoName),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally if we're trying to touch the wiki we should init it
|
||||
if results.IsWiki {
|
||||
if err = repo.InitWiki(); err != nil {
|
||||
log.Error("Failed to initialize the wiki in %-v Error: %v", repo, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"results": results,
|
||||
"type": "InternalServerError",
|
||||
"err": fmt.Sprintf("Failed to initialize the wiki in %s/%s Error: %v", ownerName, repoName, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Debug("Serv Results:\nIsWiki: %t\nIsDeployKey: %t\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d",
|
||||
results.IsWiki,
|
||||
results.IsDeployKey,
|
||||
results.KeyID,
|
||||
results.KeyName,
|
||||
results.UserName,
|
||||
results.UserID,
|
||||
results.OwnerName,
|
||||
results.RepoName,
|
||||
results.RepoID)
|
||||
|
||||
ctx.JSON(http.StatusOK, results)
|
||||
// We will update the keys in a different call.
|
||||
}
|
||||
@@ -1,34 +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 private
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
// InitWiki initilizes wiki via repo id
|
||||
func InitWiki(ctx *macaron.Context) {
|
||||
repoID := ctx.ParamsInt64("repoid")
|
||||
|
||||
repo, err := models.GetRepositoryByID(repoID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = repo.InitWiki()
|
||||
if err != nil {
|
||||
ctx.JSON(500, map[string]interface{}{
|
||||
"err": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(202)
|
||||
}
|
||||
@@ -6,13 +6,13 @@ package repo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/upload"
|
||||
)
|
||||
|
||||
func renderAttachmentSettings(ctx *context.Context) {
|
||||
@@ -42,21 +42,10 @@ func UploadAttachment(ctx *context.Context) {
|
||||
if n > 0 {
|
||||
buf = buf[:n]
|
||||
}
|
||||
fileType := http.DetectContentType(buf)
|
||||
|
||||
allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",")
|
||||
allowed := false
|
||||
for _, t := range allowedTypes {
|
||||
t := strings.Trim(t, " ")
|
||||
if t == "*/*" || t == fileType {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
log.Info("Attachment with type %s blocked from upload", fileType)
|
||||
ctx.Error(400, ErrFileTypeForbidden.Error())
|
||||
err = upload.VerifyAllowedContentType(buf, strings.Split(setting.AttachmentAllowedTypes, ","))
|
||||
if err != nil {
|
||||
ctx.Error(400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -131,14 +131,14 @@ func RefBlame(ctx *context.Context) {
|
||||
ctx.Data["FileSize"] = blob.Size()
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
|
||||
blameReader, err := models.CreateBlameReader(models.RepoPath(userName, repoName), commitID, fileName)
|
||||
blameReader, err := git.CreateBlameReader(models.RepoPath(userName, repoName), commitID, fileName)
|
||||
if err != nil {
|
||||
ctx.NotFound("CreateBlameReader", err)
|
||||
return
|
||||
}
|
||||
defer blameReader.Close()
|
||||
|
||||
blameParts := make([]models.BlamePart, 0)
|
||||
blameParts := make([]git.BlamePart, 0)
|
||||
|
||||
for {
|
||||
blamePart, err := blameReader.NextPart()
|
||||
@@ -189,10 +189,10 @@ func RefBlame(ctx *context.Context) {
|
||||
ctx.HTML(200, tplBlame)
|
||||
}
|
||||
|
||||
func renderBlame(ctx *context.Context, blameParts []models.BlamePart, commitNames map[string]models.UserCommit) {
|
||||
func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]models.UserCommit) {
|
||||
repoLink := ctx.Repo.RepoLink
|
||||
|
||||
var lines = make([]string, 0, 0)
|
||||
var lines = make([]string, 0)
|
||||
|
||||
var commitInfo bytes.Buffer
|
||||
var lineNumbers bytes.Buffer
|
||||
|
||||
+38
-21
@@ -24,13 +24,14 @@ const (
|
||||
|
||||
// Branch contains the branch information
|
||||
type Branch struct {
|
||||
Name string
|
||||
Commit *git.Commit
|
||||
IsProtected bool
|
||||
IsDeleted bool
|
||||
DeletedBranch *models.DeletedBranch
|
||||
CommitsAhead int
|
||||
CommitsBehind int
|
||||
Name string
|
||||
Commit *git.Commit
|
||||
IsProtected bool
|
||||
IsDeleted bool
|
||||
DeletedBranch *models.DeletedBranch
|
||||
CommitsAhead int
|
||||
CommitsBehind int
|
||||
LatestPullRequest *models.PullRequest
|
||||
}
|
||||
|
||||
// Branches render repository branch page
|
||||
@@ -131,15 +132,18 @@ func deleteBranch(ctx *context.Context, branchName string) error {
|
||||
}
|
||||
|
||||
// Don't return error below this
|
||||
if err := models.PushUpdate(branchName, models.PushUpdateOptions{
|
||||
RefFullName: git.BranchPrefix + branchName,
|
||||
OldCommitID: commit.ID.String(),
|
||||
NewCommitID: git.EmptySHA,
|
||||
PusherID: ctx.User.ID,
|
||||
PusherName: ctx.User.Name,
|
||||
RepoUserName: ctx.Repo.Owner.Name,
|
||||
RepoName: ctx.Repo.Repository.Name,
|
||||
}); err != nil {
|
||||
if err := repofiles.PushUpdate(
|
||||
ctx.Repo.Repository,
|
||||
branchName,
|
||||
models.PushUpdateOptions{
|
||||
RefFullName: git.BranchPrefix + branchName,
|
||||
OldCommitID: commit.ID.String(),
|
||||
NewCommitID: git.EmptySHA,
|
||||
PusherID: ctx.User.ID,
|
||||
PusherName: ctx.User.Name,
|
||||
RepoUserName: ctx.Repo.Owner.Name,
|
||||
RepoName: ctx.Repo.Repository.Name,
|
||||
}); err != nil {
|
||||
log.Error("Update: %v", err)
|
||||
}
|
||||
|
||||
@@ -178,12 +182,25 @@ func loadBranches(ctx *context.Context) []*Branch {
|
||||
return nil
|
||||
}
|
||||
|
||||
pr, err := models.GetLatestPullRequestByHeadInfo(ctx.Repo.Repository.ID, branchName)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestPullRequestByHeadInfo", err)
|
||||
return nil
|
||||
}
|
||||
if pr != nil {
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
ctx.ServerError("pr.LoadIssue", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
branches[i] = &Branch{
|
||||
Name: branchName,
|
||||
Commit: commit,
|
||||
IsProtected: isProtected,
|
||||
CommitsAhead: divergence.Ahead,
|
||||
CommitsBehind: divergence.Behind,
|
||||
Name: branchName,
|
||||
Commit: commit,
|
||||
IsProtected: isProtected,
|
||||
CommitsAhead: divergence.Ahead,
|
||||
CommitsBehind: divergence.Behind,
|
||||
LatestPullRequest: pr,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-55
@@ -15,12 +15,13 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
)
|
||||
|
||||
const (
|
||||
tplCommits base.TplName = "repo/commits"
|
||||
tplGraph base.TplName = "repo/graph"
|
||||
tplDiff base.TplName = "repo/diff/page"
|
||||
tplCommits base.TplName = "repo/commits"
|
||||
tplGraph base.TplName = "repo/graph"
|
||||
tplCommitPage base.TplName = "repo/commit_page"
|
||||
)
|
||||
|
||||
// RefCommits render commits page
|
||||
@@ -246,12 +247,24 @@ func Diff(ctx *context.Context) {
|
||||
ctx.Data["Parents"] = parents
|
||||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
|
||||
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", commitID)
|
||||
|
||||
note := &git.Note{}
|
||||
err = git.GetNote(ctx.Repo.GitRepo, commitID, note)
|
||||
if err == nil {
|
||||
ctx.Data["Note"] = string(templates.ToUTF8WithFallback(note.Message))
|
||||
ctx.Data["NoteCommit"] = note.Commit
|
||||
ctx.Data["NoteAuthor"] = models.ValidateCommitWithEmail(note.Commit)
|
||||
}
|
||||
|
||||
if commit.ParentCount() > 0 {
|
||||
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", parents[0])
|
||||
}
|
||||
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", commitID)
|
||||
ctx.Data["BranchName"], err = commit.GetBranchName()
|
||||
ctx.HTML(200, tplDiff)
|
||||
if err != nil {
|
||||
ctx.ServerError("commit.GetBranchName", err)
|
||||
}
|
||||
ctx.HTML(200, tplCommitPage)
|
||||
}
|
||||
|
||||
// RawDiff dumps diff results of repository in given commit ID to io.Writer
|
||||
@@ -266,54 +279,3 @@ func RawDiff(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// CompareDiff show different from one commit to another commit
|
||||
func CompareDiff(ctx *context.Context) {
|
||||
ctx.Data["IsRepoToolbarCommits"] = true
|
||||
ctx.Data["IsDiffCompare"] = true
|
||||
userName := ctx.Repo.Owner.Name
|
||||
repoName := ctx.Repo.Repository.Name
|
||||
beforeCommitID := ctx.Params(":before")
|
||||
afterCommitID := ctx.Params(":after")
|
||||
|
||||
commit, err := ctx.Repo.GitRepo.GetCommit(afterCommitID)
|
||||
if err != nil {
|
||||
ctx.NotFound("GetCommit", err)
|
||||
return
|
||||
}
|
||||
|
||||
diff, err := models.GetDiffRange(models.RepoPath(userName, repoName), beforeCommitID,
|
||||
afterCommitID, setting.Git.MaxGitDiffLines,
|
||||
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
|
||||
if err != nil {
|
||||
ctx.NotFound("GetDiffRange", err)
|
||||
return
|
||||
}
|
||||
|
||||
commits, err := commit.CommitsBeforeUntil(beforeCommitID)
|
||||
if err != nil {
|
||||
ctx.ServerError("CommitsBeforeUntil", err)
|
||||
return
|
||||
}
|
||||
commits = models.ValidateCommitsWithEmails(commits)
|
||||
commits = models.ParseCommitsWithSignature(commits)
|
||||
commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
|
||||
|
||||
ctx.Data["CommitRepoLink"] = ctx.Repo.RepoLink
|
||||
ctx.Data["Commits"] = commits
|
||||
ctx.Data["CommitCount"] = commits.Len()
|
||||
ctx.Data["BeforeCommitID"] = beforeCommitID
|
||||
ctx.Data["AfterCommitID"] = afterCommitID
|
||||
ctx.Data["Username"] = userName
|
||||
ctx.Data["Reponame"] = repoName
|
||||
ctx.Data["IsImageFile"] = commit.IsImageFile
|
||||
ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID) + " · " + userName + "/" + repoName
|
||||
ctx.Data["Commit"] = commit
|
||||
ctx.Data["Diff"] = diff
|
||||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
|
||||
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", afterCommitID)
|
||||
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "src", "commit", beforeCommitID)
|
||||
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(userName, repoName, "raw", "commit", afterCommitID)
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
ctx.HTML(200, tplDiff)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,337 @@
|
||||
// Copyright 2019 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 repo
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
tplCompare base.TplName = "repo/diff/compare"
|
||||
)
|
||||
|
||||
// ParseCompareInfo parse compare info between two commit for preparing comparing references
|
||||
func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
|
||||
baseRepo := ctx.Repo.Repository
|
||||
|
||||
// Get compared branches information
|
||||
// format: <base branch>...[<head repo>:]<head branch>
|
||||
// base<-head: master...head:feature
|
||||
// same repo: master...feature
|
||||
|
||||
var (
|
||||
headUser *models.User
|
||||
headBranch string
|
||||
isSameRepo bool
|
||||
infoPath string
|
||||
err error
|
||||
)
|
||||
infoPath = ctx.Params("*")
|
||||
infos := strings.Split(infoPath, "...")
|
||||
if len(infos) != 2 {
|
||||
log.Trace("ParseCompareInfo[%d]: not enough compared branches information %s", baseRepo.ID, infos)
|
||||
ctx.NotFound("CompareAndPullRequest", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
baseBranch := infos[0]
|
||||
ctx.Data["BaseBranch"] = baseBranch
|
||||
|
||||
// If there is no head repository, it means compare between same repository.
|
||||
headInfos := strings.Split(infos[1], ":")
|
||||
if len(headInfos) == 1 {
|
||||
isSameRepo = true
|
||||
headUser = ctx.Repo.Owner
|
||||
headBranch = headInfos[0]
|
||||
|
||||
} else if len(headInfos) == 2 {
|
||||
headUser, err = models.GetUserByName(headInfos[0])
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
ctx.NotFound("GetUserByName", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetUserByName", err)
|
||||
}
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
headBranch = headInfos[1]
|
||||
isSameRepo = headUser.ID == ctx.Repo.Owner.ID
|
||||
} else {
|
||||
ctx.NotFound("CompareAndPullRequest", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
ctx.Data["HeadUser"] = headUser
|
||||
ctx.Data["HeadBranch"] = headBranch
|
||||
ctx.Repo.PullRequest.SameRepo = isSameRepo
|
||||
|
||||
// Check if base branch is valid.
|
||||
baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(baseBranch)
|
||||
baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(baseBranch)
|
||||
baseIsTag := ctx.Repo.GitRepo.IsTagExist(baseBranch)
|
||||
if !baseIsCommit && !baseIsBranch && !baseIsTag {
|
||||
// Check if baseBranch is short sha commit hash
|
||||
if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(baseBranch); baseCommit != nil {
|
||||
baseBranch = baseCommit.ID.String()
|
||||
ctx.Data["BaseBranch"] = baseBranch
|
||||
baseIsCommit = true
|
||||
} else {
|
||||
ctx.NotFound("IsRefExist", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
}
|
||||
ctx.Data["BaseIsCommit"] = baseIsCommit
|
||||
ctx.Data["BaseIsBranch"] = baseIsBranch
|
||||
ctx.Data["BaseIsTag"] = baseIsTag
|
||||
|
||||
// Check if current user has fork of repository or in the same repository.
|
||||
headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID)
|
||||
if !has && !isSameRepo {
|
||||
ctx.Data["PageIsComparePull"] = false
|
||||
}
|
||||
|
||||
var headGitRepo *git.Repository
|
||||
if isSameRepo {
|
||||
headRepo = ctx.Repo.Repository
|
||||
headGitRepo = ctx.Repo.GitRepo
|
||||
ctx.Data["BaseName"] = headUser.Name
|
||||
} else {
|
||||
headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name))
|
||||
ctx.Data["BaseName"] = baseRepo.OwnerName
|
||||
if err != nil {
|
||||
ctx.ServerError("OpenRepository", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
}
|
||||
|
||||
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
|
||||
permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
if !permBase.CanRead(models.UnitTypeCode) {
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in baseRepo has Permissions: %-+v",
|
||||
ctx.User,
|
||||
baseRepo,
|
||||
permBase)
|
||||
}
|
||||
ctx.NotFound("ParseCompareInfo", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
// user should have permission to read headrepo's codes
|
||||
permHead, err := models.GetUserRepoPermission(headRepo, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
if !permHead.CanRead(models.UnitTypeCode) {
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Denied: User: %-v cannot read code in Repo: %-v\nUser in headRepo has Permissions: %-+v",
|
||||
ctx.User,
|
||||
headRepo,
|
||||
permHead)
|
||||
}
|
||||
ctx.NotFound("ParseCompareInfo", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
// Check if head branch is valid.
|
||||
headIsCommit := ctx.Repo.GitRepo.IsCommitExist(headBranch)
|
||||
headIsBranch := headGitRepo.IsBranchExist(headBranch)
|
||||
headIsTag := headGitRepo.IsTagExist(headBranch)
|
||||
if !headIsCommit && !headIsBranch && !headIsTag {
|
||||
// Check if headBranch is short sha commit hash
|
||||
if headCommit, _ := ctx.Repo.GitRepo.GetCommit(headBranch); headCommit != nil {
|
||||
headBranch = headCommit.ID.String()
|
||||
ctx.Data["HeadBranch"] = headBranch
|
||||
headIsCommit = true
|
||||
} else {
|
||||
ctx.NotFound("IsRefExist", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
}
|
||||
ctx.Data["HeadIsCommit"] = headIsCommit
|
||||
ctx.Data["HeadIsBranch"] = headIsBranch
|
||||
ctx.Data["HeadIsTag"] = headIsTag
|
||||
|
||||
// Treat as pull request if both references are branches
|
||||
if ctx.Data["PageIsComparePull"] == nil {
|
||||
ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch
|
||||
}
|
||||
|
||||
if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) {
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Denied: User: %-v cannot create/read pull requests in Repo: %-v\nUser in baseRepo has Permissions: %-+v",
|
||||
ctx.User,
|
||||
baseRepo,
|
||||
permBase)
|
||||
}
|
||||
ctx.NotFound("ParseCompareInfo", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCompareInfo", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
ctx.Data["BeforeCommitID"] = compareInfo.MergeBase
|
||||
|
||||
return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch
|
||||
}
|
||||
|
||||
// PrepareCompareDiff renders compare diff page
|
||||
func PrepareCompareDiff(
|
||||
ctx *context.Context,
|
||||
headUser *models.User,
|
||||
headRepo *models.Repository,
|
||||
headGitRepo *git.Repository,
|
||||
compareInfo *git.CompareInfo,
|
||||
baseBranch, headBranch string) bool {
|
||||
|
||||
var (
|
||||
repo = ctx.Repo.Repository
|
||||
err error
|
||||
title string
|
||||
)
|
||||
|
||||
// Get diff information.
|
||||
ctx.Data["CommitRepoLink"] = headRepo.Link()
|
||||
|
||||
headCommitID := headBranch
|
||||
if ctx.Data["HeadIsCommit"] == false {
|
||||
if ctx.Data["HeadIsTag"] == true {
|
||||
headCommitID, err = headGitRepo.GetTagCommitID(headBranch)
|
||||
} else {
|
||||
headCommitID, err = headGitRepo.GetBranchCommitID(headBranch)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRefCommitID", err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["AfterCommitID"] = headCommitID
|
||||
|
||||
if headCommitID == compareInfo.MergeBase {
|
||||
ctx.Data["IsNothingToCompare"] = true
|
||||
return true
|
||||
}
|
||||
|
||||
diff, err := models.GetDiffRange(models.RepoPath(headUser.Name, headRepo.Name),
|
||||
compareInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines,
|
||||
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetDiffRange", err)
|
||||
return false
|
||||
}
|
||||
ctx.Data["Diff"] = diff
|
||||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
|
||||
|
||||
headCommit, err := headGitRepo.GetCommit(headCommitID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommit", err)
|
||||
return false
|
||||
}
|
||||
|
||||
compareInfo.Commits = models.ValidateCommitsWithEmails(compareInfo.Commits)
|
||||
compareInfo.Commits = models.ParseCommitsWithSignature(compareInfo.Commits)
|
||||
compareInfo.Commits = models.ParseCommitsWithStatus(compareInfo.Commits, headRepo)
|
||||
ctx.Data["Commits"] = compareInfo.Commits
|
||||
ctx.Data["CommitCount"] = compareInfo.Commits.Len()
|
||||
if ctx.Data["CommitCount"] == 0 {
|
||||
ctx.Data["PageIsComparePull"] = false
|
||||
}
|
||||
|
||||
if compareInfo.Commits.Len() == 1 {
|
||||
c := compareInfo.Commits.Front().Value.(models.SignCommitWithStatuses)
|
||||
title = strings.TrimSpace(c.UserCommit.Summary())
|
||||
|
||||
body := strings.Split(strings.TrimSpace(c.UserCommit.Message()), "\n")
|
||||
if len(body) > 1 {
|
||||
ctx.Data["content"] = strings.Join(body[1:], "\n")
|
||||
}
|
||||
} else {
|
||||
title = headBranch
|
||||
}
|
||||
|
||||
ctx.Data["title"] = title
|
||||
ctx.Data["Username"] = headUser.Name
|
||||
ctx.Data["Reponame"] = headRepo.Name
|
||||
ctx.Data["IsImageFile"] = headCommit.IsImageFile
|
||||
|
||||
headTarget := path.Join(headUser.Name, repo.Name)
|
||||
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", headCommitID)
|
||||
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", compareInfo.MergeBase)
|
||||
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", "commit", headCommitID)
|
||||
return false
|
||||
}
|
||||
|
||||
// CompareDiff show different from one commit to another commit
|
||||
func CompareDiff(ctx *context.Context) {
|
||||
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Data["PageIsComparePull"] == true {
|
||||
pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
|
||||
if err != nil {
|
||||
if !models.IsErrPullRequestNotExist(err) {
|
||||
ctx.ServerError("GetUnmergedPullRequest", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Data["HasPullRequest"] = true
|
||||
ctx.Data["PullRequest"] = pr
|
||||
ctx.HTML(200, tplCompareDiff)
|
||||
return
|
||||
}
|
||||
|
||||
if !nothingToCompare {
|
||||
// Setup information for new form.
|
||||
RetrieveRepoMetas(ctx, ctx.Repo.Repository)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
headBranches, err := headGitRepo.GetBranches()
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranches", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["HeadBranches"] = headBranches
|
||||
}
|
||||
beforeCommitID := ctx.Data["BeforeCommitID"].(string)
|
||||
afterCommitID := ctx.Data["AfterCommitID"].(string)
|
||||
|
||||
ctx.Data["Title"] = "Comparing " + base.ShortSha(beforeCommitID) + "..." + base.ShortSha(afterCommitID)
|
||||
|
||||
ctx.Data["IsRepoToolbarCommits"] = true
|
||||
ctx.Data["IsDiffCompare"] = true
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
ctx.Data["RequireTribute"] = true
|
||||
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
|
||||
setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
|
||||
renderAttachmentSettings(ctx)
|
||||
|
||||
ctx.HTML(200, tplCompare)
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/lfs"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// ServeData download file from io.Reader
|
||||
@@ -39,8 +40,11 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error {
|
||||
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name))
|
||||
}
|
||||
|
||||
ctx.Resp.Write(buf)
|
||||
_, err := io.Copy(ctx.Resp, reader)
|
||||
_, err := ctx.Resp.Write(buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(ctx.Resp, reader)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -50,7 +54,11 @@ func ServeBlob(ctx *context.Context, blob *git.Blob) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dataRc.Close()
|
||||
defer func() {
|
||||
if err = dataRc.Close(); err != nil {
|
||||
log.Error("ServeBlob: Close: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return ServeData(ctx, ctx.Repo.TreePath, dataRc)
|
||||
}
|
||||
@@ -61,7 +69,11 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dataRc.Close()
|
||||
defer func() {
|
||||
if err = dataRc.Close(); err != nil {
|
||||
log.Error("ServeBlobOrLFS: Close: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if meta, _ := lfs.ReadPointerFile(dataRc); meta != nil {
|
||||
meta, _ = ctx.Repo.Repository.GetLFSMetaObjectByOid(meta.Oid)
|
||||
|
||||
+5
-24
@@ -7,7 +7,6 @@ package repo
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
@@ -20,6 +19,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/upload"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
@@ -118,9 +118,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
||||
d, _ := ioutil.ReadAll(dataRc)
|
||||
buf = append(buf, d...)
|
||||
if content, err := templates.ToUTF8WithErr(buf); err != nil {
|
||||
if err != nil {
|
||||
log.Error("ToUTF8WithErr: %v", err)
|
||||
}
|
||||
log.Error("ToUTF8WithErr: %v", err)
|
||||
ctx.Data["FileContent"] = string(buf)
|
||||
} else {
|
||||
ctx.Data["FileContent"] = content
|
||||
@@ -235,16 +233,12 @@ func editFilePost(ctx *context.Context, form auth.EditRepoFileForm, isNewFile bo
|
||||
switch fileErr.Type {
|
||||
case git.EntryModeSymlink:
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplEditFile, &form)
|
||||
break
|
||||
case git.EntryModeTree:
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_a_directory", fileErr.Path), tplEditFile, &form)
|
||||
break
|
||||
case git.EntryModeBlob:
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path), tplEditFile, &form)
|
||||
break
|
||||
default:
|
||||
ctx.Error(500, err.Error())
|
||||
break
|
||||
}
|
||||
} else {
|
||||
ctx.Error(500, err.Error())
|
||||
@@ -403,16 +397,12 @@ func DeleteFilePost(ctx *context.Context, form auth.DeleteRepoFileForm) {
|
||||
switch fileErr.Type {
|
||||
case git.EntryModeSymlink:
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_is_a_symlink", fileErr.Path), tplEditFile, &form)
|
||||
break
|
||||
case git.EntryModeTree:
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.filename_is_a_directory", fileErr.Path), tplEditFile, &form)
|
||||
break
|
||||
case git.EntryModeBlob:
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.directory_is_a_file", fileErr.Path), tplEditFile, &form)
|
||||
break
|
||||
default:
|
||||
ctx.ServerError("DeleteRepoFile", err)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
ctx.ServerError("DeleteRepoFile", err)
|
||||
@@ -604,20 +594,11 @@ func UploadFileToServer(ctx *context.Context) {
|
||||
if n > 0 {
|
||||
buf = buf[:n]
|
||||
}
|
||||
fileType := http.DetectContentType(buf)
|
||||
|
||||
if len(setting.Repository.Upload.AllowedTypes) > 0 {
|
||||
allowed := false
|
||||
for _, t := range setting.Repository.Upload.AllowedTypes {
|
||||
t := strings.Trim(t, " ")
|
||||
if t == "*/*" || t == fileType {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
ctx.Error(400, ErrFileTypeForbidden.Error())
|
||||
err = upload.VerifyAllowedContentType(buf, setting.Repository.Upload.AllowedTypes)
|
||||
if err != nil {
|
||||
ctx.Error(400, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
+52
-41
@@ -22,6 +22,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
@@ -206,10 +207,8 @@ func HTTP(ctx *context.Context) {
|
||||
if err = models.UpdateAccessToken(token); err != nil {
|
||||
ctx.ServerError("UpdateAccessToken", err)
|
||||
}
|
||||
} else {
|
||||
if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
|
||||
log.Error("GetAccessTokenBySha: %v", err)
|
||||
}
|
||||
} else if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
|
||||
log.Error("GetAccessTokenBySha: %v", err)
|
||||
}
|
||||
|
||||
if authUser == nil {
|
||||
@@ -332,32 +331,24 @@ type route struct {
|
||||
}
|
||||
|
||||
var routes = []route{
|
||||
{regexp.MustCompile("(.*?)/git-upload-pack$"), "POST", serviceUploadPack},
|
||||
{regexp.MustCompile("(.*?)/git-receive-pack$"), "POST", serviceReceivePack},
|
||||
{regexp.MustCompile("(.*?)/info/refs$"), "GET", getInfoRefs},
|
||||
{regexp.MustCompile("(.*?)/HEAD$"), "GET", getTextFile},
|
||||
{regexp.MustCompile("(.*?)/objects/info/alternates$"), "GET", getTextFile},
|
||||
{regexp.MustCompile("(.*?)/objects/info/http-alternates$"), "GET", getTextFile},
|
||||
{regexp.MustCompile("(.*?)/objects/info/packs$"), "GET", getInfoPacks},
|
||||
{regexp.MustCompile("(.*?)/objects/info/[^/]*$"), "GET", getTextFile},
|
||||
{regexp.MustCompile("(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$"), "GET", getLooseObject},
|
||||
{regexp.MustCompile("(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$"), "GET", getPackFile},
|
||||
{regexp.MustCompile("(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$"), "GET", getIdxFile},
|
||||
}
|
||||
|
||||
// FIXME: use process module
|
||||
func gitCommand(dir string, args ...string) []byte {
|
||||
cmd := exec.Command("git", args...)
|
||||
cmd.Dir = dir
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.GitLogger.Error(fmt.Sprintf("%v - %s", err, out))
|
||||
}
|
||||
return out
|
||||
{regexp.MustCompile(`(.*?)/git-upload-pack$`), "POST", serviceUploadPack},
|
||||
{regexp.MustCompile(`(.*?)/git-receive-pack$`), "POST", serviceReceivePack},
|
||||
{regexp.MustCompile(`(.*?)/info/refs$`), "GET", getInfoRefs},
|
||||
{regexp.MustCompile(`(.*?)/HEAD$`), "GET", getTextFile},
|
||||
{regexp.MustCompile(`(.*?)/objects/info/alternates$`), "GET", getTextFile},
|
||||
{regexp.MustCompile(`(.*?)/objects/info/http-alternates$`), "GET", getTextFile},
|
||||
{regexp.MustCompile(`(.*?)/objects/info/packs$`), "GET", getInfoPacks},
|
||||
{regexp.MustCompile(`(.*?)/objects/info/[^/]*$`), "GET", getTextFile},
|
||||
{regexp.MustCompile(`(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$`), "GET", getLooseObject},
|
||||
{regexp.MustCompile(`(.*?)/objects/pack/pack-[0-9a-f]{40}\.pack$`), "GET", getPackFile},
|
||||
{regexp.MustCompile(`(.*?)/objects/pack/pack-[0-9a-f]{40}\.idx$`), "GET", getIdxFile},
|
||||
}
|
||||
|
||||
func getGitConfig(option, dir string) string {
|
||||
out := string(gitCommand(dir, "config", option))
|
||||
out, err := git.NewCommand("config", option).RunInDir(dir)
|
||||
if err != nil {
|
||||
log.Error("%v - %s", err, out)
|
||||
}
|
||||
return out[0 : len(out)-1]
|
||||
}
|
||||
|
||||
@@ -393,7 +384,12 @@ func hasAccess(service string, h serviceHandler, checkContentType bool) bool {
|
||||
}
|
||||
|
||||
func serviceRPC(h serviceHandler, service string) {
|
||||
defer h.r.Body.Close()
|
||||
defer func() {
|
||||
if err := h.r.Body.Close(); err != nil {
|
||||
log.Error("serviceRPC: Close: %v", err)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
if !hasAccess(service, h, true) {
|
||||
h.w.WriteHeader(http.StatusUnauthorized)
|
||||
@@ -409,7 +405,7 @@ func serviceRPC(h serviceHandler, service string) {
|
||||
if h.r.Header.Get("Content-Encoding") == "gzip" {
|
||||
reqBody, err = gzip.NewReader(reqBody)
|
||||
if err != nil {
|
||||
log.GitLogger.Error("Fail to create gzip reader: %v", err)
|
||||
log.Error("Fail to create gzip reader: %v", err)
|
||||
h.w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@@ -419,7 +415,7 @@ func serviceRPC(h serviceHandler, service string) {
|
||||
h.environ = append(h.environ, "SSH_ORIGINAL_COMMAND="+service)
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd := exec.Command("git", service, "--stateless-rpc", h.dir)
|
||||
cmd := exec.Command(git.GitExecutable, service, "--stateless-rpc", h.dir)
|
||||
cmd.Dir = h.dir
|
||||
if service == "receive-pack" {
|
||||
cmd.Env = append(os.Environ(), h.environ...)
|
||||
@@ -428,7 +424,7 @@ func serviceRPC(h serviceHandler, service string) {
|
||||
cmd.Stdin = reqBody
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.GitLogger.Error("Fail to serve RPC(%s): %v - %v", service, err, stderr)
|
||||
log.Error("Fail to serve RPC(%s): %v - %v", service, err, stderr)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -450,7 +446,11 @@ func getServiceType(r *http.Request) string {
|
||||
}
|
||||
|
||||
func updateServerInfo(dir string) []byte {
|
||||
return gitCommand(dir, "update-server-info")
|
||||
out, err := git.NewCommand("update-server-info").RunInDirBytes(dir)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("%v - %s", err, string(out)))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func packetWrite(str string) []byte {
|
||||
@@ -465,13 +465,16 @@ func getInfoRefs(h serviceHandler) {
|
||||
h.setHeaderNoCache()
|
||||
if hasAccess(getServiceType(h.r), h, false) {
|
||||
service := getServiceType(h.r)
|
||||
refs := gitCommand(h.dir, service, "--stateless-rpc", "--advertise-refs", ".")
|
||||
refs, err := git.NewCommand(service, "--stateless-rpc", "--advertise-refs", ".").RunInDirBytes(h.dir)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("%v - %s", err, string(refs)))
|
||||
}
|
||||
|
||||
h.w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-advertisement", service))
|
||||
h.w.WriteHeader(http.StatusOK)
|
||||
h.w.Write(packetWrite("# service=git-" + service + "\n"))
|
||||
h.w.Write([]byte("0000"))
|
||||
h.w.Write(refs)
|
||||
_, _ = h.w.Write(packetWrite("# service=git-" + service + "\n"))
|
||||
_, _ = h.w.Write([]byte("0000"))
|
||||
_, _ = h.w.Write(refs)
|
||||
} else {
|
||||
updateServerInfo(h.dir)
|
||||
h.sendFile("text/plain; charset=utf-8")
|
||||
@@ -524,16 +527,25 @@ func HTTPBackend(ctx *context.Context, cfg *serviceConfig) http.HandlerFunc {
|
||||
if m := route.reg.FindStringSubmatch(r.URL.Path); m != nil {
|
||||
if setting.Repository.DisableHTTPGit {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Write([]byte("Interacting with repositories by HTTP protocol is not allowed"))
|
||||
_, err := w.Write([]byte("Interacting with repositories by HTTP protocol is not allowed"))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
if route.method != r.Method {
|
||||
if r.Proto == "HTTP/1.1" {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
w.Write([]byte("Method Not Allowed"))
|
||||
_, err := w.Write([]byte("Method Not Allowed"))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte("Bad Request"))
|
||||
_, err := w.Write([]byte("Bad Request"))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -541,7 +553,7 @@ func HTTPBackend(ctx *context.Context, cfg *serviceConfig) http.HandlerFunc {
|
||||
file := strings.Replace(r.URL.Path, m[1]+"/", "", 1)
|
||||
dir, err := getGitRepoPath(m[1])
|
||||
if err != nil {
|
||||
log.GitLogger.Error(err.Error())
|
||||
log.Error(err.Error())
|
||||
ctx.NotFound("HTTPBackend", err)
|
||||
return
|
||||
}
|
||||
@@ -552,6 +564,5 @@ func HTTPBackend(ctx *context.Context, cfg *serviceConfig) http.HandlerFunc {
|
||||
}
|
||||
|
||||
ctx.NotFound("HTTPBackend", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
+20
-10
@@ -24,6 +24,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
@@ -40,8 +41,6 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrFileTypeForbidden not allowed file type error
|
||||
ErrFileTypeForbidden = errors.New("File type is not allowed")
|
||||
// ErrTooManyFiles upload too many files
|
||||
ErrTooManyFiles = errors.New("Maximum number of files to upload exceeded")
|
||||
// IssueTemplateCandidates issue templates
|
||||
@@ -305,7 +304,7 @@ func Issues(ctx *context.Context) {
|
||||
|
||||
var err error
|
||||
// Get milestones.
|
||||
ctx.Data["Milestones"], err = models.GetMilestonesByRepoID(ctx.Repo.Repository.ID)
|
||||
ctx.Data["Milestones"], err = models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")))
|
||||
if err != nil {
|
||||
ctx.ServerError("GetAllRepoMilestones", err)
|
||||
return
|
||||
@@ -425,12 +424,14 @@ func NewIssue(ctx *context.Context) {
|
||||
ctx.Data["BodyQuery"] = body
|
||||
|
||||
milestoneID := ctx.QueryInt64("milestone")
|
||||
milestone, err := models.GetMilestoneByID(milestoneID)
|
||||
if err != nil {
|
||||
log.Error("GetMilestoneByID: %d: %v", milestoneID, err)
|
||||
} else {
|
||||
ctx.Data["milestone_id"] = milestoneID
|
||||
ctx.Data["Milestone"] = milestone
|
||||
if milestoneID > 0 {
|
||||
milestone, err := models.GetMilestoneByID(milestoneID)
|
||||
if err != nil {
|
||||
log.Error("GetMilestoneByID: %d: %v", milestoneID, err)
|
||||
} else {
|
||||
ctx.Data["milestone_id"] = milestoneID
|
||||
ctx.Data["Milestone"] = milestone
|
||||
}
|
||||
}
|
||||
|
||||
setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates)
|
||||
@@ -942,7 +943,15 @@ func ViewIssue(ctx *context.Context) {
|
||||
|
||||
// Get Dependencies
|
||||
ctx.Data["BlockedByDependencies"], err = issue.BlockedByDependencies()
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockedByDependencies", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["BlockingDependencies"], err = issue.BlockingDependencies()
|
||||
if err != nil {
|
||||
ctx.ServerError("BlockingDependencies", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Participants"] = participants
|
||||
ctx.Data["NumParticipants"] = len(participants)
|
||||
@@ -1223,7 +1232,8 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
|
||||
|
||||
if form.Status == "reopen" && issue.IsPull {
|
||||
pull := issue.PullRequest
|
||||
pr, err := models.GetUnmergedPullRequest(pull.HeadRepoID, pull.BaseRepoID, pull.HeadBranch, pull.BaseBranch)
|
||||
var err error
|
||||
pr, err = models.GetUnmergedPullRequest(pull.HeadRepoID, pull.BaseRepoID, pull.HeadBranch, pull.BaseBranch)
|
||||
if err != nil {
|
||||
if !models.IsErrPullRequestNotExist(err) {
|
||||
ctx.ServerError("GetUnmergedPullRequest", err)
|
||||
|
||||
@@ -129,7 +129,6 @@ func DeleteLabel(ctx *context.Context) {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"redirect": ctx.Repo.RepoLink + "/labels",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateIssueLabel change issue's labels
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
const (
|
||||
tplMilestone base.TplName = "repo/issue/milestones"
|
||||
tplMilestoneNew base.TplName = "repo/issue/milestone_new"
|
||||
tplMilestoneEdit base.TplName = "repo/issue/milestone_edit"
|
||||
tplMilestoneIssues base.TplName = "repo/issue/milestone_issues"
|
||||
)
|
||||
|
||||
@@ -57,7 +56,7 @@ func Milestones(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
if ctx.Repo.Repository.IsTimetrackerEnabled() {
|
||||
if miles.LoadTotalTrackedTimes(); err != nil {
|
||||
if err := miles.LoadTotalTrackedTimes(); err != nil {
|
||||
ctx.ServerError("LoadTotalTrackedTimes", err)
|
||||
return
|
||||
}
|
||||
|
||||
+53
-281
@@ -8,6 +8,7 @@ package repo
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/pull"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -28,7 +30,7 @@ import (
|
||||
|
||||
const (
|
||||
tplFork base.TplName = "repo/pulls/fork"
|
||||
tplComparePull base.TplName = "repo/pulls/compare"
|
||||
tplCompareDiff base.TplName = "repo/diff/compare"
|
||||
tplPullCommits base.TplName = "repo/pulls/commits"
|
||||
tplPullFiles base.TplName = "repo/pulls/files"
|
||||
|
||||
@@ -280,13 +282,13 @@ func setMergeTarget(ctx *context.Context, pull *models.PullRequest) {
|
||||
}
|
||||
|
||||
// PrepareMergedViewPullInfo show meta information for a merged pull request view page
|
||||
func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullRequestInfo {
|
||||
func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.CompareInfo {
|
||||
pull := issue.PullRequest
|
||||
|
||||
setMergeTarget(ctx, pull)
|
||||
ctx.Data["HasMerged"] = true
|
||||
|
||||
prInfo, err := ctx.Repo.GitRepo.GetPullRequestInfo(ctx.Repo.Repository.RepoPath(),
|
||||
compareInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(),
|
||||
pull.MergeBase, pull.GetGitRefName())
|
||||
|
||||
if err != nil {
|
||||
@@ -298,16 +300,16 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *models.Issue) *git.P
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.ServerError("GetPullRequestInfo", err)
|
||||
ctx.ServerError("GetCompareInfo", err)
|
||||
return nil
|
||||
}
|
||||
ctx.Data["NumCommits"] = prInfo.Commits.Len()
|
||||
ctx.Data["NumFiles"] = prInfo.NumFiles
|
||||
return prInfo
|
||||
ctx.Data["NumCommits"] = compareInfo.Commits.Len()
|
||||
ctx.Data["NumFiles"] = compareInfo.NumFiles
|
||||
return compareInfo
|
||||
}
|
||||
|
||||
// PrepareViewPullInfo show meta information for a pull request preview page
|
||||
func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullRequestInfo {
|
||||
func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.CompareInfo {
|
||||
repo := ctx.Repo.Repository
|
||||
pull := issue.PullRequest
|
||||
|
||||
@@ -320,15 +322,37 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullReq
|
||||
setMergeTarget(ctx, pull)
|
||||
|
||||
var headGitRepo *git.Repository
|
||||
var headBranchExist bool
|
||||
// HeadRepo may be missing
|
||||
if pull.HeadRepo != nil {
|
||||
headGitRepo, err = git.OpenRepository(pull.HeadRepo.RepoPath())
|
||||
if err != nil {
|
||||
ctx.ServerError("OpenRepository", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
headBranchExist = headGitRepo.IsBranchExist(pull.HeadBranch)
|
||||
|
||||
if headBranchExist {
|
||||
sha, err := headGitRepo.GetBranchCommitID(pull.HeadBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranchCommitID", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
commitStatuses, err := models.GetLatestCommitStatus(repo, sha, 0)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return nil
|
||||
}
|
||||
if len(commitStatuses) > 0 {
|
||||
ctx.Data["LatestCommitStatuses"] = commitStatuses
|
||||
ctx.Data["LatestCommitStatus"] = models.CalcCommitStatus(commitStatuses)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pull.HeadRepo == nil || !headGitRepo.IsBranchExist(pull.HeadBranch) {
|
||||
if pull.HeadRepo == nil || !headBranchExist {
|
||||
ctx.Data["IsPullRequestBroken"] = true
|
||||
ctx.Data["HeadTarget"] = "deleted"
|
||||
ctx.Data["NumCommits"] = 0
|
||||
@@ -336,7 +360,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullReq
|
||||
return nil
|
||||
}
|
||||
|
||||
prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(repo.Owner.Name, repo.Name),
|
||||
compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(repo.Owner.Name, repo.Name),
|
||||
pull.BaseBranch, pull.HeadBranch)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "fatal: Not a valid object name") {
|
||||
@@ -347,7 +371,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullReq
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.ServerError("GetPullRequestInfo", err)
|
||||
ctx.ServerError("GetCompareInfo", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -361,9 +385,9 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.PullReq
|
||||
ctx.Data["ConflictedFiles"] = pull.ConflictedFiles
|
||||
}
|
||||
|
||||
ctx.Data["NumCommits"] = prInfo.Commits.Len()
|
||||
ctx.Data["NumFiles"] = prInfo.NumFiles
|
||||
return prInfo
|
||||
ctx.Data["NumCommits"] = compareInfo.Commits.Len()
|
||||
ctx.Data["NumFiles"] = compareInfo.NumFiles
|
||||
return compareInfo
|
||||
}
|
||||
|
||||
// ViewPullCommits show commits for a pull request
|
||||
@@ -596,7 +620,7 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = pr.Merge(ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
|
||||
if err = pull.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
@@ -628,266 +652,6 @@ func stopTimerIfAvailable(user *models.User, issue *models.Issue) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseCompareInfo parse compare info between two commit for preparing pull request
|
||||
func ParseCompareInfo(ctx *context.Context) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
|
||||
baseRepo := ctx.Repo.Repository
|
||||
|
||||
// Get compared branches information
|
||||
// format: <base branch>...[<head repo>:]<head branch>
|
||||
// base<-head: master...head:feature
|
||||
// same repo: master...feature
|
||||
|
||||
var (
|
||||
headUser *models.User
|
||||
headBranch string
|
||||
isSameRepo bool
|
||||
infoPath string
|
||||
err error
|
||||
)
|
||||
infoPath = ctx.Params("*")
|
||||
infos := strings.Split(infoPath, "...")
|
||||
if len(infos) != 2 {
|
||||
log.Trace("ParseCompareInfo[%d]: not enough compared branches information %s", baseRepo.ID, infos)
|
||||
ctx.NotFound("CompareAndPullRequest", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
baseBranch := infos[0]
|
||||
ctx.Data["BaseBranch"] = baseBranch
|
||||
|
||||
// If there is no head repository, it means pull request between same repository.
|
||||
headInfos := strings.Split(infos[1], ":")
|
||||
if len(headInfos) == 1 {
|
||||
isSameRepo = true
|
||||
headUser = ctx.Repo.Owner
|
||||
headBranch = headInfos[0]
|
||||
|
||||
} else if len(headInfos) == 2 {
|
||||
headUser, err = models.GetUserByName(headInfos[0])
|
||||
if err != nil {
|
||||
if models.IsErrUserNotExist(err) {
|
||||
ctx.NotFound("GetUserByName", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetUserByName", err)
|
||||
}
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
headBranch = headInfos[1]
|
||||
isSameRepo = headUser.ID == ctx.Repo.Owner.ID
|
||||
} else {
|
||||
ctx.NotFound("CompareAndPullRequest", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
ctx.Data["HeadUser"] = headUser
|
||||
ctx.Data["HeadBranch"] = headBranch
|
||||
ctx.Repo.PullRequest.SameRepo = isSameRepo
|
||||
|
||||
// Check if base branch is valid.
|
||||
if !ctx.Repo.GitRepo.IsBranchExist(baseBranch) {
|
||||
ctx.NotFound("IsBranchExist", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
// Check if current user has fork of repository or in the same repository.
|
||||
headRepo, has := models.HasForkedRepo(headUser.ID, baseRepo.ID)
|
||||
if !has && !isSameRepo {
|
||||
log.Trace("ParseCompareInfo[%d]: does not have fork or in same repository", baseRepo.ID)
|
||||
ctx.NotFound("ParseCompareInfo", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
var headGitRepo *git.Repository
|
||||
if isSameRepo {
|
||||
headRepo = ctx.Repo.Repository
|
||||
headGitRepo = ctx.Repo.GitRepo
|
||||
ctx.Data["BaseName"] = headUser.Name
|
||||
} else {
|
||||
headGitRepo, err = git.OpenRepository(models.RepoPath(headUser.Name, headRepo.Name))
|
||||
ctx.Data["BaseName"] = baseRepo.OwnerName
|
||||
if err != nil {
|
||||
ctx.ServerError("OpenRepository", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
}
|
||||
|
||||
// user should have permission to read baseRepo's codes and pulls, NOT headRepo's
|
||||
permBase, err := models.GetUserRepoPermission(baseRepo, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
if !permBase.CanReadIssuesOrPulls(true) || !permBase.CanRead(models.UnitTypeCode) {
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Denied: User: %-v cannot create/read pull requests or cannot read code in Repo: %-v\nUser in baseRepo has Permissions: %-+v",
|
||||
ctx.User,
|
||||
baseRepo,
|
||||
permBase)
|
||||
}
|
||||
ctx.NotFound("ParseCompareInfo", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
// user should have permission to read headrepo's codes
|
||||
permHead, err := models.GetUserRepoPermission(headRepo, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
if !permHead.CanRead(models.UnitTypeCode) {
|
||||
if log.IsTrace() {
|
||||
log.Trace("Permission Denied: User: %-v cannot read code requests in Repo: %-v\nUser in headRepo has Permissions: %-+v",
|
||||
ctx.User,
|
||||
headRepo,
|
||||
permHead)
|
||||
}
|
||||
ctx.NotFound("ParseCompareInfo", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
// Check if head branch is valid.
|
||||
if !headGitRepo.IsBranchExist(headBranch) {
|
||||
ctx.NotFound("IsBranchExist", nil)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
headBranches, err := headGitRepo.GetBranches()
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranches", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
ctx.Data["HeadBranches"] = headBranches
|
||||
|
||||
prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPullRequestInfo", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
ctx.Data["BeforeCommitID"] = prInfo.MergeBase
|
||||
|
||||
return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch
|
||||
}
|
||||
|
||||
// PrepareCompareDiff render pull request preview diff page
|
||||
func PrepareCompareDiff(
|
||||
ctx *context.Context,
|
||||
headUser *models.User,
|
||||
headRepo *models.Repository,
|
||||
headGitRepo *git.Repository,
|
||||
prInfo *git.PullRequestInfo,
|
||||
baseBranch, headBranch string) bool {
|
||||
|
||||
var (
|
||||
repo = ctx.Repo.Repository
|
||||
err error
|
||||
title string
|
||||
)
|
||||
|
||||
// Get diff information.
|
||||
ctx.Data["CommitRepoLink"] = headRepo.Link()
|
||||
|
||||
headCommitID, err := headGitRepo.GetBranchCommitID(headBranch)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetBranchCommitID", err)
|
||||
return false
|
||||
}
|
||||
ctx.Data["AfterCommitID"] = headCommitID
|
||||
|
||||
if headCommitID == prInfo.MergeBase {
|
||||
ctx.Data["IsNothingToCompare"] = true
|
||||
return true
|
||||
}
|
||||
|
||||
diff, err := models.GetDiffRange(models.RepoPath(headUser.Name, headRepo.Name),
|
||||
prInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines,
|
||||
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetDiffRange", err)
|
||||
return false
|
||||
}
|
||||
ctx.Data["Diff"] = diff
|
||||
ctx.Data["DiffNotAvailable"] = diff.NumFiles() == 0
|
||||
|
||||
headCommit, err := headGitRepo.GetCommit(headCommitID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommit", err)
|
||||
return false
|
||||
}
|
||||
|
||||
prInfo.Commits = models.ValidateCommitsWithEmails(prInfo.Commits)
|
||||
prInfo.Commits = models.ParseCommitsWithSignature(prInfo.Commits)
|
||||
prInfo.Commits = models.ParseCommitsWithStatus(prInfo.Commits, headRepo)
|
||||
ctx.Data["Commits"] = prInfo.Commits
|
||||
ctx.Data["CommitCount"] = prInfo.Commits.Len()
|
||||
|
||||
if prInfo.Commits.Len() == 1 {
|
||||
c := prInfo.Commits.Front().Value.(models.SignCommitWithStatuses)
|
||||
title = strings.TrimSpace(c.UserCommit.Summary())
|
||||
|
||||
body := strings.Split(strings.TrimSpace(c.UserCommit.Message()), "\n")
|
||||
if len(body) > 1 {
|
||||
ctx.Data["content"] = strings.Join(body[1:], "\n")
|
||||
}
|
||||
} else {
|
||||
title = headBranch
|
||||
}
|
||||
|
||||
ctx.Data["title"] = title
|
||||
ctx.Data["Username"] = headUser.Name
|
||||
ctx.Data["Reponame"] = headRepo.Name
|
||||
ctx.Data["IsImageFile"] = headCommit.IsImageFile
|
||||
|
||||
headTarget := path.Join(headUser.Name, repo.Name)
|
||||
ctx.Data["SourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", headCommitID)
|
||||
ctx.Data["BeforeSourcePath"] = setting.AppSubURL + "/" + path.Join(headTarget, "src", "commit", prInfo.MergeBase)
|
||||
ctx.Data["RawPath"] = setting.AppSubURL + "/" + path.Join(headTarget, "raw", "commit", headCommitID)
|
||||
return false
|
||||
}
|
||||
|
||||
// CompareAndPullRequest render pull request preview page
|
||||
func CompareAndPullRequest(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
|
||||
ctx.Data["PageIsComparePull"] = true
|
||||
ctx.Data["IsDiffCompare"] = true
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
ctx.Data["RequireTribute"] = true
|
||||
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
|
||||
setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
|
||||
renderAttachmentSettings(ctx)
|
||||
|
||||
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := ParseCompareInfo(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
pr, err := models.GetUnmergedPullRequest(headRepo.ID, ctx.Repo.Repository.ID, headBranch, baseBranch)
|
||||
if err != nil {
|
||||
if !models.IsErrPullRequestNotExist(err) {
|
||||
ctx.ServerError("GetUnmergedPullRequest", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Data["HasPullRequest"] = true
|
||||
ctx.Data["PullRequest"] = pr
|
||||
ctx.HTML(200, tplComparePull)
|
||||
return
|
||||
}
|
||||
|
||||
nothingToCompare := PrepareCompareDiff(ctx, headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if !nothingToCompare {
|
||||
// Setup information for new form.
|
||||
RetrieveRepoMetas(ctx, ctx.Repo.Repository)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.HTML(200, tplComparePull)
|
||||
}
|
||||
|
||||
// CompareAndPullRequestPost response for creating pull request
|
||||
func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.pulls.compare_changes")
|
||||
@@ -926,7 +690,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.HTML(200, tplComparePull)
|
||||
ctx.HTML(200, tplCompareDiff)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -936,7 +700,7 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplComparePull, form)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.issues.new.title_empty"), tplCompareDiff, form)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -946,9 +710,15 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
|
||||
return
|
||||
}
|
||||
|
||||
maxIndex, err := models.GetMaxIndexOfIssue(repo.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPatch", err)
|
||||
return
|
||||
}
|
||||
|
||||
pullIssue := &models.Issue{
|
||||
RepoID: repo.ID,
|
||||
Index: repo.NextIssueIndex(),
|
||||
Index: maxIndex + 1,
|
||||
Title: form.Title,
|
||||
PosterID: ctx.User.ID,
|
||||
Poster: ctx.User,
|
||||
@@ -1002,7 +772,9 @@ func TriggerTask(ctx *context.Context) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if secret != base.EncodeMD5(owner.Salt) {
|
||||
got := []byte(base.EncodeMD5(owner.Salt))
|
||||
want := []byte(secret)
|
||||
if subtle.ConstantTimeCompare(got, want) != 1 {
|
||||
ctx.Error(404)
|
||||
log.Trace("TriggerTask [%s/%s]: invalid secret", owner.Name, repo.Name)
|
||||
return
|
||||
@@ -1047,10 +819,10 @@ func CleanUpPullRequest(ctx *context.Context) {
|
||||
// Forked repository has already been deleted
|
||||
ctx.NotFound("CleanUpPullRequest", nil)
|
||||
return
|
||||
} else if pr.GetBaseRepo(); err != nil {
|
||||
} else if err = pr.GetBaseRepo(); err != nil {
|
||||
ctx.ServerError("GetBaseRepo", err)
|
||||
return
|
||||
} else if pr.HeadRepo.GetOwner(); err != nil {
|
||||
} else if err = pr.HeadRepo.GetOwner(); err != nil {
|
||||
ctx.ServerError("HeadRepo.GetOwner", err)
|
||||
return
|
||||
}
|
||||
|
||||
+64
-2
@@ -7,11 +7,14 @@ package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Unknwon/com"
|
||||
"mvdan.cc/xurls/v2"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
@@ -246,7 +249,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
||||
ctx.Redirect(repo.Link() + "/settings")
|
||||
return
|
||||
}
|
||||
if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalURL(form.TrackerURLFormat) {
|
||||
if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error"))
|
||||
ctx.Redirect(repo.Link() + "/settings")
|
||||
return
|
||||
@@ -416,7 +419,10 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
|
||||
return
|
||||
}
|
||||
|
||||
repo.DeleteWiki()
|
||||
err := repo.DeleteWiki()
|
||||
if err != nil {
|
||||
log.Error("Delete Wiki: %v", err.Error())
|
||||
}
|
||||
log.Trace("Repository wiki deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.wiki_deletion_success"))
|
||||
@@ -727,3 +733,59 @@ func init() {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateAvatarSetting update repo's avatar
|
||||
func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm) error {
|
||||
ctxRepo := ctx.Repo.Repository
|
||||
|
||||
if form.Avatar == nil {
|
||||
// No avatar is uploaded and we not removing it here.
|
||||
// No random avatar generated here.
|
||||
// Just exit, no action.
|
||||
if !com.IsFile(ctxRepo.CustomAvatarPath()) {
|
||||
log.Trace("No avatar was uploaded for repo: %d. Default icon will appear instead.", ctxRepo.ID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
r, err := form.Avatar.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Avatar.Open: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
if form.Avatar.Size > setting.AvatarMaxFileSize {
|
||||
return errors.New(ctx.Tr("settings.uploaded_avatar_is_too_big"))
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ioutil.ReadAll: %v", err)
|
||||
}
|
||||
if !base.IsImageFile(data) {
|
||||
return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image"))
|
||||
}
|
||||
if err = ctxRepo.UploadAvatar(data); err != nil {
|
||||
return fmt.Errorf("UploadAvatar: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SettingsAvatar save new POSTed repository avatar
|
||||
func SettingsAvatar(ctx *context.Context, form auth.AvatarForm) {
|
||||
form.Source = auth.AvatarLocal
|
||||
if err := UpdateAvatarSetting(ctx, form); err != nil {
|
||||
ctx.Flash.Error(err.Error())
|
||||
} else {
|
||||
ctx.Flash.Success(ctx.Tr("repo.settings.update_avatar_success"))
|
||||
}
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
}
|
||||
|
||||
// SettingsDeleteAvatar delete repository avatar
|
||||
func SettingsDeleteAvatar(ctx *context.Context) {
|
||||
if err := ctx.Repo.Repository.DeleteAvatar(); err != nil {
|
||||
ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err))
|
||||
}
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
|
||||
}
|
||||
|
||||
@@ -294,9 +294,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||
// Building code view blocks with line number on server side.
|
||||
var fileContent string
|
||||
if content, err := templates.ToUTF8WithErr(buf); err != nil {
|
||||
if err != nil {
|
||||
log.Error("ToUTF8WithErr: %v", err)
|
||||
}
|
||||
log.Error("ToUTF8WithErr: %v", err)
|
||||
fileContent = string(buf)
|
||||
} else {
|
||||
fileContent = content
|
||||
|
||||
+12
-3
@@ -197,12 +197,20 @@ func WebHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
|
||||
}
|
||||
|
||||
// GogsHooksNewPost response for creating webhook
|
||||
func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) {
|
||||
func GogsHooksNewPost(ctx *context.Context, form auth.NewWebhookForm) {
|
||||
newGenericWebhookPost(ctx, form, models.GOGS)
|
||||
}
|
||||
|
||||
func newGenericWebhookPost(ctx *context.Context, form auth.NewWebhookForm, kind models.HookTaskType) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
|
||||
ctx.Data["PageIsSettingsHooks"] = true
|
||||
ctx.Data["PageIsSettingsHooksNew"] = true
|
||||
ctx.Data["Webhook"] = models.Webhook{HookEvent: &models.HookEvent{}}
|
||||
ctx.Data["HookType"] = "gogs"
|
||||
|
||||
ctx.Data["HookType"] = "gitea"
|
||||
if kind == models.GOGS {
|
||||
ctx.Data["HookType"] = "gogs"
|
||||
}
|
||||
|
||||
orCtx, err := getOrgRepoCtx(ctx)
|
||||
if err != nil {
|
||||
@@ -228,7 +236,7 @@ func GogsHooksNewPost(ctx *context.Context, form auth.NewGogshookForm) {
|
||||
Secret: form.Secret,
|
||||
HookEvent: ParseHookEvent(form.WebhookForm),
|
||||
IsActive: form.Active,
|
||||
HookTaskType: models.GOGS,
|
||||
HookTaskType: kind,
|
||||
OrgID: orCtx.OrgID,
|
||||
}
|
||||
if err := w.UpdateEvent(); err != nil {
|
||||
@@ -564,6 +572,7 @@ func WebHooksEditPost(ctx *context.Context, form auth.NewWebhookForm) {
|
||||
w.Secret = form.Secret
|
||||
w.HookEvent = ParseHookEvent(form.WebhookForm)
|
||||
w.IsActive = form.Active
|
||||
w.HTTPMethod = form.HTTPMethod
|
||||
if err := w.UpdateEvent(); err != nil {
|
||||
ctx.ServerError("UpdateEvent", err)
|
||||
return
|
||||
|
||||
+195
-71
@@ -23,10 +23,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tplWikiStart base.TplName = "repo/wiki/start"
|
||||
tplWikiView base.TplName = "repo/wiki/view"
|
||||
tplWikiNew base.TplName = "repo/wiki/new"
|
||||
tplWikiPages base.TplName = "repo/wiki/pages"
|
||||
tplWikiStart base.TplName = "repo/wiki/start"
|
||||
tplWikiView base.TplName = "repo/wiki/view"
|
||||
tplWikiRevision base.TplName = "repo/wiki/revision"
|
||||
tplWikiNew base.TplName = "repo/wiki/new"
|
||||
tplWikiPages base.TplName = "repo/wiki/pages"
|
||||
)
|
||||
|
||||
// MustEnableWiki check if wiki is enabled, if external then redirect
|
||||
@@ -107,18 +108,20 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
|
||||
|
||||
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
||||
// indicating whether the page exists. Writes to ctx if an error occurs.
|
||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, bool) {
|
||||
entry, err := findEntryForFile(commit, models.WikiNameToFilename(wikiName))
|
||||
if err != nil {
|
||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
|
||||
var entry *git.TreeEntry
|
||||
var err error
|
||||
pageFilename := models.WikiNameToFilename(wikiName)
|
||||
if entry, err = findEntryForFile(commit, pageFilename); err != nil {
|
||||
ctx.ServerError("findEntryForFile", err)
|
||||
return nil, false
|
||||
return nil, nil, "", false
|
||||
} else if entry == nil {
|
||||
return nil, false
|
||||
return nil, nil, "", true
|
||||
}
|
||||
return wikiContentsByEntry(ctx, entry), true
|
||||
return wikiContentsByEntry(ctx, entry), entry, pageFilename, false
|
||||
}
|
||||
|
||||
func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *git.TreeEntry) {
|
||||
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
wikiRepo, commit, err := findWikiRepoCommit(ctx)
|
||||
if err != nil {
|
||||
if !git.IsErrNotExist(err) {
|
||||
@@ -128,88 +131,176 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi
|
||||
}
|
||||
|
||||
// Get page list.
|
||||
if isViewPage {
|
||||
entries, err := commit.ListEntries()
|
||||
if err != nil {
|
||||
ctx.ServerError("ListEntries", err)
|
||||
return nil, nil
|
||||
}
|
||||
pages := make([]PageMeta, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if !entry.IsRegular() {
|
||||
continue
|
||||
}
|
||||
wikiName, err := models.WikiFilenameToName(entry.Name())
|
||||
if err != nil {
|
||||
if models.IsErrWikiInvalidFileName(err) {
|
||||
continue
|
||||
}
|
||||
ctx.ServerError("WikiFilenameToName", err)
|
||||
return nil, nil
|
||||
} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
|
||||
continue
|
||||
}
|
||||
pages = append(pages, PageMeta{
|
||||
Name: wikiName,
|
||||
SubURL: models.WikiNameToSubURL(wikiName),
|
||||
})
|
||||
}
|
||||
ctx.Data["Pages"] = pages
|
||||
entries, err := commit.ListEntries()
|
||||
if err != nil {
|
||||
ctx.ServerError("ListEntries", err)
|
||||
return nil, nil
|
||||
}
|
||||
pages := make([]PageMeta, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if !entry.IsRegular() {
|
||||
continue
|
||||
}
|
||||
wikiName, err := models.WikiFilenameToName(entry.Name())
|
||||
if err != nil {
|
||||
if models.IsErrWikiInvalidFileName(err) {
|
||||
continue
|
||||
}
|
||||
ctx.ServerError("WikiFilenameToName", err)
|
||||
return nil, nil
|
||||
} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
|
||||
continue
|
||||
}
|
||||
pages = append(pages, PageMeta{
|
||||
Name: wikiName,
|
||||
SubURL: models.WikiNameToSubURL(wikiName),
|
||||
})
|
||||
}
|
||||
ctx.Data["Pages"] = pages
|
||||
|
||||
// get requested pagename
|
||||
pageName := models.NormalizeWikiName(ctx.Params(":page"))
|
||||
if len(pageName) == 0 {
|
||||
pageName = "Home"
|
||||
}
|
||||
ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName)
|
||||
|
||||
ctx.Data["old_title"] = pageName
|
||||
ctx.Data["Title"] = pageName
|
||||
ctx.Data["title"] = pageName
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
|
||||
pageFilename := models.WikiNameToFilename(pageName)
|
||||
var entry *git.TreeEntry
|
||||
if entry, err = findEntryForFile(commit, pageFilename); err != nil {
|
||||
ctx.ServerError("findEntryForFile", err)
|
||||
return nil, nil
|
||||
} else if entry == nil {
|
||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
||||
}
|
||||
if entry == nil || ctx.Written() {
|
||||
return nil, nil
|
||||
}
|
||||
data := wikiContentsByEntry(ctx, entry)
|
||||
|
||||
sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
|
||||
if ctx.Written() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if isViewPage {
|
||||
sidebarContent, sidebarPresent := wikiContentsByName(ctx, commit, "_Sidebar")
|
||||
if ctx.Written() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
footerContent, footerPresent := wikiContentsByName(ctx, commit, "_Footer")
|
||||
if ctx.Written() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
metas := ctx.Repo.Repository.ComposeMetas()
|
||||
ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas)
|
||||
ctx.Data["sidebarPresent"] = sidebarPresent
|
||||
ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas)
|
||||
ctx.Data["footerPresent"] = footerPresent
|
||||
ctx.Data["footerContent"] = markdown.RenderWiki(footerContent, ctx.Repo.RepoLink, metas)
|
||||
} else {
|
||||
ctx.Data["content"] = string(data)
|
||||
ctx.Data["sidebarPresent"] = false
|
||||
ctx.Data["sidebarContent"] = ""
|
||||
ctx.Data["footerPresent"] = false
|
||||
ctx.Data["footerContent"] = ""
|
||||
footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
|
||||
if ctx.Written() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
metas := ctx.Repo.Repository.ComposeMetas()
|
||||
ctx.Data["content"] = markdown.RenderWiki(data, ctx.Repo.RepoLink, metas)
|
||||
ctx.Data["sidebarPresent"] = sidebarContent != nil
|
||||
ctx.Data["sidebarContent"] = markdown.RenderWiki(sidebarContent, ctx.Repo.RepoLink, metas)
|
||||
ctx.Data["footerPresent"] = footerContent != nil
|
||||
ctx.Data["footerContent"] = markdown.RenderWiki(footerContent, ctx.Repo.RepoLink, metas)
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
||||
return wikiRepo, entry
|
||||
}
|
||||
|
||||
func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||
wikiRepo, commit, err := findWikiRepoCommit(ctx)
|
||||
if err != nil {
|
||||
if !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// get requested pagename
|
||||
pageName := models.NormalizeWikiName(ctx.Params(":page"))
|
||||
if len(pageName) == 0 {
|
||||
pageName = "Home"
|
||||
}
|
||||
ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName)
|
||||
ctx.Data["old_title"] = pageName
|
||||
ctx.Data["Title"] = pageName
|
||||
ctx.Data["title"] = pageName
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
|
||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
||||
}
|
||||
if entry == nil || ctx.Written() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ctx.Data["content"] = string(data)
|
||||
ctx.Data["sidebarPresent"] = false
|
||||
ctx.Data["sidebarContent"] = ""
|
||||
ctx.Data["footerPresent"] = false
|
||||
ctx.Data["footerContent"] = ""
|
||||
|
||||
// get commit count - wiki revisions
|
||||
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
||||
ctx.Data["CommitCount"] = commitsCount
|
||||
|
||||
// get page
|
||||
page := ctx.QueryInt("page")
|
||||
if page <= 1 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
// get Commit Count
|
||||
commitsHistory, err := wikiRepo.CommitsByFileAndRange("master", pageFilename, page)
|
||||
if err != nil {
|
||||
ctx.ServerError("CommitsByFileAndRange", err)
|
||||
return nil, nil
|
||||
}
|
||||
commitsHistory = models.ValidateCommitsWithEmails(commitsHistory)
|
||||
commitsHistory = models.ParseCommitsWithSignature(commitsHistory)
|
||||
|
||||
ctx.Data["Commits"] = commitsHistory
|
||||
|
||||
pager := context.NewPagination(int(commitsCount), git.CommitsRangeSize, page, 5)
|
||||
pager.SetDefaultParams(ctx)
|
||||
ctx.Data["Page"] = pager
|
||||
|
||||
return wikiRepo, entry
|
||||
}
|
||||
|
||||
func renderEditPage(ctx *context.Context) {
|
||||
_, commit, err := findWikiRepoCommit(ctx)
|
||||
if err != nil {
|
||||
if !git.IsErrNotExist(err) {
|
||||
ctx.ServerError("GetBranchCommit", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// get requested pagename
|
||||
pageName := models.NormalizeWikiName(ctx.Params(":page"))
|
||||
if len(pageName) == 0 {
|
||||
pageName = "Home"
|
||||
}
|
||||
ctx.Data["PageURL"] = models.WikiNameToSubURL(pageName)
|
||||
ctx.Data["old_title"] = pageName
|
||||
ctx.Data["Title"] = pageName
|
||||
ctx.Data["title"] = pageName
|
||||
ctx.Data["RequireHighlightJS"] = true
|
||||
|
||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||
data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
|
||||
if noEntry {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
||||
}
|
||||
if entry == nil || ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["content"] = string(data)
|
||||
ctx.Data["sidebarPresent"] = false
|
||||
ctx.Data["sidebarContent"] = ""
|
||||
ctx.Data["footerPresent"] = false
|
||||
ctx.Data["footerContent"] = ""
|
||||
}
|
||||
|
||||
// Wiki renders single wiki page
|
||||
func Wiki(ctx *context.Context) {
|
||||
ctx.Data["PageIsWiki"] = true
|
||||
@@ -221,7 +312,7 @@ func Wiki(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
wikiRepo, entry := renderWikiPage(ctx, true)
|
||||
wikiRepo, entry := renderViewPage(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -247,6 +338,39 @@ func Wiki(ctx *context.Context) {
|
||||
ctx.HTML(200, tplWikiView)
|
||||
}
|
||||
|
||||
// WikiRevision renders file revision list of wiki page
|
||||
func WikiRevision(ctx *context.Context) {
|
||||
ctx.Data["PageIsWiki"] = true
|
||||
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
|
||||
|
||||
if !ctx.Repo.Repository.HasWiki() {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.wiki")
|
||||
ctx.HTML(200, tplWikiStart)
|
||||
return
|
||||
}
|
||||
|
||||
wikiRepo, entry := renderRevisionPage(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if entry == nil {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.wiki")
|
||||
ctx.HTML(200, tplWikiStart)
|
||||
return
|
||||
}
|
||||
|
||||
// Get last change information.
|
||||
wikiPath := entry.Name()
|
||||
lastCommit, err := wikiRepo.GetCommitByPath(wikiPath)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetCommitByPath", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["Author"] = lastCommit.Author
|
||||
|
||||
ctx.HTML(200, tplWikiRevision)
|
||||
}
|
||||
|
||||
// WikiPages render wiki pages list page
|
||||
func WikiPages(ctx *context.Context) {
|
||||
if !ctx.Repo.Repository.HasWiki() {
|
||||
@@ -399,7 +523,7 @@ func EditWiki(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
renderWikiPage(ctx, false)
|
||||
renderEditPage(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
+47
-28
@@ -47,19 +47,6 @@ import (
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
/*func giteaLogger(l *log.LoggerAsWriter) macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
start := time.Now()
|
||||
|
||||
l.Log(fmt.Sprintf("[Macaron] Started %s %s for %s", ctx.Req.Method, ctx.Req.RequestURI, ctx.RemoteAddr()))
|
||||
|
||||
ctx.Next()
|
||||
|
||||
rw := ctx.Resp.(macaron.ResponseWriter)
|
||||
l.Log(fmt.Sprintf("[Macaron] Completed %s %s %v %s in %v", ctx.Req.Method, ctx.Req.RequestURI, rw.Status(), http.StatusText(rw.Status()), time.Since(start)))
|
||||
}
|
||||
}*/
|
||||
|
||||
type routerLoggerOptions struct {
|
||||
Ctx *macaron.Context
|
||||
Identity *string
|
||||
@@ -83,17 +70,38 @@ func setupAccessLogger(m *macaron.Macaron) {
|
||||
rw := ctx.Resp.(macaron.ResponseWriter)
|
||||
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
logTemplate.Execute(buf, routerLoggerOptions{
|
||||
err := logTemplate.Execute(buf, routerLoggerOptions{
|
||||
Ctx: ctx,
|
||||
Identity: &identity,
|
||||
Start: &start,
|
||||
ResponseWriter: &rw,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Could not set up macaron access logger: %v", err.Error())
|
||||
}
|
||||
|
||||
logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
|
||||
err = logger.SendLog(log.INFO, "", "", 0, buf.String(), "")
|
||||
if err != nil {
|
||||
log.Error("Could not set up macaron access logger: %v", err.Error())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// RouterHandler is a macaron handler that will log the routing to the default gitea log
|
||||
func RouterHandler(level log.Level) func(ctx *macaron.Context) {
|
||||
return func(ctx *macaron.Context) {
|
||||
start := time.Now()
|
||||
|
||||
_ = log.GetLogger("router").Log(0, level, "Started %s %s for %s", log.ColoredMethod(ctx.Req.Method), ctx.Req.RequestURI, ctx.RemoteAddr())
|
||||
|
||||
rw := ctx.Resp.(macaron.ResponseWriter)
|
||||
ctx.Next()
|
||||
|
||||
status := rw.Status()
|
||||
_ = log.GetLogger("router").Log(0, level, "Completed %s %s %v %s in %v", log.ColoredMethod(ctx.Req.Method), ctx.Req.RequestURI, log.ColoredStatus(status), log.ColoredStatus(status, http.StatusText(rw.Status())), log.ColoredTime(time.Since(start)))
|
||||
}
|
||||
}
|
||||
|
||||
// NewMacaron initializes Macaron instance.
|
||||
func NewMacaron() *macaron.Macaron {
|
||||
gob.Register(&u2f.Challenge{})
|
||||
@@ -102,7 +110,9 @@ func NewMacaron() *macaron.Macaron {
|
||||
loggerAsWriter := log.NewLoggerAsWriter("INFO", log.GetLogger("macaron"))
|
||||
m = macaron.NewWithLogger(loggerAsWriter)
|
||||
if !setting.DisableRouterLog && setting.RouterLogLevel != log.NONE {
|
||||
log.SetupRouterLogger(m, setting.RouterLogLevel)
|
||||
if log.GetLogger("router").GetLevel() <= setting.RouterLogLevel {
|
||||
m.Use(RouterHandler(setting.RouterLogLevel))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m = macaron.New()
|
||||
@@ -142,6 +152,14 @@ func NewMacaron() *macaron.Macaron {
|
||||
ExpiresAfter: time.Hour * 6,
|
||||
},
|
||||
))
|
||||
m.Use(public.StaticHandler(
|
||||
setting.RepositoryAvatarUploadPath,
|
||||
&public.Options{
|
||||
Prefix: "repo-avatars",
|
||||
SkipLogging: setting.DisableRouterLog,
|
||||
ExpiresAfter: time.Hour * 6,
|
||||
},
|
||||
))
|
||||
|
||||
m.Use(templates.HTMLRenderer())
|
||||
models.InitMailRender(templates.Mailer())
|
||||
@@ -418,14 +436,14 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("/delete", admin.DeleteDefaultWebhook)
|
||||
m.Get("/:type/new", repo.WebhooksNew)
|
||||
m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||
m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
|
||||
m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.GogsHooksNewPost)
|
||||
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
||||
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
|
||||
m.Post("/msteams/new", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
|
||||
m.Get("/:id", repo.WebHooksEdit)
|
||||
m.Post("/gitea/:id", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksEditPost)
|
||||
m.Post("/gogs/:id", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksEditPost)
|
||||
m.Post("/gogs/:id", bindIgnErr(auth.NewWebhookForm{}), repo.GogsHooksEditPost)
|
||||
m.Post("/slack/:id", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksEditPost)
|
||||
m.Post("/discord/:id", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
|
||||
m.Post("/dingtalk/:id", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost)
|
||||
@@ -557,7 +575,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("/delete", org.DeleteWebhook)
|
||||
m.Get("/:type/new", repo.WebhooksNew)
|
||||
m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||
m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
|
||||
m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.GogsHooksNewPost)
|
||||
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
||||
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
|
||||
@@ -596,6 +614,9 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/settings", func() {
|
||||
m.Combo("").Get(repo.Settings).
|
||||
Post(bindIgnErr(auth.RepoSettingForm{}), repo.SettingsPost)
|
||||
m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), repo.SettingsAvatar)
|
||||
m.Post("/avatar/delete", repo.SettingsDeleteAvatar)
|
||||
|
||||
m.Group("/collaboration", func() {
|
||||
m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost)
|
||||
m.Post("/access_mode", repo.ChangeCollaborationAccessMode)
|
||||
@@ -612,7 +633,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("/delete", repo.DeleteWebhook)
|
||||
m.Get("/:type/new", repo.WebhooksNew)
|
||||
m.Post("/gitea/new", bindIgnErr(auth.NewWebhookForm{}), repo.WebHooksNewPost)
|
||||
m.Post("/gogs/new", bindIgnErr(auth.NewGogshookForm{}), repo.GogsHooksNewPost)
|
||||
m.Post("/gogs/new", bindIgnErr(auth.NewWebhookForm{}), repo.GogsHooksNewPost)
|
||||
m.Post("/slack/new", bindIgnErr(auth.NewSlackHookForm{}), repo.SlackHooksNewPost)
|
||||
m.Post("/discord/new", bindIgnErr(auth.NewDiscordHookForm{}), repo.DiscordHooksNewPost)
|
||||
m.Post("/dingtalk/new", bindIgnErr(auth.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost)
|
||||
@@ -646,7 +667,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
})
|
||||
}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.UnitTypes(), context.RepoRef())
|
||||
|
||||
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.RepoMustNotBeArchived(), repo.Action)
|
||||
m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action)
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Group("/issues", func() {
|
||||
@@ -704,9 +725,9 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/milestone", func() {
|
||||
m.Get("/:id", repo.MilestoneIssuesAndPulls)
|
||||
}, reqRepoIssuesOrPullsReader, context.RepoRef())
|
||||
m.Combo("/compare/*", context.RepoMustNotBeArchived(), reqRepoCodeReader, reqRepoPullsReader, repo.MustAllowPulls, repo.SetEditorconfigIfExists).
|
||||
Get(repo.SetDiffViewStyle, repo.CompareAndPullRequest).
|
||||
Post(bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
||||
m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
|
||||
Get(repo.SetDiffViewStyle, repo.CompareDiff).
|
||||
Post(context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Group("", func() {
|
||||
@@ -783,6 +804,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/wiki", func() {
|
||||
m.Get("/?:page", repo.Wiki)
|
||||
m.Get("/_pages", repo.WikiPages)
|
||||
m.Get("/:page/_revision", repo.WikiRevision)
|
||||
|
||||
m.Group("", func() {
|
||||
m.Combo("/_new").Get(repo.NewWiki).
|
||||
@@ -878,9 +900,6 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
}, context.RepoRef(), reqRepoCodeReader)
|
||||
m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)",
|
||||
repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
|
||||
|
||||
m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists,
|
||||
repo.SetDiffViewStyle, repo.MustBeNotEmpty, reqRepoCodeReader, repo.CompareDiff)
|
||||
}, ignSignIn, context.RepoAssignment(), context.UnitTypes())
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Get("/stars", repo.Stars)
|
||||
@@ -906,7 +925,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Post("/", lfs.PostLockHandler)
|
||||
m.Post("/verify", lfs.VerifyLockHandler)
|
||||
m.Post("/:lid/unlock", lfs.UnLockHandler)
|
||||
}, context.RepoAssignment())
|
||||
})
|
||||
m.Any("/*", func(ctx *context.Context) {
|
||||
ctx.NotFound("", nil)
|
||||
})
|
||||
|
||||
+133
-58
@@ -71,14 +71,20 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if val, _ := ctx.GetSuperSecureCookie(
|
||||
base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); val != u.Name {
|
||||
if val, ok := ctx.GetSuperSecureCookie(
|
||||
base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); !ok || val != u.Name {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isSucceed = true
|
||||
ctx.Session.Set("uid", u.ID)
|
||||
ctx.Session.Set("uname", u.Name)
|
||||
err = ctx.Session.Set("uid", u.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = ctx.Session.Set("uname", u.Name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
return true, nil
|
||||
}
|
||||
@@ -191,8 +197,16 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
|
||||
}
|
||||
|
||||
// User needs to use 2FA, save data and redirect to 2FA page.
|
||||
ctx.Session.Set("twofaUid", u.ID)
|
||||
ctx.Session.Set("twofaRemember", form.Remember)
|
||||
err = ctx.Session.Set("twofaUid", u.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
err = ctx.Session.Set("twofaRemember", form.Remember)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
|
||||
regs, err := models.GetU2FRegistrationsByUID(u.ID)
|
||||
if err == nil && len(regs) > 0 {
|
||||
@@ -383,6 +397,10 @@ func U2FChallenge(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
challenge, err := u2f.NewChallenge(setting.U2F.AppID, setting.U2F.TrustedFacets)
|
||||
if err != nil {
|
||||
ctx.ServerError("u2f.NewChallenge", err)
|
||||
return
|
||||
}
|
||||
if err = ctx.Session.Set("u2fChallenge", challenge); err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
@@ -462,16 +480,22 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
|
||||
setting.CookieRememberName, u.Name, days, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
}
|
||||
|
||||
ctx.Session.Delete("openid_verified_uri")
|
||||
ctx.Session.Delete("openid_signin_remember")
|
||||
ctx.Session.Delete("openid_determined_email")
|
||||
ctx.Session.Delete("openid_determined_username")
|
||||
ctx.Session.Delete("twofaUid")
|
||||
ctx.Session.Delete("twofaRemember")
|
||||
ctx.Session.Delete("u2fChallenge")
|
||||
ctx.Session.Delete("linkAccount")
|
||||
ctx.Session.Set("uid", u.ID)
|
||||
ctx.Session.Set("uname", u.Name)
|
||||
_ = ctx.Session.Delete("openid_verified_uri")
|
||||
_ = ctx.Session.Delete("openid_signin_remember")
|
||||
_ = ctx.Session.Delete("openid_determined_email")
|
||||
_ = ctx.Session.Delete("openid_determined_username")
|
||||
_ = ctx.Session.Delete("twofaUid")
|
||||
_ = ctx.Session.Delete("twofaRemember")
|
||||
_ = ctx.Session.Delete("u2fChallenge")
|
||||
_ = ctx.Session.Delete("linkAccount")
|
||||
err := ctx.Session.Set("uid", u.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("uname", u.Name)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
|
||||
// Language setting of the user overwrites the one previously set
|
||||
// If the user does not have a locale set, we save the current one.
|
||||
@@ -563,7 +587,10 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
|
||||
|
||||
if u == nil {
|
||||
// no existing user is found, request attach or new account
|
||||
ctx.Session.Set("linkAccountGothUser", gothUser)
|
||||
err = ctx.Session.Set("linkAccountGothUser", gothUser)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
ctx.Redirect(setting.AppSubURL + "/user/link_account")
|
||||
return
|
||||
}
|
||||
@@ -573,8 +600,14 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
|
||||
_, err = models.GetTwoFactorByUID(u.ID)
|
||||
if err != nil {
|
||||
if models.IsErrTwoFactorNotEnrolled(err) {
|
||||
ctx.Session.Set("uid", u.ID)
|
||||
ctx.Session.Set("uname", u.Name)
|
||||
err = ctx.Session.Set("uid", u.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("uname", u.Name)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
|
||||
// Clear whatever CSRF has right now, force to generate a new one
|
||||
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
@@ -600,8 +633,14 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
|
||||
}
|
||||
|
||||
// User needs to use 2FA, save data and redirect to 2FA page.
|
||||
ctx.Session.Set("twofaUid", u.ID)
|
||||
ctx.Session.Set("twofaRemember", false)
|
||||
err = ctx.Session.Set("twofaUid", u.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("twofaRemember", false)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
|
||||
// If U2F is enrolled -> Redirect to U2F instead
|
||||
regs, err := models.GetU2FRegistrationsByUID(u.ID)
|
||||
@@ -658,9 +697,10 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
|
||||
|
||||
// LinkAccount shows the page where the user can decide to login or create a new account
|
||||
func LinkAccount(ctx *context.Context) {
|
||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationCaptcha || setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
||||
ctx.Data["LinkAccountMode"] = true
|
||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
|
||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha
|
||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
||||
@@ -707,10 +747,11 @@ func LinkAccount(ctx *context.Context) {
|
||||
|
||||
// LinkAccountPostSignIn handle the coupling of external account with another account using signIn
|
||||
func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
|
||||
ctx.Data["DisablePassword"] = setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
||||
ctx.Data["LinkAccountMode"] = true
|
||||
ctx.Data["LinkAccountModeSignIn"] = true
|
||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
|
||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha
|
||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
||||
@@ -760,9 +801,18 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
|
||||
}
|
||||
|
||||
// User needs to use 2FA, save data and redirect to 2FA page.
|
||||
ctx.Session.Set("twofaUid", u.ID)
|
||||
ctx.Session.Set("twofaRemember", signInForm.Remember)
|
||||
ctx.Session.Set("linkAccount", true)
|
||||
err = ctx.Session.Set("twofaUid", u.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("twofaRemember", signInForm.Remember)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("linkAccount", true)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
|
||||
// If U2F is enrolled -> Redirect to U2F instead
|
||||
regs, err := models.GetU2FRegistrationsByUID(u.ID)
|
||||
@@ -776,10 +826,13 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
|
||||
|
||||
// LinkAccountPostRegister handle the creation of a new account for an external account using signUp
|
||||
func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterForm) {
|
||||
// TODO Make insecure passwords optional for local accounts also,
|
||||
// once email-based Second-Factor Auth is available
|
||||
ctx.Data["DisablePassword"] = !setting.Service.RequireExternalRegistrationCaptcha || setting.Service.AllowOnlyExternalRegistration
|
||||
ctx.Data["Title"] = ctx.Tr("link_account")
|
||||
ctx.Data["LinkAccountMode"] = true
|
||||
ctx.Data["LinkAccountModeRegister"] = true
|
||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
|
||||
ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha
|
||||
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
|
||||
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
|
||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
||||
@@ -806,14 +859,18 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
|
||||
return
|
||||
}
|
||||
|
||||
if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ImageCaptcha && !cpt.VerifyReq(ctx.Req) {
|
||||
ctx.Data["Err_Captcha"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplLinkAccount, &form)
|
||||
return
|
||||
}
|
||||
if setting.Service.EnableCaptcha && setting.Service.RequireExternalRegistrationCaptcha {
|
||||
var valid bool
|
||||
switch setting.Service.CaptchaType {
|
||||
case setting.ImageCaptcha:
|
||||
valid = cpt.VerifyReq(ctx.Req)
|
||||
case setting.ReCaptcha:
|
||||
valid, _ = recaptcha.Verify(form.GRecaptchaResponse)
|
||||
default:
|
||||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
|
||||
return
|
||||
}
|
||||
|
||||
if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ReCaptcha {
|
||||
valid, _ := recaptcha.Verify(form.GRecaptchaResponse)
|
||||
if !valid {
|
||||
ctx.Data["Err_Captcha"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplLinkAccount, &form)
|
||||
@@ -821,15 +878,24 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
|
||||
}
|
||||
}
|
||||
|
||||
if (len(strings.TrimSpace(form.Password)) > 0 || len(strings.TrimSpace(form.Retype)) > 0) && form.Password != form.Retype {
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplLinkAccount, &form)
|
||||
return
|
||||
}
|
||||
if len(strings.TrimSpace(form.Password)) > 0 && len(form.Password) < setting.MinPasswordLength {
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplLinkAccount, &form)
|
||||
return
|
||||
if setting.Service.AllowOnlyExternalRegistration || !setting.Service.RequireExternalRegistrationPassword {
|
||||
// In models.User an empty password is classed as not set, so we set form.Password to empty.
|
||||
// Eventually the database should be changed to indicate "Second Factor"-enabled accounts
|
||||
// (accounts that do not introduce the security vulnerabilities of a password).
|
||||
// If a user decides to circumvent second-factor security, and purposefully create a password,
|
||||
// they can still do so using the "Recover Account" option.
|
||||
form.Password = ""
|
||||
} else {
|
||||
if (len(strings.TrimSpace(form.Password)) > 0 || len(strings.TrimSpace(form.Retype)) > 0) && form.Password != form.Retype {
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplLinkAccount, &form)
|
||||
return
|
||||
}
|
||||
if len(strings.TrimSpace(form.Password)) > 0 && len(form.Password) < setting.MinPasswordLength {
|
||||
ctx.Data["Err_Password"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplLinkAccount, &form)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
loginSource, err := models.GetActiveOAuth2LoginSourceByName(gothUser.(goth.User).Provider)
|
||||
@@ -897,11 +963,11 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
|
||||
}
|
||||
|
||||
func handleSignOut(ctx *context.Context) {
|
||||
ctx.Session.Delete("uid")
|
||||
ctx.Session.Delete("uname")
|
||||
ctx.Session.Delete("socialId")
|
||||
ctx.Session.Delete("socialName")
|
||||
ctx.Session.Delete("socialEmail")
|
||||
_ = ctx.Session.Delete("uid")
|
||||
_ = ctx.Session.Delete("uname")
|
||||
_ = ctx.Session.Delete("socialId")
|
||||
_ = ctx.Session.Delete("socialName")
|
||||
_ = ctx.Session.Delete("socialEmail")
|
||||
ctx.SetCookie(setting.CookieUserName, "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
ctx.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
|
||||
@@ -952,14 +1018,18 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
|
||||
return
|
||||
}
|
||||
|
||||
if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ImageCaptcha && !cpt.VerifyReq(ctx.Req) {
|
||||
ctx.Data["Err_Captcha"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplSignUp, &form)
|
||||
return
|
||||
}
|
||||
if setting.Service.EnableCaptcha {
|
||||
var valid bool
|
||||
switch setting.Service.CaptchaType {
|
||||
case setting.ImageCaptcha:
|
||||
valid = cpt.VerifyReq(ctx.Req)
|
||||
case setting.ReCaptcha:
|
||||
valid, _ = recaptcha.Verify(form.GRecaptchaResponse)
|
||||
default:
|
||||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
|
||||
return
|
||||
}
|
||||
|
||||
if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ReCaptcha {
|
||||
valid, _ := recaptcha.Verify(form.GRecaptchaResponse)
|
||||
if !valid {
|
||||
ctx.Data["Err_Captcha"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplSignUp, &form)
|
||||
@@ -1086,8 +1156,14 @@ func Activate(ctx *context.Context) {
|
||||
|
||||
log.Trace("User activated: %s", user.Name)
|
||||
|
||||
ctx.Session.Set("uid", user.ID)
|
||||
ctx.Session.Set("uname", user.Name)
|
||||
err = ctx.Session.Set("uid", user.ID)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
err = ctx.Session.Set("uname", user.Name)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("Error setting session: %v", err))
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("auth.account_activated"))
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
return
|
||||
@@ -1113,7 +1189,6 @@ func ActivateEmail(ctx *context.Context) {
|
||||
}
|
||||
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/email")
|
||||
return
|
||||
}
|
||||
|
||||
// ForgotPasswd render the forget pasword page
|
||||
|
||||
+40
-18
@@ -126,7 +126,10 @@ func SignInOpenIDPost(ctx *context.Context, form auth.SignInOpenIDForm) {
|
||||
url += "&openid.sreg.optional=nickname%2Cemail"
|
||||
|
||||
log.Trace("Form-passed openid-remember: %t", form.Remember)
|
||||
ctx.Session.Set("openid_signin_remember", form.Remember)
|
||||
err = ctx.Session.Set("openid_signin_remember", form.Remember)
|
||||
if err != nil {
|
||||
log.Error("SignInOpenIDPost: Could not set session: %v", err.Error())
|
||||
}
|
||||
|
||||
ctx.Redirect(url)
|
||||
}
|
||||
@@ -152,7 +155,7 @@ func signInOpenIDVerify(ctx *context.Context) {
|
||||
/* Now we should seek for the user and log him in, or prompt
|
||||
* to register if not found */
|
||||
|
||||
u, _ := models.GetUserByOpenID(id)
|
||||
u, err := models.GetUserByOpenID(id)
|
||||
if err != nil {
|
||||
if !models.IsErrUserNotExist(err) {
|
||||
ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
|
||||
@@ -160,6 +163,7 @@ func signInOpenIDVerify(ctx *context.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("signInOpenIDVerify: %v", err)
|
||||
}
|
||||
if u != nil {
|
||||
log.Trace("User exists, logging in")
|
||||
@@ -191,7 +195,7 @@ func signInOpenIDVerify(ctx *context.Context) {
|
||||
log.Trace("User has email=" + email + " and nickname=" + nickname)
|
||||
|
||||
if email != "" {
|
||||
u, _ = models.GetUserByEmail(email)
|
||||
u, err = models.GetUserByEmail(email)
|
||||
if err != nil {
|
||||
if !models.IsErrUserNotExist(err) {
|
||||
ctx.RenderWithErr(err.Error(), tplSignInOpenID, &auth.SignInOpenIDForm{
|
||||
@@ -199,6 +203,7 @@ func signInOpenIDVerify(ctx *context.Context) {
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("signInOpenIDVerify: %v", err)
|
||||
}
|
||||
if u != nil {
|
||||
log.Trace("Local user " + u.LowerName + " has OpenID provided email " + email)
|
||||
@@ -220,15 +225,24 @@ func signInOpenIDVerify(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Session.Set("openid_verified_uri", id)
|
||||
err = ctx.Session.Set("openid_verified_uri", id)
|
||||
if err != nil {
|
||||
log.Error("signInOpenIDVerify: Could not set session: %v", err.Error())
|
||||
}
|
||||
|
||||
ctx.Session.Set("openid_determined_email", email)
|
||||
err = ctx.Session.Set("openid_determined_email", email)
|
||||
if err != nil {
|
||||
log.Error("signInOpenIDVerify: Could not set session: %v", err.Error())
|
||||
}
|
||||
|
||||
if u != nil {
|
||||
nickname = u.LowerName
|
||||
}
|
||||
|
||||
ctx.Session.Set("openid_determined_username", nickname)
|
||||
err = ctx.Session.Set("openid_determined_username", nickname)
|
||||
if err != nil {
|
||||
log.Error("signInOpenIDVerify: Could not set session: %v", err.Error())
|
||||
}
|
||||
|
||||
if u != nil || !setting.Service.EnableOpenIDSignUp {
|
||||
ctx.Redirect(setting.AppSubURL + "/user/openid/connect")
|
||||
@@ -343,15 +357,23 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
|
||||
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
|
||||
ctx.Data["OpenID"] = oid
|
||||
|
||||
if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ImageCaptcha && !cpt.VerifyReq(ctx.Req) {
|
||||
ctx.Data["Err_Captcha"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplSignUpOID, &form)
|
||||
return
|
||||
}
|
||||
if setting.Service.EnableCaptcha {
|
||||
var valid bool
|
||||
switch setting.Service.CaptchaType {
|
||||
case setting.ImageCaptcha:
|
||||
valid = cpt.VerifyReq(ctx.Req)
|
||||
case setting.ReCaptcha:
|
||||
err := ctx.Req.ParseForm()
|
||||
if err != nil {
|
||||
ctx.ServerError("", err)
|
||||
return
|
||||
}
|
||||
valid, _ = recaptcha.Verify(form.GRecaptchaResponse)
|
||||
default:
|
||||
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
|
||||
return
|
||||
}
|
||||
|
||||
if setting.Service.EnableCaptcha && setting.Service.CaptchaType == setting.ReCaptcha {
|
||||
ctx.Req.ParseForm()
|
||||
valid, _ := recaptcha.Verify(form.GRecaptchaResponse)
|
||||
if !valid {
|
||||
ctx.Data["Err_Captcha"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tplSignUpOID, &form)
|
||||
@@ -359,11 +381,11 @@ func RegisterOpenIDPost(ctx *context.Context, cpt *captcha.Captcha, form auth.Si
|
||||
}
|
||||
}
|
||||
|
||||
len := setting.MinPasswordLength
|
||||
if len < 256 {
|
||||
len = 256
|
||||
length := setting.MinPasswordLength
|
||||
if length < 256 {
|
||||
length = 256
|
||||
}
|
||||
password, err := generate.GetRandomString(len)
|
||||
password, err := generate.GetRandomString(length)
|
||||
if err != nil {
|
||||
ctx.RenderWithErr(err.Error(), tplSignUpOID, form)
|
||||
return
|
||||
|
||||
+14
-44
@@ -499,50 +499,20 @@ func showOrgProfile(ctx *context.Context) {
|
||||
count int64
|
||||
err error
|
||||
)
|
||||
if ctx.IsSigned && !ctx.User.IsAdmin {
|
||||
env, err := org.AccessibleReposEnv(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("AccessibleReposEnv", err)
|
||||
return
|
||||
}
|
||||
env.SetSort(orderBy)
|
||||
if len(keyword) != 0 {
|
||||
env.AddKeyword(keyword)
|
||||
}
|
||||
repos, err = env.Repos(page, setting.UI.User.RepoPagingNum)
|
||||
if err != nil {
|
||||
ctx.ServerError("env.Repos", err)
|
||||
return
|
||||
}
|
||||
count, err = env.CountRepos()
|
||||
if err != nil {
|
||||
ctx.ServerError("env.CountRepos", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
showPrivate := ctx.IsSigned && ctx.User.IsAdmin
|
||||
if len(keyword) == 0 {
|
||||
repos, err = models.GetUserRepositories(org.ID, showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String())
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRepositories", err)
|
||||
return
|
||||
}
|
||||
count = models.CountUserRepositories(org.ID, showPrivate)
|
||||
} else {
|
||||
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
Keyword: keyword,
|
||||
OwnerID: org.ID,
|
||||
OrderBy: orderBy,
|
||||
Private: showPrivate,
|
||||
Page: page,
|
||||
IsProfile: true,
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchRepositoryByName", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
Keyword: keyword,
|
||||
OwnerID: org.ID,
|
||||
OrderBy: orderBy,
|
||||
Private: ctx.IsSigned,
|
||||
UserIsAdmin: ctx.IsUserSiteAdmin(),
|
||||
UserID: ctx.Data["SignedUserID"].(int64),
|
||||
Page: page,
|
||||
IsProfile: true,
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchRepositoryByName", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := org.GetMembers(); err != nil {
|
||||
|
||||
+29
-7
@@ -7,12 +7,10 @@ package user
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/go-macaron/binding"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/go-macaron/binding"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/auth"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
@@ -20,6 +18,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -164,6 +164,14 @@ func newAccessTokenResponse(grant *models.OAuth2Grant) (*AccessTokenResponse, *A
|
||||
func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) {
|
||||
errs := binding.Errors{}
|
||||
errs = form.Validate(ctx.Context, errs)
|
||||
if len(errs) > 0 {
|
||||
errstring := ""
|
||||
for _, e := range errs {
|
||||
errstring += e.Error() + "\n"
|
||||
}
|
||||
ctx.ServerError("AuthorizeOAuth: Validate: ", fmt.Errorf("errors occurred during validation: %s", errstring))
|
||||
return
|
||||
}
|
||||
|
||||
app, err := models.GetOAuth2ApplicationByClientID(form.ClientID)
|
||||
if err != nil {
|
||||
@@ -221,7 +229,6 @@ func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) {
|
||||
}, form.RedirectURI)
|
||||
return
|
||||
}
|
||||
break
|
||||
case "":
|
||||
break
|
||||
default:
|
||||
@@ -262,9 +269,24 @@ func AuthorizeOAuth(ctx *context.Context, form auth.AuthorizationForm) {
|
||||
ctx.Data["ApplicationUserLink"] = "<a href=\"" + setting.AppURL + app.User.LowerName + "\">@" + app.User.Name + "</a>"
|
||||
ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + form.RedirectURI + "</strong>"
|
||||
// TODO document SESSION <=> FORM
|
||||
ctx.Session.Set("client_id", app.ClientID)
|
||||
ctx.Session.Set("redirect_uri", form.RedirectURI)
|
||||
ctx.Session.Set("state", form.State)
|
||||
err = ctx.Session.Set("client_id", app.ClientID)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
err = ctx.Session.Set("redirect_uri", form.RedirectURI)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
err = ctx.Session.Set("state", form.State)
|
||||
if err != nil {
|
||||
handleServerError(ctx, form.State, form.RedirectURI)
|
||||
log.Error(err.Error())
|
||||
return
|
||||
}
|
||||
ctx.HTML(200, tplGrantAccess)
|
||||
}
|
||||
|
||||
|
||||
+33
-64
@@ -20,7 +20,6 @@ import (
|
||||
|
||||
const (
|
||||
tplFollowers base.TplName = "user/meta/followers"
|
||||
tplStars base.TplName = "user/meta/stars"
|
||||
)
|
||||
|
||||
// GetUserByName get user by name
|
||||
@@ -170,74 +169,44 @@ func Profile(ctx *context.Context) {
|
||||
}
|
||||
case "stars":
|
||||
ctx.Data["PageIsProfileStarList"] = true
|
||||
if len(keyword) == 0 {
|
||||
repos, err = ctxUser.GetStarredRepos(showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String())
|
||||
if err != nil {
|
||||
ctx.ServerError("GetStarredRepos", err)
|
||||
return
|
||||
}
|
||||
|
||||
count, err = ctxUser.GetStarredRepoCount(showPrivate)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetStarredRepoCount", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
Keyword: keyword,
|
||||
OwnerID: ctxUser.ID,
|
||||
OrderBy: orderBy,
|
||||
Private: showPrivate,
|
||||
Page: page,
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
Starred: true,
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
TopicOnly: topicOnly,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchRepositoryByName", err)
|
||||
return
|
||||
}
|
||||
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
Keyword: keyword,
|
||||
OrderBy: orderBy,
|
||||
Private: ctx.IsSigned,
|
||||
UserIsAdmin: ctx.IsUserSiteAdmin(),
|
||||
UserID: ctx.Data["SignedUserID"].(int64),
|
||||
Page: page,
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
StarredByID: ctxUser.ID,
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
TopicOnly: topicOnly,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchRepositoryByName", err)
|
||||
return
|
||||
}
|
||||
|
||||
total = int(count)
|
||||
default:
|
||||
if len(keyword) == 0 {
|
||||
repos, err = models.GetUserRepositories(ctxUser.ID, showPrivate, page, setting.UI.User.RepoPagingNum, orderBy.String())
|
||||
if err != nil {
|
||||
ctx.ServerError("GetRepositories", err)
|
||||
return
|
||||
}
|
||||
|
||||
if showPrivate {
|
||||
total = ctxUser.NumRepos
|
||||
} else {
|
||||
count, err := models.GetPublicRepositoryCount(ctxUser)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPublicRepositoryCount", err)
|
||||
return
|
||||
}
|
||||
total = int(count)
|
||||
}
|
||||
} else {
|
||||
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
Keyword: keyword,
|
||||
OwnerID: ctxUser.ID,
|
||||
OrderBy: orderBy,
|
||||
Private: showPrivate,
|
||||
Page: page,
|
||||
IsProfile: true,
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
TopicOnly: topicOnly,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchRepositoryByName", err)
|
||||
return
|
||||
}
|
||||
|
||||
total = int(count)
|
||||
repos, count, err = models.SearchRepositoryByName(&models.SearchRepoOptions{
|
||||
Keyword: keyword,
|
||||
OwnerID: ctxUser.ID,
|
||||
OrderBy: orderBy,
|
||||
Private: ctx.IsSigned,
|
||||
UserIsAdmin: ctx.IsUserSiteAdmin(),
|
||||
UserID: ctx.Data["SignedUserID"].(int64),
|
||||
Page: page,
|
||||
IsProfile: true,
|
||||
PageSize: setting.UI.User.RepoPagingNum,
|
||||
Collaborate: util.OptionalBoolFalse,
|
||||
TopicOnly: topicOnly,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.ServerError("SearchRepositoryByName", err)
|
||||
return
|
||||
}
|
||||
|
||||
total = int(count)
|
||||
}
|
||||
ctx.Data["Repos"] = repos
|
||||
ctx.Data["Total"] = total
|
||||
|
||||
@@ -127,6 +127,10 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *mo
|
||||
}
|
||||
defer fr.Close()
|
||||
|
||||
if form.Avatar.Size > setting.AvatarMaxFileSize {
|
||||
return errors.New(ctx.Tr("settings.uploaded_avatar_is_too_big"))
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(fr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ioutil.ReadAll: %v", err)
|
||||
@@ -137,13 +141,11 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *mo
|
||||
if err = ctxUser.UploadAvatar(data); err != nil {
|
||||
return fmt.Errorf("UploadAvatar: %v", err)
|
||||
}
|
||||
} else {
|
||||
} else if ctxUser.UseCustomAvatar && !com.IsFile(ctxUser.CustomAvatarPath()) {
|
||||
// No avatar is uploaded but setting has been changed to enable,
|
||||
// generate a random one when needed.
|
||||
if ctxUser.UseCustomAvatar && !com.IsFile(ctxUser.CustomAvatarPath()) {
|
||||
if err := ctxUser.GenerateRandomAvatar(); err != nil {
|
||||
log.Error("GenerateRandomAvatar[%d]: %v", ctxUser.ID, err)
|
||||
}
|
||||
if err := ctxUser.GenerateRandomAvatar(); err != nil {
|
||||
log.Error("GenerateRandomAvatar[%d]: %v", ctxUser.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,12 +73,18 @@ func twofaGenerateSecretAndQr(ctx *context.Context) bool {
|
||||
uri := ctx.Session.Get("twofaUri")
|
||||
if uri != nil {
|
||||
otpKey, err = otp.NewKeyFromURL(uri.(string))
|
||||
if err != nil {
|
||||
ctx.ServerError("SettingsTwoFactor: NewKeyFromURL: ", err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Filter unsafe character ':' in issuer
|
||||
issuer := strings.Replace(setting.AppName+" ("+setting.Domain+")", ":", "", -1)
|
||||
if otpKey == nil {
|
||||
err = nil // clear the error, in case the URL was invalid
|
||||
otpKey, err = totp.Generate(totp.GenerateOpts{
|
||||
SecretSize: 40,
|
||||
Issuer: setting.AppName + " (" + strings.TrimRight(setting.AppURL, "/") + ")",
|
||||
Issuer: issuer,
|
||||
AccountName: ctx.User.Name,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -101,8 +107,16 @@ func twofaGenerateSecretAndQr(ctx *context.Context) bool {
|
||||
}
|
||||
|
||||
ctx.Data["QrUri"] = template.URL("data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBytes.Bytes()))
|
||||
ctx.Session.Set("twofaSecret", otpKey.Secret())
|
||||
ctx.Session.Set("twofaUri", otpKey.String())
|
||||
err = ctx.Session.Set("twofaSecret", otpKey.Secret())
|
||||
if err != nil {
|
||||
ctx.ServerError("SettingsTwoFactor", err)
|
||||
return false
|
||||
}
|
||||
err = ctx.Session.Set("twofaUri", otpKey.String())
|
||||
if err != nil {
|
||||
ctx.ServerError("SettingsTwoFactor", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -182,8 +196,16 @@ func EnrollTwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Session.Delete("twofaSecret")
|
||||
ctx.Session.Delete("twofaUri")
|
||||
err = ctx.Session.Delete("twofaSecret")
|
||||
if err != nil {
|
||||
ctx.ServerError("SettingsTwoFactor", err)
|
||||
return
|
||||
}
|
||||
err = ctx.Session.Delete("twofaUri")
|
||||
if err != nil {
|
||||
ctx.ServerError("SettingsTwoFactor", err)
|
||||
return
|
||||
}
|
||||
ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", token))
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/security")
|
||||
}
|
||||
|
||||
@@ -42,7 +42,11 @@ func U2FRegister(ctx *context.Context, form auth.U2FRegistrationForm) {
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.Session.Set("u2fName", form.Name)
|
||||
err = ctx.Session.Set("u2fName", form.Name)
|
||||
if err != nil {
|
||||
ctx.ServerError("", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(200, u2f.NewWebRegisterRequest(challenge, regs.ToRegistrations()))
|
||||
}
|
||||
|
||||
@@ -95,5 +99,4 @@ func U2FDelete(ctx *context.Context, form auth.U2FDeleteForm) {
|
||||
ctx.JSON(200, map[string]interface{}{
|
||||
"redirect": setting.AppSubURL + "/user/settings/security",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user