mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'main' into allow-force-push-protected-branches
This commit is contained in:
@@ -63,7 +63,6 @@ package actions
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -423,15 +422,15 @@ func (ar artifactRoutes) downloadArtifact(ctx *ArtifactContext) {
|
||||
}
|
||||
|
||||
artifactID := ctx.ParamsInt64("artifact_id")
|
||||
artifact, err := actions.GetArtifactByID(ctx, artifactID)
|
||||
if errors.Is(err, util.ErrNotExist) {
|
||||
log.Error("Error getting artifact: %v", err)
|
||||
ctx.Error(http.StatusNotFound, err.Error())
|
||||
return
|
||||
} else if err != nil {
|
||||
artifact, exist, err := db.GetByID[actions.ActionArtifact](ctx, artifactID)
|
||||
if err != nil {
|
||||
log.Error("Error getting artifact: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
} else if !exist {
|
||||
log.Error("artifact with ID %d does not exist", artifactID)
|
||||
ctx.Error(http.StatusNotFound, fmt.Sprintf("artifact with ID %d does not exist", artifactID))
|
||||
return
|
||||
}
|
||||
if artifact.RunID != runID {
|
||||
log.Error("Error dismatch runID and artifactID, task: %v, artifact: %v", runID, artifactID)
|
||||
|
||||
@@ -26,10 +26,11 @@ func saveUploadChunk(st storage.ObjectStorage, ctx *ArtifactContext,
|
||||
contentRange := ctx.Req.Header.Get("Content-Range")
|
||||
start, end, length := int64(0), int64(0), int64(0)
|
||||
if _, err := fmt.Sscanf(contentRange, "bytes %d-%d/%d", &start, &end, &length); err != nil {
|
||||
log.Warn("parse content range error: %v, content-range: %s", err, contentRange)
|
||||
return -1, fmt.Errorf("parse content range error: %v", err)
|
||||
}
|
||||
// build chunk store path
|
||||
storagePath := fmt.Sprintf("tmp%d/%d-%d-%d.chunk", runID, artifact.ID, start, end)
|
||||
storagePath := fmt.Sprintf("tmp%d/%d-%d-%d-%d.chunk", runID, runID, artifact.ID, start, end)
|
||||
// use io.TeeReader to avoid reading all body to md5 sum.
|
||||
// it writes data to hasher after reading end
|
||||
// if hash is not matched, delete the read-end result
|
||||
@@ -58,6 +59,7 @@ func saveUploadChunk(st storage.ObjectStorage, ctx *ArtifactContext,
|
||||
}
|
||||
|
||||
type chunkFileItem struct {
|
||||
RunID int64
|
||||
ArtifactID int64
|
||||
Start int64
|
||||
End int64
|
||||
@@ -67,9 +69,12 @@ type chunkFileItem struct {
|
||||
func listChunksByRunID(st storage.ObjectStorage, runID int64) (map[int64][]*chunkFileItem, error) {
|
||||
storageDir := fmt.Sprintf("tmp%d", runID)
|
||||
var chunks []*chunkFileItem
|
||||
if err := st.IterateObjects(storageDir, func(path string, obj storage.Object) error {
|
||||
item := chunkFileItem{Path: path}
|
||||
if _, err := fmt.Sscanf(path, filepath.Join(storageDir, "%d-%d-%d.chunk"), &item.ArtifactID, &item.Start, &item.End); err != nil {
|
||||
if err := st.IterateObjects(storageDir, func(fpath string, obj storage.Object) error {
|
||||
baseName := filepath.Base(fpath)
|
||||
// when read chunks from storage, it only contains storage dir and basename,
|
||||
// no matter the subdirectory setting in storage config
|
||||
item := chunkFileItem{Path: storageDir + "/" + baseName}
|
||||
if _, err := fmt.Sscanf(baseName, "%d-%d-%d-%d.chunk", &item.RunID, &item.ArtifactID, &item.Start, &item.End); err != nil {
|
||||
return fmt.Errorf("parse content range error: %v", err)
|
||||
}
|
||||
chunks = append(chunks, &item)
|
||||
|
||||
@@ -94,6 +94,12 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s
|
||||
func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string {
|
||||
variables := map[string]string{}
|
||||
|
||||
// Global
|
||||
globalVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{})
|
||||
if err != nil {
|
||||
log.Error("find global variables: %v", err)
|
||||
}
|
||||
|
||||
// Org / User level
|
||||
ownerVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID})
|
||||
if err != nil {
|
||||
@@ -106,8 +112,8 @@ func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map
|
||||
log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err)
|
||||
}
|
||||
|
||||
// Level precedence: Repo > Org / User
|
||||
for _, v := range append(ownerVariables, repoVariables...) {
|
||||
// Level precedence: Repo > Org / User > Global
|
||||
for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) {
|
||||
variables[v.Name] = v.Data
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package admin
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
)
|
||||
|
||||
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
|
||||
|
||||
// GetRegistrationToken returns the token to register global runners
|
||||
func GetRegistrationToken(ctx *context.APIContext) {
|
||||
// swagger:operation GET /admin/runners/registration-token admin adminGetRunnerRegistrationToken
|
||||
// ---
|
||||
// summary: Get an global actions runner registration token
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/RegistrationToken"
|
||||
|
||||
shared.GetRegistrationToken(ctx, 0, 0)
|
||||
}
|
||||
+36
-17
@@ -822,9 +822,7 @@ func Routes() *web.Route {
|
||||
m.Use(securityHeaders())
|
||||
if setting.CORSConfig.Enabled {
|
||||
m.Use(cors.Handler(cors.Options{
|
||||
// Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option
|
||||
AllowedOrigins: setting.CORSConfig.AllowDomain,
|
||||
// setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option
|
||||
AllowedOrigins: setting.CORSConfig.AllowDomain,
|
||||
AllowedMethods: setting.CORSConfig.Methods,
|
||||
AllowCredentials: setting.CORSConfig.AllowCredentials,
|
||||
AllowedHeaders: append([]string{"Authorization", "X-Gitea-OTP"}, setting.CORSConfig.Headers...),
|
||||
@@ -950,11 +948,17 @@ func Routes() *web.Route {
|
||||
Post(bind(api.CreateEmailOption{}), user.AddEmail).
|
||||
Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail)
|
||||
|
||||
// create or update a user's actions secrets
|
||||
m.Group("/actions/secrets", func() {
|
||||
m.Combo("/{secretname}").
|
||||
Put(bind(api.CreateOrUpdateSecretOption{}), user.CreateOrUpdateSecret).
|
||||
Delete(user.DeleteSecret)
|
||||
// manage user-level actions features
|
||||
m.Group("/actions", func() {
|
||||
m.Group("/secrets", func() {
|
||||
m.Combo("/{secretname}").
|
||||
Put(bind(api.CreateOrUpdateSecretOption{}), user.CreateOrUpdateSecret).
|
||||
Delete(user.DeleteSecret)
|
||||
})
|
||||
|
||||
m.Group("/runners", func() {
|
||||
m.Get("/registration-token", reqToken(), user.GetRegistrationToken)
|
||||
})
|
||||
})
|
||||
|
||||
m.Get("/followers", user.ListMyFollowers)
|
||||
@@ -1054,10 +1058,16 @@ func Routes() *web.Route {
|
||||
m.Post("/accept", repo.AcceptTransfer)
|
||||
m.Post("/reject", repo.RejectTransfer)
|
||||
}, reqToken())
|
||||
m.Group("/actions/secrets", func() {
|
||||
m.Combo("/{secretname}").
|
||||
Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret).
|
||||
Delete(reqToken(), reqOwner(), repo.DeleteSecret)
|
||||
m.Group("/actions", func() {
|
||||
m.Group("/secrets", func() {
|
||||
m.Combo("/{secretname}").
|
||||
Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret).
|
||||
Delete(reqToken(), reqOwner(), repo.DeleteSecret)
|
||||
})
|
||||
|
||||
m.Group("/runners", func() {
|
||||
m.Get("/registration-token", reqToken(), reqOwner(), repo.GetRegistrationToken)
|
||||
})
|
||||
})
|
||||
m.Group("/hooks/git", func() {
|
||||
m.Combo("").Get(repo.ListGitHooks)
|
||||
@@ -1424,11 +1434,17 @@ func Routes() *web.Route {
|
||||
m.Combo("/{username}").Get(reqToken(), org.IsMember).
|
||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteMember)
|
||||
})
|
||||
m.Group("/actions/secrets", func() {
|
||||
m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
|
||||
m.Combo("/{secretname}").
|
||||
Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret).
|
||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteSecret)
|
||||
m.Group("/actions", func() {
|
||||
m.Group("/secrets", func() {
|
||||
m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets)
|
||||
m.Combo("/{secretname}").
|
||||
Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret).
|
||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteSecret)
|
||||
})
|
||||
|
||||
m.Group("/runners", func() {
|
||||
m.Get("/registration-token", reqToken(), reqOrgOwnership(), org.GetRegistrationToken)
|
||||
})
|
||||
})
|
||||
m.Group("/public_members", func() {
|
||||
m.Get("", org.ListPublicMembers)
|
||||
@@ -1520,6 +1536,9 @@ func Routes() *web.Route {
|
||||
Patch(bind(api.EditHookOption{}), admin.EditHook).
|
||||
Delete(admin.DeleteHook)
|
||||
})
|
||||
m.Group("/runners", func() {
|
||||
m.Get("/registration-token", admin.GetRegistrationToken)
|
||||
})
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin())
|
||||
|
||||
m.Group("/topics", func() {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package org
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
)
|
||||
|
||||
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
|
||||
|
||||
// GetRegistrationToken returns the token to register org runners
|
||||
func GetRegistrationToken(ctx *context.APIContext) {
|
||||
// swagger:operation GET /orgs/{org}/actions/runners/registration-token organization orgGetRunnerRegistrationToken
|
||||
// ---
|
||||
// summary: Get an organization's actions runner registration token
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: org
|
||||
// in: path
|
||||
// description: name of the organization
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/RegistrationToken"
|
||||
|
||||
shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
|
||||
}
|
||||
@@ -227,11 +227,18 @@ func GetPushMirrorByName(ctx *context.APIContext) {
|
||||
|
||||
mirrorName := ctx.Params(":name")
|
||||
// Get push mirror of a specific repo by remoteName
|
||||
pushMirror, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: mirrorName})
|
||||
pushMirror, exist, err := db.Get[repo_model.PushMirror](ctx, repo_model.PushMirrorOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
RemoteName: mirrorName,
|
||||
}.ToConds())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusNotFound, "GetPushMirrors", err)
|
||||
ctx.Error(http.StatusInternalServerError, "GetPushMirrors", err)
|
||||
return
|
||||
} else if !exist {
|
||||
ctx.Error(http.StatusNotFound, "GetPushMirrors", nil)
|
||||
return
|
||||
}
|
||||
|
||||
m, err := convert.ToPushMirror(ctx, pushMirror)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPushMirrorByRemoteName", err)
|
||||
@@ -368,7 +375,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
|
||||
RemoteAddress: remoteAddress,
|
||||
}
|
||||
|
||||
if err = repo_model.InsertPushMirror(ctx, pushMirror); err != nil {
|
||||
if err = db.Insert(ctx, pushMirror); err != nil {
|
||||
ctx.ServerError("InsertPushMirror", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
)
|
||||
|
||||
// GetRegistrationToken returns the token to register repo runners
|
||||
func GetRegistrationToken(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/runners/registration-token repository repoGetRunnerRegistrationToken
|
||||
// ---
|
||||
// summary: Get a repository's actions runner registration token
|
||||
// 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
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/RegistrationToken"
|
||||
|
||||
shared.GetRegistrationToken(ctx, ctx.Repo.Repository.OwnerID, ctx.Repo.Repository.ID)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package shared
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// RegistrationToken is response related to registeration token
|
||||
// swagger:response RegistrationToken
|
||||
type RegistrationToken struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
func GetRegistrationToken(ctx *context.APIContext, ownerID, repoID int64) {
|
||||
token, err := actions_model.GetLatestRunnerToken(ctx, ownerID, repoID)
|
||||
if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) {
|
||||
token, err = actions_model.NewRunnerToken(ctx, ownerID, repoID)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.InternalServerError(err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, RegistrationToken{Token: token.Token})
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/shared"
|
||||
)
|
||||
|
||||
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
|
||||
|
||||
// GetRegistrationToken returns the token to register user runners
|
||||
func GetRegistrationToken(ctx *context.APIContext) {
|
||||
// swagger:operation GET /user/actions/runners/registration-token user userGetRunnerRegistrationToken
|
||||
// ---
|
||||
// summary: Get an user's actions runner registration token
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/RegistrationToken"
|
||||
|
||||
shared.GetRegistrationToken(ctx, ctx.Doer.ID, 0)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ package utils
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@@ -157,6 +158,7 @@ func pullHook(events []string, event string) bool {
|
||||
// addHook add the hook specified by `form`, `ownerID` and `repoID`. If there is
|
||||
// an error, write to `ctx` accordingly. Return (webhook, ok)
|
||||
func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoID int64) (*webhook.Webhook, bool) {
|
||||
var isSystemWebhook bool
|
||||
if !checkCreateHookOption(ctx, form) {
|
||||
return nil, false
|
||||
}
|
||||
@@ -164,13 +166,22 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, ownerID, repoI
|
||||
if len(form.Events) == 0 {
|
||||
form.Events = []string{"push"}
|
||||
}
|
||||
if form.Config["is_system_webhook"] != "" {
|
||||
sw, err := strconv.ParseBool(form.Config["is_system_webhook"])
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid is_system_webhook value")
|
||||
return nil, false
|
||||
}
|
||||
isSystemWebhook = sw
|
||||
}
|
||||
w := &webhook.Webhook{
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
URL: form.Config["url"],
|
||||
ContentType: webhook.ToHookContentType(form.Config["content_type"]),
|
||||
Secret: form.Config["secret"],
|
||||
HTTPMethod: "POST",
|
||||
OwnerID: ownerID,
|
||||
RepoID: repoID,
|
||||
URL: form.Config["url"],
|
||||
ContentType: webhook.ToHookContentType(form.Config["content_type"]),
|
||||
Secret: form.Config["secret"],
|
||||
HTTPMethod: "POST",
|
||||
IsSystemWebhook: isSystemWebhook,
|
||||
HookEvent: &webhook_module.HookEvent{
|
||||
ChooseEvents: true,
|
||||
HookEvents: webhook_module.HookEvents{
|
||||
|
||||
@@ -37,7 +37,6 @@ func InitDBEngine(ctx context.Context) (err error) {
|
||||
log.Info("Backing off for %d seconds", int64(setting.Database.DBConnectBackoff/time.Second))
|
||||
time.Sleep(setting.Database.DBConnectBackoff)
|
||||
}
|
||||
db.HasEngine = true
|
||||
config.SetDynGetter(system_model.NewDatabaseDynKeyGetter())
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/graceful"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
|
||||
const (
|
||||
tplDashboard base.TplName = "admin/dashboard"
|
||||
tplSelfCheck base.TplName = "admin/self_check"
|
||||
tplCron base.TplName = "admin/cron"
|
||||
tplQueue base.TplName = "admin/queue"
|
||||
tplStacktrace base.TplName = "admin/stacktrace"
|
||||
@@ -172,6 +174,33 @@ func DashboardPost(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func SelfCheck(ctx *context.Context) {
|
||||
ctx.Data["PageIsAdminSelfCheck"] = true
|
||||
r, err := db.CheckCollationsDefaultEngine()
|
||||
if err != nil {
|
||||
ctx.Flash.Error(fmt.Sprintf("CheckCollationsDefaultEngine: %v", err), true)
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
ctx.Data["DatabaseType"] = setting.Database.Type
|
||||
ctx.Data["DatabaseCheckResult"] = r
|
||||
hasProblem := false
|
||||
if !r.CollationEquals(r.DatabaseCollation, r.ExpectedCollation) {
|
||||
ctx.Data["DatabaseCheckCollationMismatch"] = true
|
||||
hasProblem = true
|
||||
}
|
||||
if !r.IsCollationCaseSensitive(r.DatabaseCollation) {
|
||||
ctx.Data["DatabaseCheckCollationCaseInsensitive"] = true
|
||||
hasProblem = true
|
||||
}
|
||||
ctx.Data["DatabaseCheckInconsistentCollationColumns"] = r.InconsistentCollationColumns
|
||||
hasProblem = hasProblem || len(r.InconsistentCollationColumns) > 0
|
||||
|
||||
ctx.Data["DatabaseCheckHasProblems"] = hasProblem
|
||||
}
|
||||
ctx.HTML(http.StatusOK, tplSelfCheck)
|
||||
}
|
||||
|
||||
func CronTasks(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("admin.monitor.cron")
|
||||
ctx.Data["PageIsAdminMonitorCron"] = true
|
||||
|
||||
@@ -58,4 +58,11 @@ func MonitorDiagnosis(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
_ = pprof.Lookup("goroutine").WriteTo(f, 1)
|
||||
|
||||
f, err = zipWriter.CreateHeader(&zip.FileHeader{Name: "heap.dat", Method: zip.Deflate, Modified: time.Now()})
|
||||
if err != nil {
|
||||
ctx.ServerError("Failed to create zip file", err)
|
||||
return
|
||||
}
|
||||
_ = pprof.Lookup("heap").WriteTo(f, 0)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
system_model "code.gitea.io/gitea/models/system"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@@ -55,7 +56,7 @@ func DeleteNotices(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := system_model.DeleteNoticesByIDs(ctx, ids); err != nil {
|
||||
if err := db.DeleteByIDs[system_model.Notice](ctx, ids...); err != nil {
|
||||
ctx.Flash.Error("DeleteNoticesByIDs: " + err.Error())
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
} else {
|
||||
|
||||
@@ -45,10 +45,6 @@ const (
|
||||
|
||||
// autoSignIn reads cookie and try to auto-login.
|
||||
func autoSignIn(ctx *context.Context) (bool, error) {
|
||||
if !db.HasEngine {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isSucceed := false
|
||||
defer func() {
|
||||
if !isSucceed {
|
||||
@@ -145,7 +141,11 @@ func CheckAutoLogin(ctx *context.Context) bool {
|
||||
|
||||
if isSucceed {
|
||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||
ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL))
|
||||
nextRedirectTo := setting.AppSubURL + string(setting.LandingPageURL)
|
||||
if setting.LandingPageURL == setting.LandingPageLogin {
|
||||
nextRedirectTo = setting.AppSubURL + "/" // do not cycle-redirect to the login page
|
||||
}
|
||||
ctx.RedirectToFirst(redirectTo, nextRedirectTo)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -368,14 +368,14 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||
return setting.AppSubURL + "/"
|
||||
}
|
||||
|
||||
func getUserName(gothUser *goth.User) string {
|
||||
func getUserName(gothUser *goth.User) (string, error) {
|
||||
switch setting.OAuth2Client.Username {
|
||||
case setting.OAuth2UsernameEmail:
|
||||
return strings.Split(gothUser.Email, "@")[0]
|
||||
return user_model.NormalizeUserName(strings.Split(gothUser.Email, "@")[0])
|
||||
case setting.OAuth2UsernameNickname:
|
||||
return gothUser.NickName
|
||||
return user_model.NormalizeUserName(gothUser.NickName)
|
||||
default: // OAuth2UsernameUserid
|
||||
return gothUser.UserID
|
||||
return gothUser.UserID, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,11 @@ func LinkAccount(ctx *context.Context) {
|
||||
}
|
||||
|
||||
gu, _ := gothUser.(goth.User)
|
||||
uname := getUserName(&gu)
|
||||
uname, err := getUserName(&gu)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
email := gu.Email
|
||||
ctx.Data["user_name"] = uname
|
||||
ctx.Data["email"] = email
|
||||
|
||||
@@ -970,8 +970,13 @@ func SignInOAuthCallback(ctx *context.Context) {
|
||||
ctx.ServerError("CreateUser", err)
|
||||
return
|
||||
}
|
||||
uname, err := getUserName(&gothUser)
|
||||
if err != nil {
|
||||
ctx.ServerError("UserSignIn", err)
|
||||
return
|
||||
}
|
||||
u = &user_model.User{
|
||||
Name: getUserName(&gothUser),
|
||||
Name: uname,
|
||||
FullName: gothUser.Name,
|
||||
Email: gothUser.Email,
|
||||
LoginType: auth.OAuth2,
|
||||
|
||||
+11
-11
@@ -28,16 +28,16 @@ func requireSignIn(ctx *context.Context) {
|
||||
|
||||
func gitHTTPRouters(m *web.Route) {
|
||||
m.Group("", func() {
|
||||
m.PostOptions("/git-upload-pack", repo.ServiceUploadPack)
|
||||
m.PostOptions("/git-receive-pack", repo.ServiceReceivePack)
|
||||
m.GetOptions("/info/refs", repo.GetInfoRefs)
|
||||
m.GetOptions("/HEAD", repo.GetTextFile("HEAD"))
|
||||
m.GetOptions("/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
|
||||
m.GetOptions("/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
|
||||
m.GetOptions("/objects/info/packs", repo.GetInfoPacks)
|
||||
m.GetOptions("/objects/info/{file:[^/]*}", repo.GetTextFile(""))
|
||||
m.GetOptions("/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
|
||||
m.GetOptions("/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
|
||||
m.Methods("POST,OPTIONS", "/git-upload-pack", repo.ServiceUploadPack)
|
||||
m.Methods("POST,OPTIONS", "/git-receive-pack", repo.ServiceReceivePack)
|
||||
m.Methods("GET,OPTIONS", "/info/refs", repo.GetInfoRefs)
|
||||
m.Methods("GET,OPTIONS", "/HEAD", repo.GetTextFile("HEAD"))
|
||||
m.Methods("GET,OPTIONS", "/objects/info/alternates", repo.GetTextFile("objects/info/alternates"))
|
||||
m.Methods("GET,OPTIONS", "/objects/info/http-alternates", repo.GetTextFile("objects/info/http-alternates"))
|
||||
m.Methods("GET,OPTIONS", "/objects/info/packs", repo.GetInfoPacks)
|
||||
m.Methods("GET,OPTIONS", "/objects/info/{file:[^/]*}", repo.GetTextFile(""))
|
||||
m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38}}", repo.GetLooseObject)
|
||||
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40}}.pack", repo.GetPackFile)
|
||||
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40}}.idx", repo.GetIdxFile)
|
||||
}, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb())
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@ func DummyOK(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func DummyBadRequest(w http.ResponseWriter, req *http.Request) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func RobotsTxt(w http.ResponseWriter, req *http.Request) {
|
||||
robotsTxt := util.FilePathJoinAbs(setting.CustomPath, "public/robots.txt")
|
||||
if ok, _ := util.IsExist(robotsTxt); !ok {
|
||||
|
||||
+11
-6
@@ -18,6 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
shared_user "code.gitea.io/gitea/routers/web/shared/user"
|
||||
)
|
||||
|
||||
@@ -157,14 +158,14 @@ func Home(ctx *context.Context) {
|
||||
|
||||
ctx.Data["ShowMemberAndTeamTab"] = ctx.Org.IsMember || len(members) > 0
|
||||
|
||||
profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
|
||||
profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
|
||||
defer profileClose()
|
||||
prepareOrgProfileReadme(ctx, profileGitRepo, profileReadmeBlob)
|
||||
prepareOrgProfileReadme(ctx, profileGitRepo, profileDbRepo, profileReadmeBlob)
|
||||
|
||||
ctx.HTML(http.StatusOK, tplOrgHome)
|
||||
}
|
||||
|
||||
func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repository, profileReadme *git.Blob) {
|
||||
func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repository, profileDbRepo *repo_model.Repository, profileReadme *git.Blob) {
|
||||
if profileGitRepo == nil || profileReadme == nil {
|
||||
return
|
||||
}
|
||||
@@ -172,10 +173,14 @@ func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repositor
|
||||
if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
|
||||
log.Error("failed to GetBlobContent: %v", err)
|
||||
} else {
|
||||
// Pass URLPrefix to markdown render for the full link of media elements.
|
||||
// The profile of default branch would be shown.
|
||||
prefix := profileDbRepo.Link() + "/src/branch/" + util.PathEscapeSegments(profileDbRepo.DefaultBranch)
|
||||
if profileContent, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
GitRepo: profileGitRepo,
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
Ctx: ctx,
|
||||
GitRepo: profileGitRepo,
|
||||
URLPrefix: prefix,
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
}, bytes); err != nil {
|
||||
log.Error("failed to RenderString: %v", err)
|
||||
} else {
|
||||
|
||||
@@ -125,7 +125,7 @@ func RefBlame(ctx *context.Context) {
|
||||
}
|
||||
|
||||
type blameResult struct {
|
||||
Parts []git.BlamePart
|
||||
Parts []*git.BlamePart
|
||||
UsesIgnoreRevs bool
|
||||
FaultyIgnoreRevsFile bool
|
||||
}
|
||||
@@ -175,7 +175,9 @@ func performBlame(ctx *context.Context, repoPath string, commit *git.Commit, fil
|
||||
func fillBlameResult(br *git.BlameReader, r *blameResult) error {
|
||||
r.UsesIgnoreRevs = br.UsesIgnoreRevs()
|
||||
|
||||
r.Parts = make([]git.BlamePart, 0, 5)
|
||||
previousHelper := make(map[string]*git.BlamePart)
|
||||
|
||||
r.Parts = make([]*git.BlamePart, 0, 5)
|
||||
for {
|
||||
blamePart, err := br.NextPart()
|
||||
if err != nil {
|
||||
@@ -184,13 +186,23 @@ func fillBlameResult(br *git.BlameReader, r *blameResult) error {
|
||||
if blamePart == nil {
|
||||
break
|
||||
}
|
||||
r.Parts = append(r.Parts, *blamePart)
|
||||
|
||||
if prev, ok := previousHelper[blamePart.Sha]; ok {
|
||||
if blamePart.PreviousSha == "" {
|
||||
blamePart.PreviousSha = prev.PreviousSha
|
||||
blamePart.PreviousPath = prev.PreviousPath
|
||||
}
|
||||
} else {
|
||||
previousHelper[blamePart.Sha] = blamePart
|
||||
}
|
||||
|
||||
r.Parts = append(r.Parts, blamePart)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) map[string]*user_model.UserCommit {
|
||||
func processBlameParts(ctx *context.Context, blameParts []*git.BlamePart) map[string]*user_model.UserCommit {
|
||||
// store commit data by SHA to look up avatar info etc
|
||||
commitNames := make(map[string]*user_model.UserCommit)
|
||||
// and as blameParts can reference the same commits multiple
|
||||
@@ -232,7 +244,7 @@ func processBlameParts(ctx *context.Context, blameParts []git.BlamePart) map[str
|
||||
return commitNames
|
||||
}
|
||||
|
||||
func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames map[string]*user_model.UserCommit) {
|
||||
func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames map[string]*user_model.UserCommit) {
|
||||
repoLink := ctx.Repo.RepoLink
|
||||
|
||||
language := ""
|
||||
|
||||
@@ -653,7 +653,15 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
||||
if pb != nil && pb.EnableStatusCheck {
|
||||
ctx.Data["is_context_required"] = func(context string) bool {
|
||||
for _, c := range pb.StatusCheckContexts {
|
||||
if gp, err := glob.Compile(c); err == nil && gp.Match(context) {
|
||||
if c == context {
|
||||
return true
|
||||
}
|
||||
if gp, err := glob.Compile(c); err != nil {
|
||||
// All newly created status_check_contexts are checked to ensure they are valid glob expressions before being stored in the database.
|
||||
// But some old status_check_context created before glob was introduced may be invalid glob expressions.
|
||||
// So log the error here for debugging.
|
||||
log.Error("compile glob %q: %v", c, err)
|
||||
} else if gp.Match(context) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +418,7 @@ func SettingsPost(ctx *context.Context) {
|
||||
Interval: interval,
|
||||
RemoteAddress: remoteAddress,
|
||||
}
|
||||
if err := repo_model.InsertPushMirror(ctx, m); err != nil {
|
||||
if err := db.Insert(ctx, m); err != nil {
|
||||
ctx.ServerError("InsertPushMirror", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,9 +15,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tplRepoVariables base.TplName = "repo/settings/actions"
|
||||
tplOrgVariables base.TplName = "org/settings/actions"
|
||||
tplUserVariables base.TplName = "user/settings/actions"
|
||||
tplRepoVariables base.TplName = "repo/settings/actions"
|
||||
tplOrgVariables base.TplName = "org/settings/actions"
|
||||
tplUserVariables base.TplName = "user/settings/actions"
|
||||
tplAdminVariables base.TplName = "admin/actions"
|
||||
)
|
||||
|
||||
type variablesCtx struct {
|
||||
@@ -26,6 +27,7 @@ type variablesCtx struct {
|
||||
IsRepo bool
|
||||
IsOrg bool
|
||||
IsUser bool
|
||||
IsGlobal bool
|
||||
VariablesTemplate base.TplName
|
||||
RedirectLink string
|
||||
}
|
||||
@@ -33,6 +35,7 @@ type variablesCtx struct {
|
||||
func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
|
||||
if ctx.Data["PageIsRepoSettings"] == true {
|
||||
return &variablesCtx{
|
||||
OwnerID: 0,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsRepo: true,
|
||||
VariablesTemplate: tplRepoVariables,
|
||||
@@ -48,6 +51,7 @@ func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
|
||||
}
|
||||
return &variablesCtx{
|
||||
OwnerID: ctx.ContextUser.ID,
|
||||
RepoID: 0,
|
||||
IsOrg: true,
|
||||
VariablesTemplate: tplOrgVariables,
|
||||
RedirectLink: ctx.Org.OrgLink + "/settings/actions/variables",
|
||||
@@ -57,12 +61,23 @@ func getVariablesCtx(ctx *context.Context) (*variablesCtx, error) {
|
||||
if ctx.Data["PageIsUserSettings"] == true {
|
||||
return &variablesCtx{
|
||||
OwnerID: ctx.Doer.ID,
|
||||
RepoID: 0,
|
||||
IsUser: true,
|
||||
VariablesTemplate: tplUserVariables,
|
||||
RedirectLink: setting.AppSubURL + "/user/settings/actions/variables",
|
||||
}, nil
|
||||
}
|
||||
|
||||
if ctx.Data["PageIsAdmin"] == true {
|
||||
return &variablesCtx{
|
||||
OwnerID: 0,
|
||||
RepoID: 0,
|
||||
IsGlobal: true,
|
||||
VariablesTemplate: tplAdminVariables,
|
||||
RedirectLink: setting.AppSubURL + "/admin/actions/variables",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("unable to set Variables context")
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
|
||||
func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) {
|
||||
profileDbRepo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ".profile")
|
||||
if err == nil {
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, profileDbRepo, doer)
|
||||
@@ -105,7 +105,7 @@ func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profile
|
||||
} else if !repo_model.IsErrRepoNotExist(err) {
|
||||
log.Error("FindUserProfileReadme failed to GetRepositoryByName: %v", err)
|
||||
}
|
||||
return profileGitRepo, profileReadmeBlob, func() {
|
||||
return profileDbRepo, profileGitRepo, profileReadmeBlob, func() {
|
||||
if profileGitRepo != nil {
|
||||
_ = profileGitRepo.Close()
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func FindUserProfileReadme(ctx *context.Context, doer *user_model.User) (profile
|
||||
func RenderUserHeader(ctx *context.Context) {
|
||||
prepareContextForCommonProfile(ctx)
|
||||
|
||||
_, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx, ctx.Doer)
|
||||
_, _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx, ctx.Doer)
|
||||
defer profileClose()
|
||||
ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil
|
||||
}
|
||||
|
||||
@@ -64,17 +64,17 @@ func userProfile(ctx *context.Context) {
|
||||
ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data)
|
||||
}
|
||||
|
||||
profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
|
||||
profileDbRepo, profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx, ctx.Doer)
|
||||
defer profileClose()
|
||||
|
||||
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
||||
prepareUserProfileTabData(ctx, showPrivate, profileGitRepo, profileReadmeBlob)
|
||||
prepareUserProfileTabData(ctx, showPrivate, profileDbRepo, profileGitRepo, profileReadmeBlob)
|
||||
// call PrepareContextForProfileBigAvatar later to avoid re-querying the NumFollowers & NumFollowing
|
||||
shared_user.PrepareContextForProfileBigAvatar(ctx)
|
||||
ctx.HTML(http.StatusOK, tplProfile)
|
||||
}
|
||||
|
||||
func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGitRepo *git.Repository, profileReadme *git.Blob) {
|
||||
func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDbRepo *repo_model.Repository, profileGitRepo *git.Repository, profileReadme *git.Blob) {
|
||||
// if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page
|
||||
// if there is not a profile readme, the overview tab should be treated as the repositories tab
|
||||
tab := ctx.FormString("tab")
|
||||
@@ -233,10 +233,18 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGi
|
||||
if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
|
||||
log.Error("failed to GetBlobContent: %v", err)
|
||||
} else {
|
||||
// Give the URLPrefix to the markdown render for the full link of media element.
|
||||
// the media link usually be like /[user]/[repoName]/media/branch/[branchName],
|
||||
// Eg. /Tom/.profile/media/branch/main
|
||||
// The branch shown on the profile page is the default branch, this need to be in sync with doc, see:
|
||||
// https://docs.gitea.com/usage/profile-readme
|
||||
|
||||
prefix := profileDbRepo.Link() + "/src/branch/" + util.PathEscapeSegments(profileDbRepo.DefaultBranch)
|
||||
if profileContent, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
GitRepo: profileGitRepo,
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
Ctx: ctx,
|
||||
GitRepo: profileGitRepo,
|
||||
URLPrefix: prefix,
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
}, bytes); err != nil {
|
||||
log.Error("failed to RenderString: %v", err)
|
||||
} else {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/avatars"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@@ -130,7 +131,7 @@ func UpdateAvatarSetting(ctx *context.Context, form *forms.AvatarForm, ctxUser *
|
||||
ctxUser.UseCustomAvatar = form.Source == forms.AvatarLocal
|
||||
if len(form.Gravatar) > 0 {
|
||||
if form.Avatar != nil {
|
||||
ctxUser.Avatar = base.EncodeMD5(form.Gravatar)
|
||||
ctxUser.Avatar = avatars.HashEmail(form.Gravatar)
|
||||
} else {
|
||||
ctxUser.Avatar = ""
|
||||
}
|
||||
|
||||
+38
-21
@@ -60,13 +60,12 @@ const (
|
||||
GzipMinSize = 1400
|
||||
)
|
||||
|
||||
// CorsHandler return a http handler who set CORS options if enabled by config
|
||||
func CorsHandler() func(next http.Handler) http.Handler {
|
||||
// optionsCorsHandler return a http handler which sets CORS options if enabled by config, it blocks non-CORS OPTIONS requests.
|
||||
func optionsCorsHandler() func(next http.Handler) http.Handler {
|
||||
var corsHandler func(next http.Handler) http.Handler
|
||||
if setting.CORSConfig.Enabled {
|
||||
return cors.Handler(cors.Options{
|
||||
// Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option
|
||||
AllowedOrigins: setting.CORSConfig.AllowDomain,
|
||||
// setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option
|
||||
corsHandler = cors.Handler(cors.Options{
|
||||
AllowedOrigins: setting.CORSConfig.AllowDomain,
|
||||
AllowedMethods: setting.CORSConfig.Methods,
|
||||
AllowCredentials: setting.CORSConfig.AllowCredentials,
|
||||
AllowedHeaders: setting.CORSConfig.Headers,
|
||||
@@ -75,7 +74,23 @@ func CorsHandler() func(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return next
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodOptions {
|
||||
if corsHandler != nil && r.Header.Get("Access-Control-Request-Method") != "" {
|
||||
corsHandler(next).ServeHTTP(w, r)
|
||||
} else {
|
||||
// it should explicitly deny OPTIONS requests if CORS handler is not executed, to avoid the next GET/POST handler being incorrectly called by the OPTIONS request
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
}
|
||||
return
|
||||
}
|
||||
// for non-OPTIONS requests, call the CORS handler to add some related headers like "Vary"
|
||||
if corsHandler != nil {
|
||||
corsHandler(next).ServeHTTP(w, r)
|
||||
} else {
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +233,7 @@ func Routes() *web.Route {
|
||||
routes := web.NewRoute()
|
||||
|
||||
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
|
||||
routes.Methods("GET, HEAD", "/assets/*", CorsHandler(), public.FileHandlerFunc())
|
||||
routes.Methods("GET, HEAD, OPTIONS", "/assets/*", optionsCorsHandler(), public.FileHandlerFunc())
|
||||
routes.Methods("GET, HEAD", "/avatars/*", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
|
||||
routes.Methods("GET, HEAD", "/repo-avatars/*", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
|
||||
routes.Methods("GET, HEAD", "/apple-touch-icon.png", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
|
||||
@@ -417,7 +432,7 @@ func registerRoutes(m *web.Route) {
|
||||
m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksEditPost)
|
||||
}
|
||||
|
||||
addSettingVariablesRoutes := func() {
|
||||
addSettingsVariablesRoutes := func() {
|
||||
m.Group("/variables", func() {
|
||||
m.Get("", repo_setting.Variables)
|
||||
m.Post("/new", web.Bind(forms.EditVariableForm{}), repo_setting.VariableCreate)
|
||||
@@ -458,8 +473,8 @@ func registerRoutes(m *web.Route) {
|
||||
m.Get("/change-password", func(ctx *context.Context) {
|
||||
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
|
||||
})
|
||||
m.Any("/*", CorsHandler(), public.FileHandlerFunc())
|
||||
}, CorsHandler())
|
||||
m.Methods("GET, HEAD", "/*", public.FileHandlerFunc())
|
||||
}, optionsCorsHandler())
|
||||
|
||||
m.Group("/explore", func() {
|
||||
m.Get("", func(ctx *context.Context) {
|
||||
@@ -532,12 +547,11 @@ func registerRoutes(m *web.Route) {
|
||||
// TODO manage redirection
|
||||
m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth)
|
||||
}, ignSignInAndCsrf, reqSignIn)
|
||||
m.Get("/login/oauth/userinfo", ignSignInAndCsrf, auth.InfoOAuth)
|
||||
m.Options("/login/oauth/access_token", CorsHandler(), misc.DummyBadRequest)
|
||||
m.Post("/login/oauth/access_token", CorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth)
|
||||
m.Get("/login/oauth/keys", ignSignInAndCsrf, auth.OIDCKeys)
|
||||
m.Options("/login/oauth/introspect", CorsHandler(), misc.DummyBadRequest)
|
||||
m.Post("/login/oauth/introspect", CorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth)
|
||||
|
||||
m.Methods("GET, OPTIONS", "/login/oauth/userinfo", optionsCorsHandler(), ignSignInAndCsrf, auth.InfoOAuth)
|
||||
m.Methods("POST, OPTIONS", "/login/oauth/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth)
|
||||
m.Methods("GET, OPTIONS", "/login/oauth/keys", optionsCorsHandler(), ignSignInAndCsrf, auth.OIDCKeys)
|
||||
m.Methods("POST, OPTIONS", "/login/oauth/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth)
|
||||
|
||||
m.Group("/user/settings", func() {
|
||||
m.Get("", user_setting.Profile)
|
||||
@@ -616,7 +630,7 @@ func registerRoutes(m *web.Route) {
|
||||
m.Get("", user_setting.RedirectToDefaultSetting)
|
||||
addSettingsRunnersRoutes()
|
||||
addSettingsSecretsRoutes()
|
||||
addSettingVariablesRoutes()
|
||||
addSettingsVariablesRoutes()
|
||||
}, actions.MustEnableActions)
|
||||
|
||||
m.Get("/organization", user_setting.Organization)
|
||||
@@ -664,6 +678,8 @@ func registerRoutes(m *web.Route) {
|
||||
m.Get("", admin.Dashboard)
|
||||
m.Post("", web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost)
|
||||
|
||||
m.Get("/self_check", admin.SelfCheck)
|
||||
|
||||
m.Group("/config", func() {
|
||||
m.Get("", admin.Config)
|
||||
m.Post("", admin.ChangeConfig)
|
||||
@@ -761,13 +777,14 @@ func registerRoutes(m *web.Route) {
|
||||
m.Group("/actions", func() {
|
||||
m.Get("", admin.RedirectToDefaultSetting)
|
||||
addSettingsRunnersRoutes()
|
||||
addSettingsVariablesRoutes()
|
||||
})
|
||||
}, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enable, "EnablePackages", setting.Packages.Enabled))
|
||||
// ***** END: Admin *****
|
||||
|
||||
m.Group("", func() {
|
||||
m.Get("/{username}", user.UsernameSubRoute)
|
||||
m.Get("/attachments/{uuid}", repo.GetAttachment)
|
||||
m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment)
|
||||
}, ignSignIn)
|
||||
|
||||
m.Post("/{username}", reqSignIn, context_service.UserAssignmentWeb(), user.Action)
|
||||
@@ -903,7 +920,7 @@ func registerRoutes(m *web.Route) {
|
||||
m.Get("", org_setting.RedirectToDefaultSetting)
|
||||
addSettingsRunnersRoutes()
|
||||
addSettingsSecretsRoutes()
|
||||
addSettingVariablesRoutes()
|
||||
addSettingsVariablesRoutes()
|
||||
}, actions.MustEnableActions)
|
||||
|
||||
m.Methods("GET,POST", "/delete", org.SettingsDelete)
|
||||
@@ -1082,7 +1099,7 @@ func registerRoutes(m *web.Route) {
|
||||
m.Get("", repo_setting.RedirectToDefaultSetting)
|
||||
addSettingsRunnersRoutes()
|
||||
addSettingsSecretsRoutes()
|
||||
addSettingVariablesRoutes()
|
||||
addSettingsVariablesRoutes()
|
||||
}, actions.MustEnableActions)
|
||||
// the follow handler must be under "settings", otherwise this incomplete repo can't be accessed
|
||||
m.Group("/migrate", func() {
|
||||
|
||||
Reference in New Issue
Block a user