+++ /dev/null
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "bytes"
- "fmt"
- "net"
- "net/http"
- "strings"
- "text/template"
- "time"
-
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
-)
-
-type routerLoggerOptions struct {
- req *http.Request
- Identity *string
- Start *time.Time
- ResponseWriter http.ResponseWriter
- Ctx map[string]any
- RequestID *string
-}
-
-const keyOfRequestIDInTemplate = ".RequestID"
-
-// According to:
-// TraceId: A valid trace identifier is a 16-byte array with at least one non-zero byte
-// MD5 output is 16 or 32 bytes: md5-bytes is 16, md5-hex is 32
-// SHA1: similar, SHA1-bytes is 20, SHA1-hex is 40.
-// UUID is 128-bit, 32 hex chars, 36 ASCII chars with 4 dashes
-// So, we accept a Request ID with a maximum character length of 40
-const maxRequestIDByteLength = 40
-
-func parseRequestIDFromRequestHeader(req *http.Request) string {
- requestID := "-"
- for _, key := range setting.Log.RequestIDHeaders {
- if req.Header.Get(key) != "" {
- requestID = req.Header.Get(key)
- break
- }
- }
- if len(requestID) > maxRequestIDByteLength {
- requestID = fmt.Sprintf("%s...", requestID[:maxRequestIDByteLength])
- }
- return requestID
-}
-
-// AccessLogger returns a middleware to log access logger
-func AccessLogger() func(http.Handler) http.Handler {
- logger := log.GetLogger("access")
- needRequestID := len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate)
- logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- start := time.Now()
-
- var requestID string
- if needRequestID {
- requestID = parseRequestIDFromRequestHeader(req)
- }
-
- reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
- if err != nil {
- reqHost = req.RemoteAddr
- }
-
- next.ServeHTTP(w, req)
- rw := w.(ResponseWriter)
-
- identity := "-"
- data := middleware.GetContextData(req.Context())
- if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
- identity = signedUser.Name
- }
- buf := bytes.NewBuffer([]byte{})
- err = logTemplate.Execute(buf, routerLoggerOptions{
- req: req,
- Identity: &identity,
- Start: &start,
- ResponseWriter: rw,
- Ctx: map[string]any{
- "RemoteAddr": req.RemoteAddr,
- "RemoteHost": reqHost,
- "Req": req,
- },
- RequestID: &requestID,
- })
- if err != nil {
- log.Error("Could not execute access logger template: %v", err.Error())
- }
-
- logger.Info("%s", buf.String())
- })
- }
-}
+++ /dev/null
-// Copyright 2016 The Gogs Authors. All rights reserved.
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "context"
- "fmt"
- "net/http"
- "net/url"
- "strings"
-
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- mc "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- web_types "code.gitea.io/gitea/modules/web/types"
-
- "gitea.com/go-chi/cache"
-)
-
-// APIContext is a specific context for API service
-type APIContext struct {
- *Base
-
- Cache cache.Cache
-
- Doer *user_model.User // current signed-in user
- IsSigned bool
- IsBasicAuth bool
-
- ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
-
- Repo *Repository
- Org *APIOrganization
- Package *Package
-}
-
-func init() {
- web.RegisterResponseStatusProvider[*APIContext](func(req *http.Request) web_types.ResponseStatusProvider {
- return req.Context().Value(apiContextKey).(*APIContext)
- })
-}
-
-// Currently, we have the following common fields in error response:
-// * message: the message for end users (it shouldn't be used for error type detection)
-// if we need to indicate some errors, we should introduce some new fields like ErrorCode or ErrorType
-// * url: the swagger document URL
-
-// APIError is error format response
-// swagger:response error
-type APIError struct {
- Message string `json:"message"`
- URL string `json:"url"`
-}
-
-// APIValidationError is error format response related to input validation
-// swagger:response validationError
-type APIValidationError struct {
- Message string `json:"message"`
- URL string `json:"url"`
-}
-
-// APIInvalidTopicsError is error format response to invalid topics
-// swagger:response invalidTopicsError
-type APIInvalidTopicsError struct {
- Message string `json:"message"`
- InvalidTopics []string `json:"invalidTopics"`
-}
-
-// APIEmpty is an empty response
-// swagger:response empty
-type APIEmpty struct{}
-
-// APIForbiddenError is a forbidden error response
-// swagger:response forbidden
-type APIForbiddenError struct {
- APIError
-}
-
-// APINotFound is a not found empty response
-// swagger:response notFound
-type APINotFound struct{}
-
-// APIConflict is a conflict empty response
-// swagger:response conflict
-type APIConflict struct{}
-
-// APIRedirect is a redirect response
-// swagger:response redirect
-type APIRedirect struct{}
-
-// APIString is a string response
-// swagger:response string
-type APIString string
-
-// APIRepoArchivedError is an error that is raised when an archived repo should be modified
-// swagger:response repoArchivedError
-type APIRepoArchivedError struct {
- APIError
-}
-
-// ServerError responds with error message, status is 500
-func (ctx *APIContext) ServerError(title string, err error) {
- ctx.Error(http.StatusInternalServerError, title, err)
-}
-
-// Error responds with an error message to client with given obj as the message.
-// If status is 500, also it prints error to log.
-func (ctx *APIContext) Error(status int, title string, obj any) {
- var message string
- if err, ok := obj.(error); ok {
- message = err.Error()
- } else {
- message = fmt.Sprintf("%s", obj)
- }
-
- if status == http.StatusInternalServerError {
- log.ErrorWithSkip(1, "%s: %s", title, message)
-
- if setting.IsProd && !(ctx.Doer != nil && ctx.Doer.IsAdmin) {
- message = ""
- }
- }
-
- ctx.JSON(status, APIError{
- Message: message,
- URL: setting.API.SwaggerURL,
- })
-}
-
-// InternalServerError responds with an error message to the client with the error as a message
-// and the file and line of the caller.
-func (ctx *APIContext) InternalServerError(err error) {
- log.ErrorWithSkip(1, "InternalServerError: %v", err)
-
- var message string
- if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
- message = err.Error()
- }
-
- ctx.JSON(http.StatusInternalServerError, APIError{
- Message: message,
- URL: setting.API.SwaggerURL,
- })
-}
-
-type apiContextKeyType struct{}
-
-var apiContextKey = apiContextKeyType{}
-
-// GetAPIContext returns a context for API routes
-func GetAPIContext(req *http.Request) *APIContext {
- return req.Context().Value(apiContextKey).(*APIContext)
-}
-
-func genAPILinks(curURL *url.URL, total, pageSize, curPage int) []string {
- page := NewPagination(total, pageSize, curPage, 0)
- paginater := page.Paginater
- links := make([]string, 0, 4)
-
- if paginater.HasNext() {
- u := *curURL
- queries := u.Query()
- queries.Set("page", fmt.Sprintf("%d", paginater.Next()))
- u.RawQuery = queries.Encode()
-
- links = append(links, fmt.Sprintf("<%s%s>; rel=\"next\"", setting.AppURL, u.RequestURI()[1:]))
- }
- if !paginater.IsLast() {
- u := *curURL
- queries := u.Query()
- queries.Set("page", fmt.Sprintf("%d", paginater.TotalPages()))
- u.RawQuery = queries.Encode()
-
- links = append(links, fmt.Sprintf("<%s%s>; rel=\"last\"", setting.AppURL, u.RequestURI()[1:]))
- }
- if !paginater.IsFirst() {
- u := *curURL
- queries := u.Query()
- queries.Set("page", "1")
- u.RawQuery = queries.Encode()
-
- links = append(links, fmt.Sprintf("<%s%s>; rel=\"first\"", setting.AppURL, u.RequestURI()[1:]))
- }
- if paginater.HasPrevious() {
- u := *curURL
- queries := u.Query()
- queries.Set("page", fmt.Sprintf("%d", paginater.Previous()))
- u.RawQuery = queries.Encode()
-
- links = append(links, fmt.Sprintf("<%s%s>; rel=\"prev\"", setting.AppURL, u.RequestURI()[1:]))
- }
- return links
-}
-
-// SetLinkHeader sets pagination link header by given total number and page size.
-func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
- links := genAPILinks(ctx.Req.URL, total, pageSize, ctx.FormInt("page"))
-
- if len(links) > 0 {
- ctx.RespHeader().Set("Link", strings.Join(links, ","))
- ctx.AppendAccessControlExposeHeaders("Link")
- }
-}
-
-// APIContexter returns apicontext as middleware
-func APIContexter() func(http.Handler) http.Handler {
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- base, baseCleanUp := NewBaseContext(w, req)
- ctx := &APIContext{
- Base: base,
- Cache: mc.GetCache(),
- Repo: &Repository{PullRequest: &PullRequest{}},
- Org: &APIOrganization{},
- }
- defer baseCleanUp()
-
- ctx.Base.AppendContextValue(apiContextKey, ctx)
- ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
-
- // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
- if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
- if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
- ctx.InternalServerError(err)
- return
- }
- }
-
- httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
- ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
-
- next.ServeHTTP(ctx.Resp, ctx.Req)
- })
- }
-}
-
-// NotFound handles 404s for APIContext
-// String will replace message, errors will be added to a slice
-func (ctx *APIContext) NotFound(objs ...any) {
- message := ctx.Locale.TrString("error.not_found")
- var errors []string
- for _, obj := range objs {
- // Ignore nil
- if obj == nil {
- continue
- }
-
- if err, ok := obj.(error); ok {
- errors = append(errors, err.Error())
- } else {
- message = obj.(string)
- }
- }
-
- ctx.JSON(http.StatusNotFound, map[string]any{
- "message": message,
- "url": setting.API.SwaggerURL,
- "errors": errors,
- })
-}
-
-// ReferencesGitRepo injects the GitRepo into the Context
-// you can optional skip the IsEmpty check
-func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context.CancelFunc) {
- return func(ctx *APIContext) (cancel context.CancelFunc) {
- // Empty repository does not have reference information.
- if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) {
- return nil
- }
-
- // For API calls.
- if ctx.Repo.GitRepo == nil {
- gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
- return cancel
- }
- ctx.Repo.GitRepo = gitRepo
- // We opened it, we should close it
- return func() {
- // If it's been set to nil then assume someone else has closed it.
- if ctx.Repo.GitRepo != nil {
- _ = ctx.Repo.GitRepo.Close()
- }
- }
- }
-
- return cancel
- }
-}
-
-// RepoRefForAPI handles repository reference names when the ref name is not explicitly given
-func RepoRefForAPI(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- ctx := GetAPIContext(req)
-
- if ctx.Repo.GitRepo == nil {
- ctx.InternalServerError(fmt.Errorf("no open git repo"))
- return
- }
-
- if ref := ctx.FormTrim("ref"); len(ref) > 0 {
- commit, err := ctx.Repo.GitRepo.GetCommit(ref)
- if err != nil {
- if git.IsErrNotExist(err) {
- ctx.NotFound()
- } else {
- ctx.Error(http.StatusInternalServerError, "GetCommit", err)
- }
- return
- }
- ctx.Repo.Commit = commit
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- ctx.Repo.TreePath = ctx.Params("*")
- next.ServeHTTP(w, req)
- return
- }
-
- refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
- var err error
-
- if ctx.Repo.GitRepo.IsBranchExist(refName) {
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
- if err != nil {
- ctx.InternalServerError(err)
- return
- }
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if ctx.Repo.GitRepo.IsTagExist(refName) {
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
- if err != nil {
- ctx.InternalServerError(err)
- return
- }
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if len(refName) == ctx.Repo.GetObjectFormat().FullLength() {
- ctx.Repo.CommitID = refName
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
- if err != nil {
- ctx.NotFound("GetCommit", err)
- return
- }
- } else {
- ctx.NotFound(fmt.Errorf("not exist: '%s'", ctx.Params("*")))
- return
- }
-
- next.ServeHTTP(w, req)
- })
-}
-
-// HasAPIError returns true if error occurs in form validation.
-func (ctx *APIContext) HasAPIError() bool {
- hasErr, ok := ctx.Data["HasError"]
- if !ok {
- return false
- }
- return hasErr.(bool)
-}
-
-// GetErrMsg returns error message in form validation.
-func (ctx *APIContext) GetErrMsg() string {
- msg, _ := ctx.Data["ErrorMsg"].(string)
- if msg == "" {
- msg = "invalid form data"
- }
- return msg
-}
-
-// NotFoundOrServerError use error check function to determine if the error
-// is about not found. It responds with 404 status code for not found error,
-// or error context description for logging purpose of 500 server error.
-func (ctx *APIContext) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
- if errCheck(logErr) {
- ctx.JSON(http.StatusNotFound, nil)
- return
- }
- ctx.Error(http.StatusInternalServerError, "NotFoundOrServerError", logMsg)
-}
-
-// IsUserSiteAdmin returns true if current user is a site admin
-func (ctx *APIContext) IsUserSiteAdmin() bool {
- return ctx.IsSigned && ctx.Doer.IsAdmin
-}
-
-// IsUserRepoAdmin returns true if current user is admin in current repo
-func (ctx *APIContext) IsUserRepoAdmin() bool {
- return ctx.Repo.IsAdmin()
-}
-
-// IsUserRepoWriter returns true if current user has write privilege in current repo
-func (ctx *APIContext) IsUserRepoWriter(unitTypes []unit.Type) bool {
- for _, unitType := range unitTypes {
- if ctx.Repo.CanWrite(unitType) {
- return true
- }
- }
-
- return false
-}
+++ /dev/null
-// Copyright 2016 The Gogs Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import "code.gitea.io/gitea/models/organization"
-
-// APIOrganization contains organization and team
-type APIOrganization struct {
- Organization *organization.Organization
- Team *organization.Team
-}
+++ /dev/null
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "net/url"
- "strconv"
- "testing"
-
- "code.gitea.io/gitea/modules/setting"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestGenAPILinks(t *testing.T) {
- setting.AppURL = "http://localhost:3000/"
- kases := map[string][]string{
- "api/v1/repos/jerrykan/example-repo/issues?state=all": {
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=2&state=all>; rel="next"`,
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=5&state=all>; rel="last"`,
- },
- "api/v1/repos/jerrykan/example-repo/issues?state=all&page=1": {
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=2&state=all>; rel="next"`,
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=5&state=all>; rel="last"`,
- },
- "api/v1/repos/jerrykan/example-repo/issues?state=all&page=2": {
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=3&state=all>; rel="next"`,
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=5&state=all>; rel="last"`,
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=1&state=all>; rel="first"`,
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=1&state=all>; rel="prev"`,
- },
- "api/v1/repos/jerrykan/example-repo/issues?state=all&page=5": {
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=1&state=all>; rel="first"`,
- `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=4&state=all>; rel="prev"`,
- },
- }
-
- for req, response := range kases {
- u, err := url.Parse(setting.AppURL + req)
- assert.NoError(t, err)
-
- p := u.Query().Get("page")
- curPage, _ := strconv.Atoi(p)
-
- links := genAPILinks(u, 100, 20, curPage)
-
- assert.EqualValues(t, links, response)
- }
-}
+++ /dev/null
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "context"
- "fmt"
- "html/template"
- "io"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
-
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web/middleware"
-
- "github.com/go-chi/chi/v5"
-)
-
-type contextValuePair struct {
- key any
- valueFn func() any
-}
-
-type Base struct {
- originCtx context.Context
- contextValues []contextValuePair
-
- Resp ResponseWriter
- Req *http.Request
-
- // Data is prepared by ContextDataStore middleware, this field only refers to the pre-created/prepared ContextData.
- // Although it's mainly used for MVC templates, sometimes it's also used to pass data between middlewares/handler
- Data middleware.ContextData
-
- // Locale is mainly for Web context, although the API context also uses it in some cases: message response, form validation
- Locale translation.Locale
-}
-
-func (b *Base) Deadline() (deadline time.Time, ok bool) {
- return b.originCtx.Deadline()
-}
-
-func (b *Base) Done() <-chan struct{} {
- return b.originCtx.Done()
-}
-
-func (b *Base) Err() error {
- return b.originCtx.Err()
-}
-
-func (b *Base) Value(key any) any {
- for _, pair := range b.contextValues {
- if pair.key == key {
- return pair.valueFn()
- }
- }
- return b.originCtx.Value(key)
-}
-
-func (b *Base) AppendContextValueFunc(key any, valueFn func() any) any {
- b.contextValues = append(b.contextValues, contextValuePair{key, valueFn})
- return b
-}
-
-func (b *Base) AppendContextValue(key, value any) any {
- b.contextValues = append(b.contextValues, contextValuePair{key, func() any { return value }})
- return b
-}
-
-func (b *Base) GetData() middleware.ContextData {
- return b.Data
-}
-
-// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
-func (b *Base) AppendAccessControlExposeHeaders(names ...string) {
- val := b.RespHeader().Get("Access-Control-Expose-Headers")
- if len(val) != 0 {
- b.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
- } else {
- b.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
- }
-}
-
-// SetTotalCountHeader set "X-Total-Count" header
-func (b *Base) SetTotalCountHeader(total int64) {
- b.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
- b.AppendAccessControlExposeHeaders("X-Total-Count")
-}
-
-// Written returns true if there are something sent to web browser
-func (b *Base) Written() bool {
- return b.Resp.WrittenStatus() != 0
-}
-
-func (b *Base) WrittenStatus() int {
- return b.Resp.WrittenStatus()
-}
-
-// Status writes status code
-func (b *Base) Status(status int) {
- b.Resp.WriteHeader(status)
-}
-
-// Write writes data to web browser
-func (b *Base) Write(bs []byte) (int, error) {
- return b.Resp.Write(bs)
-}
-
-// RespHeader returns the response header
-func (b *Base) RespHeader() http.Header {
- return b.Resp.Header()
-}
-
-// Error returned an error to web browser
-func (b *Base) Error(status int, contents ...string) {
- v := http.StatusText(status)
- if len(contents) > 0 {
- v = contents[0]
- }
- http.Error(b.Resp, v, status)
-}
-
-// JSON render content as JSON
-func (b *Base) JSON(status int, content any) {
- b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
- b.Resp.WriteHeader(status)
- if err := json.NewEncoder(b.Resp).Encode(content); err != nil {
- log.Error("Render JSON failed: %v", err)
- }
-}
-
-// RemoteAddr returns the client machine ip address
-func (b *Base) RemoteAddr() string {
- return b.Req.RemoteAddr
-}
-
-// Params returns the param on route
-func (b *Base) Params(p string) string {
- s, _ := url.PathUnescape(chi.URLParam(b.Req, strings.TrimPrefix(p, ":")))
- return s
-}
-
-func (b *Base) PathParamRaw(p string) string {
- return chi.URLParam(b.Req, strings.TrimPrefix(p, ":"))
-}
-
-// ParamsInt64 returns the param on route as int64
-func (b *Base) ParamsInt64(p string) int64 {
- v, _ := strconv.ParseInt(b.Params(p), 10, 64)
- return v
-}
-
-// SetParams set params into routes
-func (b *Base) SetParams(k, v string) {
- chiCtx := chi.RouteContext(b)
- chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
-}
-
-// FormString returns the first value matching the provided key in the form as a string
-func (b *Base) FormString(key string) string {
- return b.Req.FormValue(key)
-}
-
-// FormStrings returns a string slice for the provided key from the form
-func (b *Base) FormStrings(key string) []string {
- if b.Req.Form == nil {
- if err := b.Req.ParseMultipartForm(32 << 20); err != nil {
- return nil
- }
- }
- if v, ok := b.Req.Form[key]; ok {
- return v
- }
- return nil
-}
-
-// FormTrim returns the first value for the provided key in the form as a space trimmed string
-func (b *Base) FormTrim(key string) string {
- return strings.TrimSpace(b.Req.FormValue(key))
-}
-
-// FormInt returns the first value for the provided key in the form as an int
-func (b *Base) FormInt(key string) int {
- v, _ := strconv.Atoi(b.Req.FormValue(key))
- return v
-}
-
-// FormInt64 returns the first value for the provided key in the form as an int64
-func (b *Base) FormInt64(key string) int64 {
- v, _ := strconv.ParseInt(b.Req.FormValue(key), 10, 64)
- return v
-}
-
-// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
-func (b *Base) FormBool(key string) bool {
- s := b.Req.FormValue(key)
- v, _ := strconv.ParseBool(s)
- v = v || strings.EqualFold(s, "on")
- return v
-}
-
-// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
-// for the provided key exists in the form else it returns OptionalBoolNone
-func (b *Base) FormOptionalBool(key string) util.OptionalBool {
- value := b.Req.FormValue(key)
- if len(value) == 0 {
- return util.OptionalBoolNone
- }
- s := b.Req.FormValue(key)
- v, _ := strconv.ParseBool(s)
- v = v || strings.EqualFold(s, "on")
- return util.OptionalBoolOf(v)
-}
-
-func (b *Base) SetFormString(key, value string) {
- _ = b.Req.FormValue(key) // force parse form
- b.Req.Form.Set(key, value)
-}
-
-// PlainTextBytes renders bytes as plain text
-func (b *Base) plainTextInternal(skip, status int, bs []byte) {
- statusPrefix := status / 100
- if statusPrefix == 4 || statusPrefix == 5 {
- log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
- }
- b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
- b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
- b.Resp.WriteHeader(status)
- if _, err := b.Resp.Write(bs); err != nil {
- log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
- }
-}
-
-// PlainTextBytes renders bytes as plain text
-func (b *Base) PlainTextBytes(status int, bs []byte) {
- b.plainTextInternal(2, status, bs)
-}
-
-// PlainText renders content as plain text
-func (b *Base) PlainText(status int, text string) {
- b.plainTextInternal(2, status, []byte(text))
-}
-
-// Redirect redirects the request
-func (b *Base) Redirect(location string, status ...int) {
- code := http.StatusSeeOther
- if len(status) == 1 {
- code = status[0]
- }
-
- if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
- // Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
- // 1. the first request to "/my-path" contains cookie
- // 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
- // 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
- // 4. then the browser accepts the empty session, then the user is logged out
- // So in this case, we should remove the session cookie from the response header
- removeSessionCookieHeader(b.Resp)
- }
- // in case the request is made by htmx, have it redirect the browser instead of trying to follow the redirect inside htmx
- if b.Req.Header.Get("HX-Request") == "true" {
- b.Resp.Header().Set("HX-Redirect", location)
- // we have to return a non-redirect status code so XMLHTTPRequest will not immediately follow the redirect
- // so as to give htmx redirect logic a chance to run
- b.Status(http.StatusNoContent)
- return
- }
- http.Redirect(b.Resp, b.Req, location, code)
-}
-
-type ServeHeaderOptions httplib.ServeHeaderOptions
-
-func (b *Base) SetServeHeaders(opt *ServeHeaderOptions) {
- httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opt))
-}
-
-// ServeContent serves content to http request
-func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
- httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opts))
- http.ServeContent(b.Resp, b.Req, opts.Filename, opts.LastModified, r)
-}
-
-// Close frees all resources hold by Context
-func (b *Base) cleanUp() {
- if b.Req != nil && b.Req.MultipartForm != nil {
- _ = b.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
- }
-}
-
-func (b *Base) Tr(msg string, args ...any) template.HTML {
- return b.Locale.Tr(msg, args...)
-}
-
-func (b *Base) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
- return b.Locale.TrN(cnt, key1, keyN, args...)
-}
-
-func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, closeFunc func()) {
- b = &Base{
- originCtx: req.Context(),
- Req: req,
- Resp: WrapResponseWriter(resp),
- Locale: middleware.Locale(resp, req),
- Data: middleware.GetContextData(req.Context()),
- }
- b.AppendContextValue(translation.ContextKey, b.Locale)
- b.Req = b.Req.WithContext(b)
- return b, b.cleanUp
-}
+++ /dev/null
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "fmt"
- "sync"
-
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/hcaptcha"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/mcaptcha"
- "code.gitea.io/gitea/modules/recaptcha"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/turnstile"
-
- "gitea.com/go-chi/captcha"
-)
-
-var (
- imageCaptchaOnce sync.Once
- cpt *captcha.Captcha
-)
-
-// GetImageCaptcha returns global image captcha
-func GetImageCaptcha() *captcha.Captcha {
- imageCaptchaOnce.Do(func() {
- cpt = captcha.NewCaptcha(captcha.Options{
- SubURL: setting.AppSubURL,
- })
- cpt.Store = cache.GetCache()
- })
- return cpt
-}
-
-// SetCaptchaData sets common captcha data
-func SetCaptchaData(ctx *Context) {
- if !setting.Service.EnableCaptcha {
- return
- }
- ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
- ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
- ctx.Data["Captcha"] = GetImageCaptcha()
- ctx.Data["CaptchaType"] = setting.Service.CaptchaType
- ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
- ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
- ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
- ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
- ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
-}
-
-const (
- gRecaptchaResponseField = "g-recaptcha-response"
- hCaptchaResponseField = "h-captcha-response"
- mCaptchaResponseField = "m-captcha-response"
- cfTurnstileResponseField = "cf-turnstile-response"
-)
-
-// VerifyCaptcha verifies Captcha data
-// No-op if captchas are not enabled
-func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) {
- if !setting.Service.EnableCaptcha {
- return
- }
-
- var valid bool
- var err error
- switch setting.Service.CaptchaType {
- case setting.ImageCaptcha:
- valid = GetImageCaptcha().VerifyReq(ctx.Req)
- case setting.ReCaptcha:
- valid, err = recaptcha.Verify(ctx, ctx.Req.Form.Get(gRecaptchaResponseField))
- case setting.HCaptcha:
- valid, err = hcaptcha.Verify(ctx, ctx.Req.Form.Get(hCaptchaResponseField))
- case setting.MCaptcha:
- valid, err = mcaptcha.Verify(ctx, ctx.Req.Form.Get(mCaptchaResponseField))
- case setting.CfTurnstile:
- valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField))
- default:
- ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
- return
- }
- if err != nil {
- log.Debug("%v", err)
- }
-
- if !valid {
- ctx.Data["Err_Captcha"] = true
- ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tpl, form)
- }
-}
+++ /dev/null
-// Copyright 2014 The Gogs Authors. All rights reserved.
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "context"
- "encoding/hex"
- "fmt"
- "html/template"
- "io"
- "net/http"
- "net/url"
- "strings"
- "time"
-
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- mc "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/httpcache"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/modules/web/middleware"
- web_types "code.gitea.io/gitea/modules/web/types"
-
- "gitea.com/go-chi/cache"
- "gitea.com/go-chi/session"
-)
-
-// Render represents a template render
-type Render interface {
- TemplateLookup(tmpl string, templateCtx context.Context) (templates.TemplateExecutor, error)
- HTML(w io.Writer, status int, name string, data any, templateCtx context.Context) error
-}
-
-// Context represents context of a request.
-type Context struct {
- *Base
-
- TemplateContext TemplateContext
-
- Render Render
- PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
-
- Cache cache.Cache
- Csrf CSRFProtector
- Flash *middleware.Flash
- Session session.Store
-
- Link string // current request URL (without query string)
-
- Doer *user_model.User // current signed-in user
- IsSigned bool
- IsBasicAuth bool
-
- ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
-
- Repo *Repository
- Org *Organization
- Package *Package
-}
-
-type TemplateContext map[string]any
-
-func init() {
- web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider {
- return req.Context().Value(WebContextKey).(*Context)
- })
-}
-
-type webContextKeyType struct{}
-
-var WebContextKey = webContextKeyType{}
-
-func GetWebContext(req *http.Request) *Context {
- ctx, _ := req.Context().Value(WebContextKey).(*Context)
- return ctx
-}
-
-// ValidateContext is a special context for form validation middleware. It may be different from other contexts.
-type ValidateContext struct {
- *Base
-}
-
-// GetValidateContext gets a context for middleware form validation
-func GetValidateContext(req *http.Request) (ctx *ValidateContext) {
- if ctxAPI, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
- ctx = &ValidateContext{Base: ctxAPI.Base}
- } else if ctxWeb, ok := req.Context().Value(WebContextKey).(*Context); ok {
- ctx = &ValidateContext{Base: ctxWeb.Base}
- } else {
- panic("invalid context, expect either APIContext or Context")
- }
- return ctx
-}
-
-func NewTemplateContextForWeb(ctx *Context) TemplateContext {
- tmplCtx := NewTemplateContext(ctx)
- tmplCtx["Locale"] = ctx.Base.Locale
- tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
- return tmplCtx
-}
-
-func NewWebContext(base *Base, render Render, session session.Store) *Context {
- ctx := &Context{
- Base: base,
- Render: render,
- Session: session,
-
- Cache: mc.GetCache(),
- Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"),
- Repo: &Repository{PullRequest: &PullRequest{}},
- Org: &Organization{},
- }
- ctx.TemplateContext = NewTemplateContextForWeb(ctx)
- ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}}
- return ctx
-}
-
-// Contexter initializes a classic context for a request.
-func Contexter() func(next http.Handler) http.Handler {
- rnd := templates.HTMLRenderer()
- csrfOpts := CsrfOptions{
- Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
- Cookie: setting.CSRFCookieName,
- SetCookie: true,
- Secure: setting.SessionConfig.Secure,
- CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
- Header: "X-Csrf-Token",
- CookieDomain: setting.SessionConfig.Domain,
- CookiePath: setting.SessionConfig.CookiePath,
- SameSite: setting.SessionConfig.SameSite,
- }
- if !setting.IsProd {
- CsrfTokenRegenerationInterval = 5 * time.Second // in dev, re-generate the tokens more aggressively for debug purpose
- }
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- base, baseCleanUp := NewBaseContext(resp, req)
- defer baseCleanUp()
- ctx := NewWebContext(base, rnd, session.GetSession(req))
-
- ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
- ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
- ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
- ctx.Data["Link"] = ctx.Link
-
- // PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
- ctx.PageData = map[string]any{}
- ctx.Data["PageData"] = ctx.PageData
-
- ctx.Base.AppendContextValue(WebContextKey, ctx)
- ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
-
- ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
-
- // Get the last flash message from cookie
- lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
- if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
- // store last Flash message into the template data, to render it
- ctx.Data["Flash"] = &middleware.Flash{
- DataStore: ctx,
- Values: vals,
- ErrorMsg: vals.Get("error"),
- SuccessMsg: vals.Get("success"),
- InfoMsg: vals.Get("info"),
- WarningMsg: vals.Get("warning"),
- }
- }
-
- // if there are new messages in the ctx.Flash, write them into cookie
- ctx.Resp.Before(func(resp ResponseWriter) {
- if val := ctx.Flash.Encode(); val != "" {
- middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
- } else if lastFlashCookie != "" {
- middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, "", -1)
- }
- })
-
- // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
- if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
- if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
- ctx.ServerError("ParseMultipartForm", err)
- return
- }
- }
-
- httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
- ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
-
- ctx.Data["SystemConfig"] = setting.Config()
- ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
- ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
-
- // FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
- ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
- ctx.Data["DisableStars"] = setting.Repository.DisableStars
- ctx.Data["EnableActions"] = setting.Actions.Enabled
-
- ctx.Data["ManifestData"] = setting.ManifestData
-
- ctx.Data["UnitWikiGlobalDisabled"] = unit.TypeWiki.UnitGlobalDisabled()
- ctx.Data["UnitIssuesGlobalDisabled"] = unit.TypeIssues.UnitGlobalDisabled()
- ctx.Data["UnitPullsGlobalDisabled"] = unit.TypePullRequests.UnitGlobalDisabled()
- ctx.Data["UnitProjectsGlobalDisabled"] = unit.TypeProjects.UnitGlobalDisabled()
- ctx.Data["UnitActionsGlobalDisabled"] = unit.TypeActions.UnitGlobalDisabled()
-
- ctx.Data["AllLangs"] = translation.AllLangs()
-
- next.ServeHTTP(ctx.Resp, ctx.Req)
- })
- }
-}
-
-// HasError returns true if error occurs in form validation.
-// Attention: this function changes ctx.Data and ctx.Flash
-func (ctx *Context) HasError() bool {
- hasErr, ok := ctx.Data["HasError"]
- if !ok {
- return false
- }
- ctx.Flash.ErrorMsg = ctx.GetErrMsg()
- ctx.Data["Flash"] = ctx.Flash
- return hasErr.(bool)
-}
-
-// GetErrMsg returns error message in form validation.
-func (ctx *Context) GetErrMsg() string {
- msg, _ := ctx.Data["ErrorMsg"].(string)
- if msg == "" {
- msg = "invalid form data"
- }
- return msg
-}
-
-func (ctx *Context) JSONRedirect(redirect string) {
- ctx.JSON(http.StatusOK, map[string]any{"redirect": redirect})
-}
-
-func (ctx *Context) JSONOK() {
- ctx.JSON(http.StatusOK, map[string]any{"ok": true}) // this is only a dummy response, frontend seldom uses it
-}
-
-func (ctx *Context) JSONError(msg any) {
- switch v := msg.(type) {
- case string:
- ctx.JSON(http.StatusBadRequest, map[string]any{"errorMessage": v, "renderFormat": "text"})
- case template.HTML:
- ctx.JSON(http.StatusBadRequest, map[string]any{"errorMessage": v, "renderFormat": "html"})
- default:
- panic(fmt.Sprintf("unsupported type: %T", msg))
- }
-}
+++ /dev/null
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "net/http"
- "strings"
-
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web/middleware"
-)
-
-const CookieNameFlash = "gitea_flash"
-
-func removeSessionCookieHeader(w http.ResponseWriter) {
- cookies := w.Header()["Set-Cookie"]
- w.Header().Del("Set-Cookie")
- for _, cookie := range cookies {
- if strings.HasPrefix(cookie, setting.SessionConfig.CookieName+"=") {
- continue
- }
- w.Header().Add("Set-Cookie", cookie)
- }
-}
-
-// SetSiteCookie convenience function to set most cookies consistently
-// CSRF and a few others are the exception here
-func (ctx *Context) SetSiteCookie(name, value string, maxAge int) {
- middleware.SetSiteCookie(ctx.Resp, name, value, maxAge)
-}
-
-// DeleteSiteCookie convenience function to delete most cookies consistently
-// CSRF and a few others are the exception here
-func (ctx *Context) DeleteSiteCookie(name string) {
- middleware.SetSiteCookie(ctx.Resp, name, "", -1)
-}
-
-// GetSiteCookie returns given cookie value from request header.
-func (ctx *Context) GetSiteCookie(name string) string {
- return middleware.GetSiteCookie(ctx.Req, name)
-}
+++ /dev/null
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "code.gitea.io/gitea/models/unit"
-)
-
-// IsUserSiteAdmin returns true if current user is a site admin
-func (ctx *Context) IsUserSiteAdmin() bool {
- return ctx.IsSigned && ctx.Doer.IsAdmin
-}
-
-// IsUserRepoAdmin returns true if current user is admin in current repo
-func (ctx *Context) IsUserRepoAdmin() bool {
- return ctx.Repo.IsAdmin()
-}
-
-// IsUserRepoWriter returns true if current user has write privilege in current repo
-func (ctx *Context) IsUserRepoWriter(unitTypes []unit.Type) bool {
- for _, unitType := range unitTypes {
- if ctx.Repo.CanWrite(unitType) {
- return true
- }
- }
-
- return false
-}
+++ /dev/null
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "io"
- "net/http"
- "strings"
-)
-
-// UploadStream returns the request body or the first form file
-// Only form files need to get closed.
-func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) {
- contentType := strings.ToLower(ctx.Req.Header.Get("Content-Type"))
- if strings.HasPrefix(contentType, "application/x-www-form-urlencoded") || strings.HasPrefix(contentType, "multipart/form-data") {
- if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil {
- return nil, false, err
- }
- if ctx.Req.MultipartForm.File == nil {
- return nil, false, http.ErrMissingFile
- }
- for _, files := range ctx.Req.MultipartForm.File {
- if len(files) > 0 {
- r, err := files[0].Open()
- return r, true, err
- }
- }
- return nil, false, http.ErrMissingFile
- }
- return ctx.Req.Body, false, nil
-}
+++ /dev/null
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "errors"
- "fmt"
- "net"
- "net/http"
- "net/url"
- "path"
- "strconv"
- "strings"
- "time"
-
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/httplib"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/web/middleware"
-)
-
-// RedirectToUser redirect to a differently-named user
-func RedirectToUser(ctx *Base, userName string, redirectUserID int64) {
- user, err := user_model.GetUserByID(ctx, redirectUserID)
- if err != nil {
- ctx.Error(http.StatusInternalServerError, "unable to get user")
- return
- }
-
- redirectPath := strings.Replace(
- ctx.Req.URL.EscapedPath(),
- url.PathEscape(userName),
- url.PathEscape(user.Name),
- 1,
- )
- if ctx.Req.URL.RawQuery != "" {
- redirectPath += "?" + ctx.Req.URL.RawQuery
- }
- ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect)
-}
-
-// RedirectToFirst redirects to first not empty URL
-func (ctx *Context) RedirectToFirst(location ...string) {
- for _, loc := range location {
- if len(loc) == 0 {
- continue
- }
-
- if httplib.IsRiskyRedirectURL(loc) {
- continue
- }
-
- ctx.Redirect(loc)
- return
- }
-
- ctx.Redirect(setting.AppSubURL + "/")
-}
-
-const tplStatus500 base.TplName = "status/500"
-
-// HTML calls Context.HTML and renders the template to HTTP response
-func (ctx *Context) HTML(status int, name base.TplName) {
- log.Debug("Template: %s", name)
-
- tmplStartTime := time.Now()
- if !setting.IsProd {
- ctx.Data["TemplateName"] = name
- }
- ctx.Data["TemplateLoadTimes"] = func() string {
- return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms"
- }
-
- err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext)
- if err == nil {
- return
- }
-
- // if rendering fails, show error page
- if name != tplStatus500 {
- err = fmt.Errorf("failed to render template: %s, error: %s", name, templates.HandleTemplateRenderingError(err))
- ctx.ServerError("Render failed", err) // show the 500 error page
- } else {
- ctx.PlainText(http.StatusInternalServerError, "Unable to render status/500 page, the template system is broken, or Gitea can't find your template files.")
- return
- }
-}
-
-// JSONTemplate renders the template as JSON response
-// keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape
-func (ctx *Context) JSONTemplate(tmpl base.TplName) {
- t, err := ctx.Render.TemplateLookup(string(tmpl), nil)
- if err != nil {
- ctx.ServerError("unable to find template", err)
- return
- }
- ctx.Resp.Header().Set("Content-Type", "application/json")
- if err = t.Execute(ctx.Resp, ctx.Data); err != nil {
- ctx.ServerError("unable to execute template", err)
- }
-}
-
-// RenderToString renders the template content to a string
-func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) {
- var buf strings.Builder
- err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data, ctx.TemplateContext)
- return buf.String(), err
-}
-
-// RenderWithErr used for page has form validation but need to prompt error to users.
-func (ctx *Context) RenderWithErr(msg any, tpl base.TplName, form any) {
- if form != nil {
- middleware.AssignForm(form, ctx.Data)
- }
- ctx.Flash.Error(msg, true)
- ctx.HTML(http.StatusOK, tpl)
-}
-
-// NotFound displays a 404 (Not Found) page and prints the given error, if any.
-func (ctx *Context) NotFound(logMsg string, logErr error) {
- ctx.notFoundInternal(logMsg, logErr)
-}
-
-func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
- if logErr != nil {
- log.Log(2, log.DEBUG, "%s: %v", logMsg, logErr)
- if !setting.IsProd {
- ctx.Data["ErrorMsg"] = logErr
- }
- }
-
- // response simple message if Accept isn't text/html
- showHTML := false
- for _, part := range ctx.Req.Header["Accept"] {
- if strings.Contains(part, "text/html") {
- showHTML = true
- break
- }
- }
-
- if !showHTML {
- ctx.plainTextInternal(3, http.StatusNotFound, []byte("Not found.\n"))
- return
- }
-
- ctx.Data["IsRepo"] = ctx.Repo.Repository != nil
- ctx.Data["Title"] = "Page Not Found"
- ctx.HTML(http.StatusNotFound, base.TplName("status/404"))
-}
-
-// ServerError displays a 500 (Internal Server Error) page and prints the given error, if any.
-func (ctx *Context) ServerError(logMsg string, logErr error) {
- ctx.serverErrorInternal(logMsg, logErr)
-}
-
-func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
- if logErr != nil {
- log.ErrorWithSkip(2, "%s: %v", logMsg, logErr)
- if _, ok := logErr.(*net.OpError); ok || errors.Is(logErr, &net.OpError{}) {
- // This is an error within the underlying connection
- // and further rendering will not work so just return
- return
- }
-
- // it's safe to show internal error to admin users, and it helps
- if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
- ctx.Data["ErrorMsg"] = fmt.Sprintf("%s, %s", logMsg, logErr)
- }
- }
-
- ctx.Data["Title"] = "Internal Server Error"
- ctx.HTML(http.StatusInternalServerError, tplStatus500)
-}
-
-// NotFoundOrServerError use error check function to determine if the error
-// is about not found. It responds with 404 status code for not found error,
-// or error context description for logging purpose of 500 server error.
-// TODO: remove the "errCheck" and use util.ErrNotFound to check
-func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
- if errCheck(logErr) {
- ctx.notFoundInternal(logMsg, logErr)
- return
- }
- ctx.serverErrorInternal(logMsg, logErr)
-}
+++ /dev/null
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "context"
- "time"
-)
-
-var _ context.Context = TemplateContext(nil)
-
-func NewTemplateContext(ctx context.Context) TemplateContext {
- return TemplateContext{"_ctx": ctx}
-}
-
-func (c TemplateContext) parentContext() context.Context {
- return c["_ctx"].(context.Context)
-}
-
-func (c TemplateContext) Deadline() (deadline time.Time, ok bool) {
- return c.parentContext().Deadline()
-}
-
-func (c TemplateContext) Done() <-chan struct{} {
- return c.parentContext().Done()
-}
-
-func (c TemplateContext) Err() error {
- return c.parentContext().Err()
-}
-
-func (c TemplateContext) Value(key any) any {
- return c.parentContext().Value(key)
-}
+++ /dev/null
-// Copyright 2023 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "net/http"
- "net/http/httptest"
- "testing"
-
- "code.gitea.io/gitea/modules/setting"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestRemoveSessionCookieHeader(t *testing.T) {
- w := httptest.NewRecorder()
- w.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "foo"}).String())
- w.Header().Add("Set-Cookie", (&http.Cookie{Name: "other", Value: "bar"}).String())
- assert.Len(t, w.Header().Values("Set-Cookie"), 2)
- removeSessionCookieHeader(w)
- assert.Len(t, w.Header().Values("Set-Cookie"), 1)
- assert.Contains(t, "other=bar", w.Header().Get("Set-Cookie"))
-}
+++ /dev/null
-// Copyright 2013 Martini Authors
-// Copyright 2014 The Macaron Authors
-// Copyright 2021 The Gitea Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-// SPDX-License-Identifier: Apache-2.0
-
-// a middleware that generates and validates CSRF tokens.
-
-package context
-
-import (
- "encoding/base32"
- "fmt"
- "net/http"
- "strconv"
- "time"
-
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/modules/web/middleware"
-)
-
-// CSRFProtector represents a CSRF protector and is used to get the current token and validate the token.
-type CSRFProtector interface {
- // GetHeaderName returns HTTP header to search for token.
- GetHeaderName() string
- // GetFormName returns form value to search for token.
- GetFormName() string
- // GetToken returns the token.
- GetToken() string
- // Validate validates the token in http context.
- Validate(ctx *Context)
- // DeleteCookie deletes the cookie
- DeleteCookie(ctx *Context)
-}
-
-type csrfProtector struct {
- opt CsrfOptions
- // Token generated to pass via header, cookie, or hidden form value.
- Token string
- // This value must be unique per user.
- ID string
-}
-
-// GetHeaderName returns the name of the HTTP header for csrf token.
-func (c *csrfProtector) GetHeaderName() string {
- return c.opt.Header
-}
-
-// GetFormName returns the name of the form value for csrf token.
-func (c *csrfProtector) GetFormName() string {
- return c.opt.Form
-}
-
-// GetToken returns the current token. This is typically used
-// to populate a hidden form in an HTML template.
-func (c *csrfProtector) GetToken() string {
- return c.Token
-}
-
-// CsrfOptions maintains options to manage behavior of Generate.
-type CsrfOptions struct {
- // The global secret value used to generate Tokens.
- Secret string
- // HTTP header used to set and get token.
- Header string
- // Form value used to set and get token.
- Form string
- // Cookie value used to set and get token.
- Cookie string
- // Cookie domain.
- CookieDomain string
- // Cookie path.
- CookiePath string
- CookieHTTPOnly bool
- // SameSite set the cookie SameSite type
- SameSite http.SameSite
- // Key used for getting the unique ID per user.
- SessionKey string
- // oldSessionKey saves old value corresponding to SessionKey.
- oldSessionKey string
- // If true, send token via X-Csrf-Token header.
- SetHeader bool
- // If true, send token via _csrf cookie.
- SetCookie bool
- // Set the Secure flag to true on the cookie.
- Secure bool
- // Disallow Origin appear in request header.
- Origin bool
- // Cookie lifetime. Default is 0
- CookieLifeTime int
-}
-
-func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions {
- if opt.Secret == "" {
- randBytes, err := util.CryptoRandomBytes(8)
- if err != nil {
- // this panic can be handled by the recover() in http handlers
- panic(fmt.Errorf("failed to generate random bytes: %w", err))
- }
- opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
- }
- if opt.Header == "" {
- opt.Header = "X-Csrf-Token"
- }
- if opt.Form == "" {
- opt.Form = "_csrf"
- }
- if opt.Cookie == "" {
- opt.Cookie = "_csrf"
- }
- if opt.CookiePath == "" {
- opt.CookiePath = "/"
- }
- if opt.SessionKey == "" {
- opt.SessionKey = "uid"
- }
- if opt.CookieLifeTime == 0 {
- opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
- }
-
- opt.oldSessionKey = "_old_" + opt.SessionKey
- return opt
-}
-
-func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
- return &http.Cookie{
- Name: c.opt.Cookie,
- Value: value,
- Path: c.opt.CookiePath,
- Domain: c.opt.CookieDomain,
- MaxAge: c.opt.CookieLifeTime,
- Secure: c.opt.Secure,
- HttpOnly: c.opt.CookieHTTPOnly,
- SameSite: c.opt.SameSite,
- }
-}
-
-// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
-// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
-func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
- opt = prepareDefaultCsrfOptions(opt)
- x := &csrfProtector{opt: opt}
-
- if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
- return x
- }
-
- x.ID = "0"
- uidAny := ctx.Session.Get(opt.SessionKey)
- if uidAny != nil {
- switch uidVal := uidAny.(type) {
- case string:
- x.ID = uidVal
- case int64:
- x.ID = strconv.FormatInt(uidVal, 10)
- default:
- log.Error("invalid uid type in session: %T", uidAny)
- }
- }
-
- oldUID := ctx.Session.Get(opt.oldSessionKey)
- uidChanged := oldUID == nil || oldUID.(string) != x.ID
- cookieToken := ctx.GetSiteCookie(opt.Cookie)
-
- needsNew := true
- if uidChanged {
- _ = ctx.Session.Set(opt.oldSessionKey, x.ID)
- } else if cookieToken != "" {
- // If cookie token presents, re-use existing unexpired token, else generate a new one.
- if issueTime, ok := ParseCsrfToken(cookieToken); ok {
- dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
- if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
- x.Token = cookieToken
- needsNew = false
- }
- }
- }
-
- if needsNew {
- // FIXME: actionId.
- x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now())
- if opt.SetCookie {
- cookie := newCsrfCookie(x, x.Token)
- ctx.Resp.Header().Add("Set-Cookie", cookie.String())
- }
- }
-
- if opt.SetHeader {
- ctx.Resp.Header().Add(opt.Header, x.Token)
- }
- return x
-}
-
-func (c *csrfProtector) validateToken(ctx *Context, token string) {
- if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) {
- c.DeleteCookie(ctx)
- if middleware.IsAPIPath(ctx.Req) {
- // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
- http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
- } else {
- ctx.Flash.Error(ctx.Tr("error.invalid_csrf"))
- ctx.Redirect(setting.AppSubURL + "/")
- }
- }
-}
-
-// Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token"
-// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated.
-// If this validation fails, custom Error is sent in the reply.
-// If neither a header nor form value is found, http.StatusBadRequest is sent.
-func (c *csrfProtector) Validate(ctx *Context) {
- if token := ctx.Req.Header.Get(c.GetHeaderName()); token != "" {
- c.validateToken(ctx, token)
- return
- }
- if token := ctx.Req.FormValue(c.GetFormName()); token != "" {
- c.validateToken(ctx, token)
- return
- }
- c.validateToken(ctx, "") // no csrf token, use an empty token to respond error
-}
-
-func (c *csrfProtector) DeleteCookie(ctx *Context) {
- if c.opt.SetCookie {
- cookie := newCsrfCookie(c, "")
- cookie.MaxAge = -1
- ctx.Resp.Header().Add("Set-Cookie", cookie.String())
- }
-}
+++ /dev/null
-// Copyright 2014 The Gogs Authors. All rights reserved.
-// Copyright 2020 The Gitea Authors.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "strings"
-
- "code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/markup"
- "code.gitea.io/gitea/modules/markup/markdown"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
-)
-
-// Organization contains organization context
-type Organization struct {
- IsOwner bool
- IsMember bool
- IsTeamMember bool // Is member of team.
- IsTeamAdmin bool // In owner team or team that has admin permission level.
- Organization *organization.Organization
- OrgLink string
- CanCreateOrgRepo bool
- PublicMemberOnly bool // Only display public members
-
- Team *organization.Team
- Teams []*organization.Team
-}
-
-func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool {
- return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeWrite
-}
-
-func (org *Organization) CanReadUnit(ctx *Context, unitType unit.Type) bool {
- return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeRead
-}
-
-func GetOrganizationByParams(ctx *Context) {
- orgName := ctx.Params(":org")
-
- var err error
-
- ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName)
- if err != nil {
- if organization.IsErrOrgNotExist(err) {
- redirectUserID, err := user_model.LookupUserRedirect(ctx, orgName)
- if err == nil {
- RedirectToUser(ctx.Base, orgName, redirectUserID)
- } else if user_model.IsErrUserRedirectNotExist(err) {
- ctx.NotFound("GetUserByName", err)
- } else {
- ctx.ServerError("LookupUserRedirect", err)
- }
- } else {
- ctx.ServerError("GetUserByName", err)
- }
- return
- }
-}
-
-// HandleOrgAssignment handles organization assignment
-func HandleOrgAssignment(ctx *Context, args ...bool) {
- var (
- requireMember bool
- requireOwner bool
- requireTeamMember bool
- requireTeamAdmin bool
- )
- if len(args) >= 1 {
- requireMember = args[0]
- }
- if len(args) >= 2 {
- requireOwner = args[1]
- }
- if len(args) >= 3 {
- requireTeamMember = args[2]
- }
- if len(args) >= 4 {
- requireTeamAdmin = args[3]
- }
-
- var err error
-
- if ctx.ContextUser == nil {
- // if Organization is not defined, get it from params
- if ctx.Org.Organization == nil {
- GetOrganizationByParams(ctx)
- if ctx.Written() {
- return
- }
- }
- } else if ctx.ContextUser.IsOrganization() {
- if ctx.Org == nil {
- ctx.Org = &Organization{}
- }
- ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser)
- } else {
- // ContextUser is an individual User
- return
- }
-
- org := ctx.Org.Organization
-
- // Handle Visibility
- if org.Visibility != structs.VisibleTypePublic && !ctx.IsSigned {
- // We must be signed in to see limited or private organizations
- ctx.NotFound("OrgAssignment", err)
- return
- }
-
- if org.Visibility == structs.VisibleTypePrivate {
- requireMember = true
- } else if ctx.IsSigned && ctx.Doer.IsRestricted {
- requireMember = true
- }
-
- ctx.ContextUser = org.AsUser()
- ctx.Data["Org"] = org
-
- // Admin has super access.
- if ctx.IsSigned && ctx.Doer.IsAdmin {
- ctx.Org.IsOwner = true
- ctx.Org.IsMember = true
- ctx.Org.IsTeamMember = true
- ctx.Org.IsTeamAdmin = true
- ctx.Org.CanCreateOrgRepo = true
- } else if ctx.IsSigned {
- ctx.Org.IsOwner, err = org.IsOwnedBy(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("IsOwnedBy", err)
- return
- }
-
- if ctx.Org.IsOwner {
- ctx.Org.IsMember = true
- ctx.Org.IsTeamMember = true
- ctx.Org.IsTeamAdmin = true
- ctx.Org.CanCreateOrgRepo = true
- } else {
- ctx.Org.IsMember, err = org.IsOrgMember(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("IsOrgMember", err)
- return
- }
- ctx.Org.CanCreateOrgRepo, err = org.CanCreateOrgRepo(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("CanCreateOrgRepo", err)
- return
- }
- }
- } else {
- // Fake data.
- ctx.Data["SignedUser"] = &user_model.User{}
- }
- if (requireMember && !ctx.Org.IsMember) ||
- (requireOwner && !ctx.Org.IsOwner) {
- ctx.NotFound("OrgAssignment", err)
- return
- }
- ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
- ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
- ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
- ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
- ctx.Data["IsPublicMember"] = func(uid int64) bool {
- is, _ := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, uid)
- return is
- }
- ctx.Data["CanCreateOrgRepo"] = ctx.Org.CanCreateOrgRepo
-
- ctx.Org.OrgLink = org.AsUser().OrganisationLink()
- ctx.Data["OrgLink"] = ctx.Org.OrgLink
-
- // Member
- ctx.Org.PublicMemberOnly = ctx.Doer == nil || !ctx.Org.IsMember && !ctx.Doer.IsAdmin
- opts := &organization.FindOrgMembersOpts{
- OrgID: org.ID,
- PublicOnly: ctx.Org.PublicMemberOnly,
- }
- ctx.Data["NumMembers"], err = organization.CountOrgMembers(ctx, opts)
- if err != nil {
- ctx.ServerError("CountOrgMembers", err)
- return
- }
-
- // Team.
- if ctx.Org.IsMember {
- shouldSeeAllTeams := false
- if ctx.Org.IsOwner {
- shouldSeeAllTeams = true
- } else {
- teams, err := org.GetUserTeams(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("GetUserTeams", err)
- return
- }
- for _, team := range teams {
- if team.IncludesAllRepositories && team.AccessMode >= perm.AccessModeAdmin {
- shouldSeeAllTeams = true
- break
- }
- }
- }
- if shouldSeeAllTeams {
- ctx.Org.Teams, err = org.LoadTeams(ctx)
- if err != nil {
- ctx.ServerError("LoadTeams", err)
- return
- }
- } else {
- ctx.Org.Teams, err = org.GetUserTeams(ctx, ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("GetUserTeams", err)
- return
- }
- }
- ctx.Data["NumTeams"] = len(ctx.Org.Teams)
- }
-
- teamName := ctx.Params(":team")
- if len(teamName) > 0 {
- teamExists := false
- for _, team := range ctx.Org.Teams {
- if team.LowerName == strings.ToLower(teamName) {
- teamExists = true
- ctx.Org.Team = team
- ctx.Org.IsTeamMember = true
- ctx.Data["Team"] = ctx.Org.Team
- break
- }
- }
-
- if !teamExists {
- ctx.NotFound("OrgAssignment", err)
- return
- }
-
- ctx.Data["IsTeamMember"] = ctx.Org.IsTeamMember
- if requireTeamMember && !ctx.Org.IsTeamMember {
- ctx.NotFound("OrgAssignment", err)
- return
- }
-
- ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.AccessMode >= perm.AccessModeAdmin
- ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
- if requireTeamAdmin && !ctx.Org.IsTeamAdmin {
- ctx.NotFound("OrgAssignment", err)
- return
- }
- }
- ctx.Data["ContextUser"] = ctx.ContextUser
-
- ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
- ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages)
- ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode)
-
- ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
- if len(ctx.ContextUser.Description) != 0 {
- content, err := markdown.RenderString(&markup.RenderContext{
- Metas: map[string]string{"mode": "document"},
- Ctx: ctx,
- }, ctx.ContextUser.Description)
- if err != nil {
- ctx.ServerError("RenderString", err)
- return
- }
- ctx.Data["RenderedDescription"] = content
- }
-}
-
-// OrgAssignment returns a middleware to handle organization assignment
-func OrgAssignment(args ...bool) func(ctx *Context) {
- return func(ctx *Context) {
- HandleOrgAssignment(ctx, args...)
- }
-}
+++ /dev/null
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "fmt"
- "net/http"
-
- "code.gitea.io/gitea/models/organization"
- packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/templates"
-)
-
-// Package contains owner, access mode and optional the package descriptor
-type Package struct {
- Owner *user_model.User
- AccessMode perm.AccessMode
- Descriptor *packages_model.PackageDescriptor
-}
-
-type packageAssignmentCtx struct {
- *Base
- Doer *user_model.User
- ContextUser *user_model.User
-}
-
-// PackageAssignment returns a middleware to handle Context.Package assignment
-func PackageAssignment() func(ctx *Context) {
- return func(ctx *Context) {
- errorFn := func(status int, title string, obj any) {
- err, ok := obj.(error)
- if !ok {
- err = fmt.Errorf("%s", obj)
- }
- if status == http.StatusNotFound {
- ctx.NotFound(title, err)
- } else {
- ctx.ServerError(title, err)
- }
- }
- paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
- ctx.Package = packageAssignment(paCtx, errorFn)
- }
-}
-
-// PackageAssignmentAPI returns a middleware to handle Context.Package assignment
-func PackageAssignmentAPI() func(ctx *APIContext) {
- return func(ctx *APIContext) {
- paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
- ctx.Package = packageAssignment(paCtx, ctx.Error)
- }
-}
-
-func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any)) *Package {
- pkg := &Package{
- Owner: ctx.ContextUser,
- }
- var err error
- pkg.AccessMode, err = determineAccessMode(ctx.Base, pkg, ctx.Doer)
- if err != nil {
- errCb(http.StatusInternalServerError, "determineAccessMode", err)
- return pkg
- }
-
- packageType := ctx.Params("type")
- name := ctx.Params("name")
- version := ctx.Params("version")
- if packageType != "" && name != "" && version != "" {
- pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
- if err != nil {
- if err == packages_model.ErrPackageNotExist {
- errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err)
- } else {
- errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err)
- }
- return pkg
- }
-
- pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
- if err != nil {
- errCb(http.StatusInternalServerError, "GetPackageDescriptor", err)
- return pkg
- }
- }
-
- return pkg
-}
-
-func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
- if setting.Service.RequireSignInView && (doer == nil || doer.IsGhost()) {
- return perm.AccessModeNone, nil
- }
-
- if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) {
- return perm.AccessModeNone, nil
- }
-
- // TODO: ActionUser permission check
- accessMode := perm.AccessModeNone
- if pkg.Owner.IsOrganization() {
- org := organization.OrgFromUser(pkg.Owner)
-
- if doer != nil && !doer.IsGhost() {
- // 1. If user is logged in, check all team packages permissions
- var err error
- accessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx, doer.ID)
- if err != nil {
- return accessMode, err
- }
- // If access mode is less than write check every team for more permissions
- // The minimum possible access mode is read for org members
- if accessMode < perm.AccessModeWrite {
- teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
- if err != nil {
- return accessMode, err
- }
- for _, t := range teams {
- perm := t.UnitAccessMode(ctx, unit.TypePackages)
- if accessMode < perm {
- accessMode = perm
- }
- }
- }
- }
- if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
- // 2. If user is unauthorized or no org member, check if org is visible
- accessMode = perm.AccessModeRead
- }
- } else {
- if doer != nil && !doer.IsGhost() {
- // 1. Check if user is package owner
- if doer.ID == pkg.Owner.ID {
- accessMode = perm.AccessModeOwner
- } else if pkg.Owner.Visibility == structs.VisibleTypePublic || pkg.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
- accessMode = perm.AccessModeRead
- }
- } else if pkg.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
- accessMode = perm.AccessModeRead
- }
- }
-
- return accessMode, nil
-}
-
-// PackageContexter initializes a package context for a request.
-func PackageContexter() func(next http.Handler) http.Handler {
- renderer := templates.HTMLRenderer()
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- base, baseCleanUp := NewBaseContext(resp, req)
- defer baseCleanUp()
-
- // it is still needed when rendering 500 page in a package handler
- ctx := NewWebContext(base, renderer, nil)
- ctx.Base.AppendContextValue(WebContextKey, ctx)
- next.ServeHTTP(ctx.Resp, ctx.Req)
- })
- }
-}
+++ /dev/null
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "fmt"
- "html/template"
- "net/url"
- "strings"
-
- "code.gitea.io/gitea/modules/paginator"
-)
-
-// Pagination provides a pagination via paginator.Paginator and additional configurations for the link params used in rendering
-type Pagination struct {
- Paginater *paginator.Paginator
- urlParams []string
-}
-
-// NewPagination creates a new instance of the Pagination struct.
-// "pagingNum" is "page size" or "limit", "current" is "page"
-func NewPagination(total, pagingNum, current, numPages int) *Pagination {
- p := &Pagination{}
- p.Paginater = paginator.New(total, pagingNum, current, numPages)
- return p
-}
-
-// AddParam adds a value from context identified by ctxKey as link param under a given paramKey
-func (p *Pagination) AddParam(ctx *Context, paramKey, ctxKey string) {
- _, exists := ctx.Data[ctxKey]
- if !exists {
- return
- }
- paramData := fmt.Sprintf("%v", ctx.Data[ctxKey]) // cast any to string
- urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(paramKey), url.QueryEscape(paramData))
- p.urlParams = append(p.urlParams, urlParam)
-}
-
-// AddParamString adds a string parameter directly
-func (p *Pagination) AddParamString(key, value string) {
- urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(key), url.QueryEscape(value))
- p.urlParams = append(p.urlParams, urlParam)
-}
-
-// GetParams returns the configured URL params
-func (p *Pagination) GetParams() template.URL {
- return template.URL(strings.Join(p.urlParams, "&"))
-}
-
-// SetDefaultParams sets common pagination params that are often used
-func (p *Pagination) SetDefaultParams(ctx *Context) {
- p.AddParam(ctx, "sort", "SortType")
- p.AddParam(ctx, "q", "Keyword")
- // do not add any more uncommon params here!
- p.AddParam(ctx, "t", "queryType")
-}
+++ /dev/null
-// Copyright 2018 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "net/http"
-
- auth_model "code.gitea.io/gitea/models/auth"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/log"
-)
-
-// RequireRepoAdmin returns a middleware for requiring repository admin permission
-func RequireRepoAdmin() func(ctx *Context) {
- return func(ctx *Context) {
- if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
- return
- }
- }
-}
-
-// RequireRepoWriter returns a middleware for requiring repository write to the specify unitType
-func RequireRepoWriter(unitType unit.Type) func(ctx *Context) {
- return func(ctx *Context) {
- if !ctx.Repo.CanWrite(unitType) {
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
- return
- }
- }
-}
-
-// CanEnableEditor checks if the user is allowed to write to the branch of the repo
-func CanEnableEditor() func(ctx *Context) {
- return func(ctx *Context) {
- if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
- ctx.NotFound("CanWriteToBranch denies permission", nil)
- return
- }
- }
-}
-
-// RequireRepoWriterOr returns a middleware for requiring repository write to one of the unit permission
-func RequireRepoWriterOr(unitTypes ...unit.Type) func(ctx *Context) {
- return func(ctx *Context) {
- for _, unitType := range unitTypes {
- if ctx.Repo.CanWrite(unitType) {
- return
- }
- }
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
- }
-}
-
-// RequireRepoReader returns a middleware for requiring repository read to the specify unitType
-func RequireRepoReader(unitType unit.Type) func(ctx *Context) {
- return func(ctx *Context) {
- if !ctx.Repo.CanRead(unitType) {
- if log.IsTrace() {
- if ctx.IsSigned {
- log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
- "User in Repo has Permissions: %-+v",
- ctx.Doer,
- unitType,
- ctx.Repo.Repository,
- ctx.Repo.Permission)
- } else {
- log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
- "Anonymous user in Repo has Permissions: %-+v",
- unitType,
- ctx.Repo.Repository,
- ctx.Repo.Permission)
- }
- }
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
- return
- }
- }
-}
-
-// RequireRepoReaderOr returns a middleware for requiring repository write to one of the unit permission
-func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) {
- return func(ctx *Context) {
- for _, unitType := range unitTypes {
- if ctx.Repo.CanRead(unitType) {
- return
- }
- }
- if log.IsTrace() {
- var format string
- var args []any
- if ctx.IsSigned {
- format = "Permission Denied: User %-v cannot read ["
- args = append(args, ctx.Doer)
- } else {
- format = "Permission Denied: Anonymous user cannot read ["
- }
- for _, unit := range unitTypes {
- format += "%-v, "
- args = append(args, unit)
- }
-
- format = format[:len(format)-2] + "] in Repo %-v\n" +
- "User in Repo has Permissions: %-+v"
- args = append(args, ctx.Repo.Repository, ctx.Repo.Permission)
- log.Trace(format, args...)
- }
- ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
- }
-}
-
-// CheckRepoScopedToken check whether personal access token has repo scope
-func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository, level auth_model.AccessTokenScopeLevel) {
- if !ctx.IsBasicAuth || ctx.Data["IsApiToken"] != true {
- return
- }
-
- scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
- if ok { // it's a personal access token but not oauth2 token
- var scopeMatched bool
-
- requiredScopes := auth_model.GetRequiredScopes(level, auth_model.AccessTokenScopeCategoryRepository)
-
- // check if scope only applies to public resources
- publicOnly, err := scope.PublicOnly()
- if err != nil {
- ctx.ServerError("HasScope", err)
- return
- }
-
- if publicOnly && repo.IsPrivate {
- ctx.Error(http.StatusForbidden)
- return
- }
-
- scopeMatched, err = scope.HasScope(requiredScopes...)
- if err != nil {
- ctx.ServerError("HasScope", err)
- return
- }
-
- if !scopeMatched {
- ctx.Error(http.StatusForbidden)
- return
- }
- }
-}
+++ /dev/null
-// Copyright 2020 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "context"
- "fmt"
- "net/http"
- "time"
-
- "code.gitea.io/gitea/modules/graceful"
- "code.gitea.io/gitea/modules/process"
- "code.gitea.io/gitea/modules/web"
- web_types "code.gitea.io/gitea/modules/web/types"
-)
-
-// PrivateContext represents a context for private routes
-type PrivateContext struct {
- *Base
- Override context.Context
-
- Repo *Repository
-}
-
-func init() {
- web.RegisterResponseStatusProvider[*PrivateContext](func(req *http.Request) web_types.ResponseStatusProvider {
- return req.Context().Value(privateContextKey).(*PrivateContext)
- })
-}
-
-// Deadline is part of the interface for context.Context and we pass this to the request context
-func (ctx *PrivateContext) Deadline() (deadline time.Time, ok bool) {
- if ctx.Override != nil {
- return ctx.Override.Deadline()
- }
- return ctx.Base.Deadline()
-}
-
-// Done is part of the interface for context.Context and we pass this to the request context
-func (ctx *PrivateContext) Done() <-chan struct{} {
- if ctx.Override != nil {
- return ctx.Override.Done()
- }
- return ctx.Base.Done()
-}
-
-// Err is part of the interface for context.Context and we pass this to the request context
-func (ctx *PrivateContext) Err() error {
- if ctx.Override != nil {
- return ctx.Override.Err()
- }
- return ctx.Base.Err()
-}
-
-var privateContextKey any = "default_private_context"
-
-// GetPrivateContext returns a context for Private routes
-func GetPrivateContext(req *http.Request) *PrivateContext {
- return req.Context().Value(privateContextKey).(*PrivateContext)
-}
-
-// PrivateContexter returns apicontext as middleware
-func PrivateContexter() func(http.Handler) http.Handler {
- return func(next http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- base, baseCleanUp := NewBaseContext(w, req)
- ctx := &PrivateContext{Base: base}
- defer baseCleanUp()
- ctx.Base.AppendContextValue(privateContextKey, ctx)
-
- next.ServeHTTP(ctx.Resp, ctx.Req)
- })
- }
-}
-
-// OverrideContext overrides the underlying request context for Done() etc.
-// This function should be used when there is a need for work to continue even if the request has been cancelled.
-// Primarily this affects hook/post-receive and hook/proc-receive both of which need to continue working even if
-// the underlying request has timed out from the ssh/http push
-func OverrideContext(ctx *PrivateContext) (cancel context.CancelFunc) {
- // We now need to override the request context as the base for our work because even if the request is cancelled we have to continue this work
- ctx.Override, _, cancel = process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PrivateContext: %s", ctx.Req.RequestURI), process.RequestProcessType, true)
- return cancel
-}
+++ /dev/null
-// Copyright 2014 The Gogs Authors. All rights reserved.
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "context"
- "errors"
- "fmt"
- "html"
- "net/http"
- "net/url"
- "path"
- "strings"
-
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/db"
- git_model "code.gitea.io/gitea/models/git"
- issues_model "code.gitea.io/gitea/models/issues"
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- unit_model "code.gitea.io/gitea/models/unit"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/gitrepo"
- code_indexer "code.gitea.io/gitea/modules/indexer/code"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/optional"
- repo_module "code.gitea.io/gitea/modules/repository"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
- asymkey_service "code.gitea.io/gitea/services/asymkey"
-
- "github.com/editorconfig/editorconfig-core-go/v2"
-)
-
-// PullRequest contains information to make a pull request
-type PullRequest struct {
- BaseRepo *repo_model.Repository
- Allowed bool
- SameRepo bool
- HeadInfoSubURL string // [<user>:]<branch> url segment
-}
-
-// Repository contains information to operate a repository
-type Repository struct {
- access_model.Permission
- IsWatching bool
- IsViewBranch bool
- IsViewTag bool
- IsViewCommit bool
- Repository *repo_model.Repository
- Owner *user_model.User
- Commit *git.Commit
- Tag *git.Tag
- GitRepo *git.Repository
- RefName string
- BranchName string
- TagName string
- TreePath string
- CommitID string
- RepoLink string
- CloneLink repo_model.CloneLink
- CommitsCount int64
-
- PullRequest *PullRequest
-}
-
-// CanWriteToBranch checks if the branch is writable by the user
-func (r *Repository) CanWriteToBranch(ctx context.Context, user *user_model.User, branch string) bool {
- return issues_model.CanMaintainerWriteToBranch(ctx, r.Permission, branch, user)
-}
-
-// CanEnableEditor returns true if repository is editable and user has proper access level.
-func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User) bool {
- return r.IsViewBranch && r.CanWriteToBranch(ctx, user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived
-}
-
-// CanCreateBranch returns true if repository is editable and user has proper access level.
-func (r *Repository) CanCreateBranch() bool {
- return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch()
-}
-
-func (r *Repository) GetObjectFormat() git.ObjectFormat {
- return git.ObjectFormatFromName(r.Repository.ObjectFormatName)
-}
-
-// RepoMustNotBeArchived checks if a repo is archived
-func RepoMustNotBeArchived() func(ctx *Context) {
- return func(ctx *Context) {
- if ctx.Repo.Repository.IsArchived {
- ctx.NotFound("IsArchived", errors.New(ctx.Locale.TrString("repo.archive.title")))
- }
- }
-}
-
-// CanCommitToBranchResults represents the results of CanCommitToBranch
-type CanCommitToBranchResults struct {
- CanCommitToBranch bool
- EditorEnabled bool
- UserCanPush bool
- RequireSigned bool
- WillSign bool
- SigningKey string
- WontSignReason string
-}
-
-// CanCommitToBranch returns true if repository is editable and user has proper access level
-//
-// and branch is not protected for push
-func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.User) (CanCommitToBranchResults, error) {
- protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, r.Repository.ID, r.BranchName)
- if err != nil {
- return CanCommitToBranchResults{}, err
- }
- userCanPush := true
- requireSigned := false
- if protectedBranch != nil {
- protectedBranch.Repo = r.Repository
- userCanPush = protectedBranch.CanUserPush(ctx, doer)
- requireSigned = protectedBranch.RequireSignedCommits
- }
-
- sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName)
-
- canCommit := r.CanEnableEditor(ctx, doer) && userCanPush
- if requireSigned {
- canCommit = canCommit && sign
- }
- wontSignReason := ""
- if err != nil {
- if asymkey_service.IsErrWontSign(err) {
- wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason)
- err = nil
- } else {
- wontSignReason = "error"
- }
- }
-
- return CanCommitToBranchResults{
- CanCommitToBranch: canCommit,
- EditorEnabled: r.CanEnableEditor(ctx, doer),
- UserCanPush: userCanPush,
- RequireSigned: requireSigned,
- WillSign: sign,
- SigningKey: keyID,
- WontSignReason: wontSignReason,
- }, err
-}
-
-// CanUseTimetracker returns whether or not a user can use the timetracker.
-func (r *Repository) CanUseTimetracker(ctx context.Context, issue *issues_model.Issue, user *user_model.User) bool {
- // Checking for following:
- // 1. Is timetracker enabled
- // 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
- isAssigned, _ := issues_model.IsUserAssignedToIssue(ctx, issue, user)
- return r.Repository.IsTimetrackerEnabled(ctx) && (!r.Repository.AllowOnlyContributorsToTrackTime(ctx) ||
- r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
-}
-
-// CanCreateIssueDependencies returns whether or not a user can create dependencies.
-func (r *Repository) CanCreateIssueDependencies(ctx context.Context, user *user_model.User, isPull bool) bool {
- return r.Repository.IsDependenciesEnabled(ctx) && r.Permission.CanWriteIssuesOrPulls(isPull)
-}
-
-// GetCommitsCount returns cached commit count for current view
-func (r *Repository) GetCommitsCount() (int64, error) {
- if r.Commit == nil {
- return 0, nil
- }
- var contextName string
- if r.IsViewBranch {
- contextName = r.BranchName
- } else if r.IsViewTag {
- contextName = r.TagName
- } else {
- contextName = r.CommitID
- }
- return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, r.IsViewBranch || r.IsViewTag), func() (int64, error) {
- return r.Commit.CommitsCount()
- })
-}
-
-// GetCommitGraphsCount returns cached commit count for current view
-func (r *Repository) GetCommitGraphsCount(ctx context.Context, hidePRRefs bool, branches, files []string) (int64, error) {
- cacheKey := fmt.Sprintf("commits-count-%d-graph-%t-%s-%s", r.Repository.ID, hidePRRefs, branches, files)
-
- return cache.GetInt64(cacheKey, func() (int64, error) {
- if len(branches) == 0 {
- return git.AllCommitsCount(ctx, r.Repository.RepoPath(), hidePRRefs, files...)
- }
- return git.CommitsCount(ctx,
- git.CommitsCountOptions{
- RepoPath: r.Repository.RepoPath(),
- Revision: branches,
- RelPath: files,
- })
- })
-}
-
-// BranchNameSubURL sub-URL for the BranchName field
-func (r *Repository) BranchNameSubURL() string {
- switch {
- case r.IsViewBranch:
- return "branch/" + util.PathEscapeSegments(r.BranchName)
- case r.IsViewTag:
- return "tag/" + util.PathEscapeSegments(r.TagName)
- case r.IsViewCommit:
- return "commit/" + util.PathEscapeSegments(r.CommitID)
- }
- log.Error("Unknown view type for repo: %v", r)
- return ""
-}
-
-// FileExists returns true if a file exists in the given repo branch
-func (r *Repository) FileExists(path, branch string) (bool, error) {
- if branch == "" {
- branch = r.Repository.DefaultBranch
- }
- commit, err := r.GitRepo.GetBranchCommit(branch)
- if err != nil {
- return false, err
- }
- if _, err := commit.GetTreeEntryByPath(path); err != nil {
- return false, err
- }
- return true, nil
-}
-
-// GetEditorconfig returns the .editorconfig definition if found in the
-// HEAD of the default repo branch.
-func (r *Repository) GetEditorconfig(optCommit ...*git.Commit) (cfg *editorconfig.Editorconfig, warning, err error) {
- if r.GitRepo == nil {
- return nil, nil, nil
- }
-
- var commit *git.Commit
-
- if len(optCommit) != 0 {
- commit = optCommit[0]
- } else {
- commit, err = r.GitRepo.GetBranchCommit(r.Repository.DefaultBranch)
- if err != nil {
- return nil, nil, err
- }
- }
- treeEntry, err := commit.GetTreeEntryByPath(".editorconfig")
- if err != nil {
- return nil, nil, err
- }
- if treeEntry.Blob().Size() >= setting.UI.MaxDisplayFileSize {
- return nil, nil, git.ErrNotExist{ID: "", RelPath: ".editorconfig"}
- }
- reader, err := treeEntry.Blob().DataAsync()
- if err != nil {
- return nil, nil, err
- }
- defer reader.Close()
- return editorconfig.ParseGraceful(reader)
-}
-
-// RetrieveBaseRepo retrieves base repository
-func RetrieveBaseRepo(ctx *Context, repo *repo_model.Repository) {
- // Non-fork repository will not return error in this method.
- if err := repo.GetBaseRepo(ctx); err != nil {
- if repo_model.IsErrRepoNotExist(err) {
- repo.IsFork = false
- repo.ForkID = 0
- return
- }
- ctx.ServerError("GetBaseRepo", err)
- return
- } else if err = repo.BaseRepo.LoadOwner(ctx); err != nil {
- ctx.ServerError("BaseRepo.LoadOwner", err)
- return
- }
-}
-
-// RetrieveTemplateRepo retrieves template repository used to generate this repository
-func RetrieveTemplateRepo(ctx *Context, repo *repo_model.Repository) {
- // Non-generated repository will not return error in this method.
- templateRepo, err := repo_model.GetTemplateRepo(ctx, repo)
- if err != nil {
- if repo_model.IsErrRepoNotExist(err) {
- repo.TemplateID = 0
- return
- }
- ctx.ServerError("GetTemplateRepo", err)
- return
- } else if err = templateRepo.LoadOwner(ctx); err != nil {
- ctx.ServerError("TemplateRepo.LoadOwner", err)
- return
- }
-
- perm, err := access_model.GetUserRepoPermission(ctx, templateRepo, ctx.Doer)
- if err != nil {
- ctx.ServerError("GetUserRepoPermission", err)
- return
- }
-
- if !perm.CanRead(unit_model.TypeCode) {
- repo.TemplateID = 0
- }
-}
-
-// ComposeGoGetImport returns go-get-import meta content.
-func ComposeGoGetImport(owner, repo string) string {
- /// setting.AppUrl is guaranteed to be parse as url
- appURL, _ := url.Parse(setting.AppURL)
-
- return path.Join(appURL.Host, setting.AppSubURL, url.PathEscape(owner), url.PathEscape(repo))
-}
-
-// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
-// if user does not have actual access to the requested repository,
-// or the owner or repository does not exist at all.
-// This is particular a workaround for "go get" command which does not respect
-// .netrc file.
-func EarlyResponseForGoGetMeta(ctx *Context) {
- username := ctx.Params(":username")
- reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
- if username == "" || reponame == "" {
- ctx.PlainText(http.StatusBadRequest, "invalid repository path")
- return
- }
-
- var cloneURL string
- if setting.Repository.GoGetCloneURLProtocol == "ssh" {
- cloneURL = repo_model.ComposeSSHCloneURL(username, reponame)
- } else {
- cloneURL = repo_model.ComposeHTTPSCloneURL(username, reponame)
- }
- goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(username, reponame), cloneURL)
- htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
- ctx.PlainText(http.StatusOK, htmlMeta)
-}
-
-// RedirectToRepo redirect to a differently-named repository
-func RedirectToRepo(ctx *Base, redirectRepoID int64) {
- ownerName := ctx.Params(":username")
- previousRepoName := ctx.Params(":reponame")
-
- repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID)
- if err != nil {
- log.Error("GetRepositoryByID: %v", err)
- ctx.Error(http.StatusInternalServerError, "GetRepositoryByID")
- return
- }
-
- redirectPath := strings.Replace(
- ctx.Req.URL.EscapedPath(),
- url.PathEscape(ownerName)+"/"+url.PathEscape(previousRepoName),
- url.PathEscape(repo.OwnerName)+"/"+url.PathEscape(repo.Name),
- 1,
- )
- if ctx.Req.URL.RawQuery != "" {
- redirectPath += "?" + ctx.Req.URL.RawQuery
- }
- ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect)
-}
-
-func repoAssignment(ctx *Context, repo *repo_model.Repository) {
- var err error
- if err = repo.LoadOwner(ctx); err != nil {
- ctx.ServerError("LoadOwner", err)
- return
- }
-
- ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
- if err != nil {
- ctx.ServerError("GetUserRepoPermission", err)
- return
- }
-
- // Check access.
- if !ctx.Repo.Permission.HasAccess() {
- if ctx.FormString("go-get") == "1" {
- EarlyResponseForGoGetMeta(ctx)
- return
- }
- ctx.NotFound("no access right", nil)
- return
- }
- ctx.Data["HasAccess"] = true
- ctx.Data["Permission"] = &ctx.Repo.Permission
-
- if repo.IsMirror {
- pullMirror, err := repo_model.GetMirrorByRepoID(ctx, repo.ID)
- if err == nil {
- ctx.Data["PullMirror"] = pullMirror
- } else if err != repo_model.ErrMirrorNotExist {
- ctx.ServerError("GetMirrorByRepoID", err)
- return
- }
- }
-
- pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
- if err != nil {
- ctx.ServerError("GetPushMirrorsByRepoID", err)
- return
- }
-
- ctx.Repo.Repository = repo
- ctx.Data["PushMirrors"] = pushMirrors
- ctx.Data["RepoName"] = ctx.Repo.Repository.Name
- ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty
-}
-
-// RepoIDAssignment returns a handler which assigns the repo to the context.
-func RepoIDAssignment() func(ctx *Context) {
- return func(ctx *Context) {
- repoID := ctx.ParamsInt64(":repoid")
-
- // Get repository.
- repo, err := repo_model.GetRepositoryByID(ctx, repoID)
- if err != nil {
- if repo_model.IsErrRepoNotExist(err) {
- ctx.NotFound("GetRepositoryByID", nil)
- } else {
- ctx.ServerError("GetRepositoryByID", err)
- }
- return
- }
-
- repoAssignment(ctx, repo)
- }
-}
-
-// RepoAssignment returns a middleware to handle repository assignment
-func RepoAssignment(ctx *Context) context.CancelFunc {
- if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
- log.Trace("RepoAssignment was exec already, skipping second call ...")
- return nil
- }
- ctx.Data["repoAssignmentExecuted"] = true
-
- var (
- owner *user_model.User
- err error
- )
-
- userName := ctx.Params(":username")
- repoName := ctx.Params(":reponame")
- repoName = strings.TrimSuffix(repoName, ".git")
- if setting.Other.EnableFeed {
- repoName = strings.TrimSuffix(repoName, ".rss")
- repoName = strings.TrimSuffix(repoName, ".atom")
- }
-
- // Check if the user is the same as the repository owner
- if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) {
- owner = ctx.Doer
- } else {
- owner, err = user_model.GetUserByName(ctx, userName)
- if err != nil {
- if user_model.IsErrUserNotExist(err) {
- // go-get does not support redirects
- // https://github.com/golang/go/issues/19760
- if ctx.FormString("go-get") == "1" {
- EarlyResponseForGoGetMeta(ctx)
- return nil
- }
-
- if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil {
- RedirectToUser(ctx.Base, userName, redirectUserID)
- } else if user_model.IsErrUserRedirectNotExist(err) {
- ctx.NotFound("GetUserByName", nil)
- } else {
- ctx.ServerError("LookupUserRedirect", err)
- }
- } else {
- ctx.ServerError("GetUserByName", err)
- }
- return nil
- }
- }
- ctx.Repo.Owner = owner
- ctx.ContextUser = owner
- ctx.Data["ContextUser"] = ctx.ContextUser
- ctx.Data["Username"] = ctx.Repo.Owner.Name
-
- // redirect link to wiki
- if strings.HasSuffix(repoName, ".wiki") {
- // ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added
- // Now we happen to know that all of our paths are: /:username/:reponame/whatever_else
- originalRepoName := ctx.Params(":reponame")
- redirectRepoName := strings.TrimSuffix(repoName, ".wiki")
- redirectRepoName += originalRepoName[len(redirectRepoName)+5:]
- redirectPath := strings.Replace(
- ctx.Req.URL.EscapedPath(),
- url.PathEscape(userName)+"/"+url.PathEscape(originalRepoName),
- url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki",
- 1,
- )
- if ctx.Req.URL.RawQuery != "" {
- redirectPath += "?" + ctx.Req.URL.RawQuery
- }
- ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
- return nil
- }
-
- // Get repository.
- repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName)
- if err != nil {
- if repo_model.IsErrRepoNotExist(err) {
- redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName)
- if err == nil {
- RedirectToRepo(ctx.Base, redirectRepoID)
- } else if repo_model.IsErrRedirectNotExist(err) {
- if ctx.FormString("go-get") == "1" {
- EarlyResponseForGoGetMeta(ctx)
- return nil
- }
- ctx.NotFound("GetRepositoryByName", nil)
- } else {
- ctx.ServerError("LookupRepoRedirect", err)
- }
- } else {
- ctx.ServerError("GetRepositoryByName", err)
- }
- return nil
- }
- repo.Owner = owner
-
- repoAssignment(ctx, repo)
- if ctx.Written() {
- return nil
- }
-
- ctx.Repo.RepoLink = repo.Link()
- ctx.Data["RepoLink"] = ctx.Repo.RepoLink
- ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
-
- if setting.Other.EnableFeed {
- ctx.Data["EnableFeed"] = true
- ctx.Data["FeedURL"] = ctx.Repo.RepoLink
- }
-
- unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker)
- if err == nil {
- ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
- }
-
- ctx.Data["NumTags"], err = db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
- IncludeDrafts: true,
- IncludeTags: true,
- HasSha1: util.OptionalBoolTrue, // only draft releases which are created with existing tags
- RepoID: ctx.Repo.Repository.ID,
- })
- if err != nil {
- ctx.ServerError("GetReleaseCountByRepoID", err)
- return nil
- }
- ctx.Data["NumReleases"], err = db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
- // only show draft releases for users who can write, read-only users shouldn't see draft releases.
- IncludeDrafts: ctx.Repo.CanWrite(unit_model.TypeReleases),
- RepoID: ctx.Repo.Repository.ID,
- })
- if err != nil {
- ctx.ServerError("GetReleaseCountByRepoID", err)
- return nil
- }
-
- ctx.Data["Title"] = owner.Name + "/" + repo.Name
- ctx.Data["Repository"] = repo
- ctx.Data["Owner"] = ctx.Repo.Repository.Owner
- ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
- ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
- ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization()
- ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
- ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
- ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
- ctx.Data["CanWriteActions"] = ctx.Repo.CanWrite(unit_model.TypeActions)
-
- canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository)
- if err != nil {
- ctx.ServerError("CanUserForkRepo", err)
- return nil
- }
- ctx.Data["CanSignedUserFork"] = canSignedUserFork
-
- userAndOrgForks, err := repo_model.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository)
- if err != nil {
- ctx.ServerError("GetForksByUserAndOrgs", err)
- return nil
- }
- ctx.Data["UserAndOrgForks"] = userAndOrgForks
-
- // canSignedUserFork is true if the current user doesn't have a fork of this repo yet or
- // if he owns an org that doesn't have a fork of this repo yet
- // If multiple forks are available or if the user can fork to another account, but there is already a fork: open selection dialog
- ctx.Data["ShowForkModal"] = len(userAndOrgForks) > 1 || (canSignedUserFork && len(userAndOrgForks) > 0)
-
- ctx.Data["RepoCloneLink"] = repo.CloneLink()
-
- cloneButtonShowHTTPS := !setting.Repository.DisableHTTPGit
- cloneButtonShowSSH := !setting.SSH.Disabled && (ctx.IsSigned || setting.SSH.ExposeAnonymous)
- if !cloneButtonShowHTTPS && !cloneButtonShowSSH {
- // We have to show at least one link, so we just show the HTTPS
- cloneButtonShowHTTPS = true
- }
- ctx.Data["CloneButtonShowHTTPS"] = cloneButtonShowHTTPS
- ctx.Data["CloneButtonShowSSH"] = cloneButtonShowSSH
- ctx.Data["CloneButtonOriginLink"] = ctx.Data["RepoCloneLink"] // it may be rewritten to the WikiCloneLink by the router middleware
-
- ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled
- if setting.Indexer.RepoIndexerEnabled {
- ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
- }
-
- if ctx.IsSigned {
- ctx.Data["IsWatchingRepo"] = repo_model.IsWatching(ctx, ctx.Doer.ID, repo.ID)
- ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, repo.ID)
- }
-
- if repo.IsFork {
- RetrieveBaseRepo(ctx, repo)
- if ctx.Written() {
- return nil
- }
- }
-
- if repo.IsGenerated() {
- RetrieveTemplateRepo(ctx, repo)
- if ctx.Written() {
- return nil
- }
- }
-
- isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || ctx.Link == ctx.Repo.RepoLink+"/settings" || strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/")
-
- // Disable everything when the repo is being created
- if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() {
- ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
- if !isHomeOrSettings {
- ctx.Redirect(ctx.Repo.RepoLink)
- }
- return nil
- }
-
- gitRepo, err := gitrepo.OpenRepository(ctx, repo)
- if err != nil {
- if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
- log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
- ctx.Repo.Repository.MarkAsBrokenEmpty()
- ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
- // Only allow access to base of repo or settings
- if !isHomeOrSettings {
- ctx.Redirect(ctx.Repo.RepoLink)
- }
- return nil
- }
- ctx.ServerError("RepoAssignment Invalid repo "+repo.FullName(), err)
- return nil
- }
- if ctx.Repo.GitRepo != nil {
- ctx.Repo.GitRepo.Close()
- }
- ctx.Repo.GitRepo = gitRepo
-
- // We opened it, we should close it
- cancel := func() {
- // If it's been set to nil then assume someone else has closed it.
- if ctx.Repo.GitRepo != nil {
- ctx.Repo.GitRepo.Close()
- }
- }
-
- // Stop at this point when the repo is empty.
- if ctx.Repo.Repository.IsEmpty {
- ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
- return cancel
- }
-
- branchOpts := git_model.FindBranchOptions{
- RepoID: ctx.Repo.Repository.ID,
- IsDeletedBranch: optional.Some(false),
- ListOptions: db.ListOptionsAll,
- }
- branchesTotal, err := db.Count[git_model.Branch](ctx, branchOpts)
- if err != nil {
- ctx.ServerError("CountBranches", err)
- return cancel
- }
-
- // non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
- if branchesTotal == 0 { // fallback to do a sync immediately
- branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
- if err != nil {
- ctx.ServerError("SyncRepoBranches", err)
- return cancel
- }
- }
-
- ctx.Data["BranchesCount"] = branchesTotal
-
- // If no branch is set in the request URL, try to guess a default one.
- if len(ctx.Repo.BranchName) == 0 {
- if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
- ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
- } else {
- ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch()
- if ctx.Repo.BranchName == "" {
- // If it still can't get a default branch, fall back to default branch from setting.
- // Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
- ctx.Repo.BranchName = setting.Repository.DefaultBranch
- }
- }
- ctx.Repo.RefName = ctx.Repo.BranchName
- }
- ctx.Data["BranchName"] = ctx.Repo.BranchName
-
- // People who have push access or have forked repository can propose a new pull request.
- canPush := ctx.Repo.CanWrite(unit_model.TypeCode) ||
- (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))
- canCompare := false
-
- // Pull request is allowed if this is a fork repository
- // and base repository accepts pull requests.
- if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls(ctx) {
- canCompare = true
- ctx.Data["BaseRepo"] = repo.BaseRepo
- ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
- ctx.Repo.PullRequest.Allowed = canPush
- ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.Repo.Owner.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
- } else if repo.AllowsPulls(ctx) {
- // Or, this is repository accepts pull requests between branches.
- canCompare = true
- ctx.Data["BaseRepo"] = repo
- ctx.Repo.PullRequest.BaseRepo = repo
- ctx.Repo.PullRequest.Allowed = canPush
- ctx.Repo.PullRequest.SameRepo = true
- ctx.Repo.PullRequest.HeadInfoSubURL = util.PathEscapeSegments(ctx.Repo.BranchName)
- }
- ctx.Data["CanCompareOrPull"] = canCompare
- ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
-
- if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer {
- repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
- if err != nil {
- ctx.ServerError("GetPendingRepositoryTransfer", err)
- return cancel
- }
-
- if err := repoTransfer.LoadAttributes(ctx); err != nil {
- ctx.ServerError("LoadRecipient", err)
- return cancel
- }
-
- ctx.Data["RepoTransfer"] = repoTransfer
- if ctx.Doer != nil {
- ctx.Data["CanUserAcceptTransfer"] = repoTransfer.CanUserAcceptTransfer(ctx, ctx.Doer)
- }
- }
-
- if ctx.FormString("go-get") == "1" {
- ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
- fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
- ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}"
- ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}"
- }
- return cancel
-}
-
-// RepoRefType type of repo reference
-type RepoRefType int
-
-const (
- // RepoRefLegacy unknown type, make educated guess and redirect.
- // for backward compatibility with previous URL scheme
- RepoRefLegacy RepoRefType = iota
- // RepoRefAny is for usage where educated guess is needed
- // but redirect can not be made
- RepoRefAny
- // RepoRefBranch branch
- RepoRefBranch
- // RepoRefTag tag
- RepoRefTag
- // RepoRefCommit commit
- RepoRefCommit
- // RepoRefBlob blob
- RepoRefBlob
-)
-
-const headRefName = "HEAD"
-
-// RepoRef handles repository reference names when the ref name is not
-// explicitly given
-func RepoRef() func(*Context) context.CancelFunc {
- // since no ref name is explicitly specified, ok to just use branch
- return RepoRefByType(RepoRefBranch)
-}
-
-// RefTypeIncludesBranches returns true if ref type can be a branch
-func (rt RepoRefType) RefTypeIncludesBranches() bool {
- if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefBranch {
- return true
- }
- return false
-}
-
-// RefTypeIncludesTags returns true if ref type can be a tag
-func (rt RepoRefType) RefTypeIncludesTags() bool {
- if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefTag {
- return true
- }
- return false
-}
-
-func getRefNameFromPath(ctx *Base, repo *Repository, path string, isExist func(string) bool) string {
- refName := ""
- parts := strings.Split(path, "/")
- for i, part := range parts {
- refName = strings.TrimPrefix(refName+"/"+part, "/")
- if isExist(refName) {
- repo.TreePath = strings.Join(parts[i+1:], "/")
- return refName
- }
- }
- return ""
-}
-
-func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
- path := ctx.Params("*")
- switch pathType {
- case RepoRefLegacy, RepoRefAny:
- if refName := getRefName(ctx, repo, RepoRefBranch); len(refName) > 0 {
- return refName
- }
- if refName := getRefName(ctx, repo, RepoRefTag); len(refName) > 0 {
- return refName
- }
- // For legacy and API support only full commit sha
- parts := strings.Split(path, "/")
-
- if len(parts) > 0 && len(parts[0]) == git.ObjectFormatFromName(repo.Repository.ObjectFormatName).FullLength() {
- repo.TreePath = strings.Join(parts[1:], "/")
- return parts[0]
- }
- if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
- return refName
- }
- repo.TreePath = path
- return repo.Repository.DefaultBranch
- case RepoRefBranch:
- ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist)
- if len(ref) == 0 {
-
- // check if ref is HEAD
- parts := strings.Split(path, "/")
- if parts[0] == headRefName {
- repo.TreePath = strings.Join(parts[1:], "/")
- return repo.Repository.DefaultBranch
- }
-
- // maybe it's a renamed branch
- return getRefNameFromPath(ctx, repo, path, func(s string) bool {
- b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s)
- if err != nil {
- log.Error("FindRenamedBranch: %v", err)
- return false
- }
-
- if !exist {
- return false
- }
-
- ctx.Data["IsRenamedBranch"] = true
- ctx.Data["RenamedBranchName"] = b.To
-
- return true
- })
- }
-
- return ref
- case RepoRefTag:
- return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
- case RepoRefCommit:
- parts := strings.Split(path, "/")
-
- if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= repo.GetObjectFormat().FullLength() {
- repo.TreePath = strings.Join(parts[1:], "/")
- return parts[0]
- }
-
- if len(parts) > 0 && parts[0] == headRefName {
- // HEAD ref points to last default branch commit
- commit, err := repo.GitRepo.GetBranchCommit(repo.Repository.DefaultBranch)
- if err != nil {
- return ""
- }
- repo.TreePath = strings.Join(parts[1:], "/")
- return commit.ID.String()
- }
- case RepoRefBlob:
- _, err := repo.GitRepo.GetBlob(path)
- if err != nil {
- return ""
- }
- return path
- default:
- log.Error("Unrecognized path type: %v", path)
- }
- return ""
-}
-
-// RepoRefByType handles repository reference name for a specific type
-// of repository reference
-func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context) context.CancelFunc {
- return func(ctx *Context) (cancel context.CancelFunc) {
- // Empty repository does not have reference information.
- if ctx.Repo.Repository.IsEmpty {
- // assume the user is viewing the (non-existent) default branch
- ctx.Repo.IsViewBranch = true
- ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
- ctx.Data["TreePath"] = ""
- return nil
- }
-
- var (
- refName string
- err error
- )
-
- if ctx.Repo.GitRepo == nil {
- ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
- if err != nil {
- ctx.ServerError(fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
- return nil
- }
- // We opened it, we should close it
- cancel = func() {
- // If it's been set to nil then assume someone else has closed it.
- if ctx.Repo.GitRepo != nil {
- ctx.Repo.GitRepo.Close()
- }
- }
- }
-
- // Get default branch.
- if len(ctx.Params("*")) == 0 {
- refName = ctx.Repo.Repository.DefaultBranch
- if !ctx.Repo.GitRepo.IsBranchExist(refName) {
- brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
- if err == nil && len(brs) != 0 {
- refName = brs[0].Name
- } else if len(brs) == 0 {
- log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
- ctx.Repo.Repository.MarkAsBrokenEmpty()
- } else {
- log.Error("GetBranches error: %v", err)
- ctx.Repo.Repository.MarkAsBrokenEmpty()
- }
- }
- ctx.Repo.RefName = refName
- ctx.Repo.BranchName = refName
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
- if err == nil {
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") {
- // if the repository is broken, we can continue to the handler code, to show "Settings -> Delete Repository" for end users
- log.Error("GetBranchCommit: %v", err)
- ctx.Repo.Repository.MarkAsBrokenEmpty()
- } else {
- ctx.ServerError("GetBranchCommit", err)
- return cancel
- }
- ctx.Repo.IsViewBranch = true
- } else {
- refName = getRefName(ctx.Base, ctx.Repo, refType)
- ctx.Repo.RefName = refName
- isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
- if isRenamedBranch && has {
- renamedBranchName := ctx.Data["RenamedBranchName"].(string)
- ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
- link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1)
- ctx.Redirect(link)
- return cancel
- }
-
- if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
- ctx.Repo.IsViewBranch = true
- ctx.Repo.BranchName = refName
-
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
- if err != nil {
- ctx.ServerError("GetBranchCommit", err)
- return cancel
- }
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
-
- } else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
- ctx.Repo.IsViewTag = true
- ctx.Repo.TagName = refName
-
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
- if err != nil {
- if git.IsErrNotExist(err) {
- ctx.NotFound("GetTagCommit", err)
- return cancel
- }
- ctx.ServerError("GetTagCommit", err)
- return cancel
- }
- ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
- } else if len(refName) >= 7 && len(refName) <= ctx.Repo.GetObjectFormat().FullLength() {
- ctx.Repo.IsViewCommit = true
- ctx.Repo.CommitID = refName
-
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
- if err != nil {
- ctx.NotFound("GetCommit", err)
- return cancel
- }
- // If short commit ID add canonical link header
- if len(refName) < ctx.Repo.GetObjectFormat().FullLength() {
- ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
- util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
- }
- } else {
- if len(ignoreNotExistErr) > 0 && ignoreNotExistErr[0] {
- return cancel
- }
- ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
- return cancel
- }
-
- if refType == RepoRefLegacy {
- // redirect from old URL scheme to new URL scheme
- prefix := strings.TrimPrefix(setting.AppSubURL+strings.ToLower(strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*"))), strings.ToLower(ctx.Repo.RepoLink))
-
- ctx.Redirect(path.Join(
- ctx.Repo.RepoLink,
- util.PathEscapeSegments(prefix),
- ctx.Repo.BranchNameSubURL(),
- util.PathEscapeSegments(ctx.Repo.TreePath)))
- return cancel
- }
- }
-
- ctx.Data["BranchName"] = ctx.Repo.BranchName
- ctx.Data["RefName"] = ctx.Repo.RefName
- ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
- ctx.Data["TagName"] = ctx.Repo.TagName
- ctx.Data["CommitID"] = ctx.Repo.CommitID
- ctx.Data["TreePath"] = ctx.Repo.TreePath
- ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
- ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
- ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
- ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch()
-
- ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
- if err != nil {
- ctx.ServerError("GetCommitsCount", err)
- return cancel
- }
- ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
- ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
-
- return cancel
- }
-}
-
-// GitHookService checks if repository Git hooks service has been enabled.
-func GitHookService() func(ctx *Context) {
- return func(ctx *Context) {
- if !ctx.Doer.CanEditGitHook() {
- ctx.NotFound("GitHookService", nil)
- return
- }
- }
-}
-
-// UnitTypes returns a middleware to set unit types to context variables.
-func UnitTypes() func(ctx *Context) {
- return func(ctx *Context) {
- ctx.Data["UnitTypeCode"] = unit_model.TypeCode
- ctx.Data["UnitTypeIssues"] = unit_model.TypeIssues
- ctx.Data["UnitTypePullRequests"] = unit_model.TypePullRequests
- ctx.Data["UnitTypeReleases"] = unit_model.TypeReleases
- ctx.Data["UnitTypeWiki"] = unit_model.TypeWiki
- ctx.Data["UnitTypeExternalWiki"] = unit_model.TypeExternalWiki
- ctx.Data["UnitTypeExternalTracker"] = unit_model.TypeExternalTracker
- ctx.Data["UnitTypeProjects"] = unit_model.TypeProjects
- ctx.Data["UnitTypePackages"] = unit_model.TypePackages
- ctx.Data["UnitTypeActions"] = unit_model.TypeActions
- }
-}
+++ /dev/null
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "net/http"
-
- web_types "code.gitea.io/gitea/modules/web/types"
-)
-
-// ResponseWriter represents a response writer for HTTP
-type ResponseWriter interface {
- http.ResponseWriter
- http.Flusher
- web_types.ResponseStatusProvider
-
- Before(func(ResponseWriter))
-
- Status() int // used by access logger template
- Size() int // used by access logger template
-}
-
-var _ ResponseWriter = &Response{}
-
-// Response represents a response
-type Response struct {
- http.ResponseWriter
- written int
- status int
- befores []func(ResponseWriter)
- beforeExecuted bool
-}
-
-// Write writes bytes to HTTP endpoint
-func (r *Response) Write(bs []byte) (int, error) {
- if !r.beforeExecuted {
- for _, before := range r.befores {
- before(r)
- }
- r.beforeExecuted = true
- }
- size, err := r.ResponseWriter.Write(bs)
- r.written += size
- if err != nil {
- return size, err
- }
- if r.status == 0 {
- r.status = http.StatusOK
- }
- return size, nil
-}
-
-func (r *Response) Status() int {
- return r.status
-}
-
-func (r *Response) Size() int {
- return r.written
-}
-
-// WriteHeader write status code
-func (r *Response) WriteHeader(statusCode int) {
- if !r.beforeExecuted {
- for _, before := range r.befores {
- before(r)
- }
- r.beforeExecuted = true
- }
- if r.status == 0 {
- r.status = statusCode
- r.ResponseWriter.WriteHeader(statusCode)
- }
-}
-
-// Flush flushes cached data
-func (r *Response) Flush() {
- if f, ok := r.ResponseWriter.(http.Flusher); ok {
- f.Flush()
- }
-}
-
-// WrittenStatus returned status code written
-func (r *Response) WrittenStatus() int {
- return r.status
-}
-
-// Before allows for a function to be called before the ResponseWriter has been written to. This is
-// useful for setting headers or any other operations that must happen before a response has been written.
-func (r *Response) Before(f func(ResponseWriter)) {
- r.befores = append(r.befores, f)
-}
-
-func WrapResponseWriter(resp http.ResponseWriter) *Response {
- if v, ok := resp.(*Response); ok {
- return v
- }
- return &Response{
- ResponseWriter: resp,
- status: 0,
- befores: make([]func(ResponseWriter), 0),
- }
-}
+++ /dev/null
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package context
-
-import (
- "strings"
- "time"
-)
-
-// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
-func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) {
- before, err = parseFormTime(ctx, "before")
- if err != nil {
- return 0, 0, err
- }
-
- since, err = parseFormTime(ctx, "since")
- if err != nil {
- return 0, 0, err
- }
- return before, since, nil
-}
-
-// parseTime parse time and return unix timestamp
-func parseFormTime(ctx *Base, name string) (int64, error) {
- value := strings.TrimSpace(ctx.FormString(name))
- if len(value) != 0 {
- t, err := time.Parse(time.RFC3339, value)
- if err != nil {
- return 0, err
- }
- if !t.IsZero() {
- return t.Unix(), nil
- }
- }
- return 0, nil
-}
+++ /dev/null
-// Copyright 2012 Google Inc. All Rights Reserved.
-// Copyright 2014 The Macaron Authors
-// Copyright 2020 The Gitea Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// SPDX-License-Identifier: Apache-2.0
-
-package context
-
-import (
- "bytes"
- "crypto/hmac"
- "crypto/sha1"
- "crypto/subtle"
- "encoding/base64"
- "fmt"
- "strconv"
- "strings"
- "time"
-)
-
-// CsrfTokenTimeout represents the duration that XSRF tokens are valid.
-// It is exported so clients may set cookie timeouts that match generated tokens.
-const CsrfTokenTimeout = 24 * time.Hour
-
-// CsrfTokenRegenerationInterval is the interval between token generations, old tokens are still valid before CsrfTokenTimeout
-var CsrfTokenRegenerationInterval = 10 * time.Minute
-
-var csrfTokenSep = []byte(":")
-
-// GenerateCsrfToken returns a URL-safe secure XSRF token that expires in CsrfTokenTimeout hours.
-// key is a secret key for your application.
-// userID is a unique identifier for the user.
-// actionID is the action the user is taking (e.g. POSTing to a particular path).
-func GenerateCsrfToken(key, userID, actionID string, now time.Time) string {
- nowUnixNano := now.UnixNano()
- nowUnixNanoStr := strconv.FormatInt(nowUnixNano, 10)
- h := hmac.New(sha1.New, []byte(key))
- h.Write([]byte(strings.ReplaceAll(userID, ":", "_")))
- h.Write(csrfTokenSep)
- h.Write([]byte(strings.ReplaceAll(actionID, ":", "_")))
- h.Write(csrfTokenSep)
- h.Write([]byte(nowUnixNanoStr))
- tok := fmt.Sprintf("%s:%s", h.Sum(nil), nowUnixNanoStr)
- return base64.RawURLEncoding.EncodeToString([]byte(tok))
-}
-
-func ParseCsrfToken(token string) (issueTime time.Time, ok bool) {
- data, err := base64.RawURLEncoding.DecodeString(token)
- if err != nil {
- return time.Time{}, false
- }
-
- pos := bytes.LastIndex(data, csrfTokenSep)
- if pos == -1 {
- return time.Time{}, false
- }
- nanos, err := strconv.ParseInt(string(data[pos+1:]), 10, 64)
- if err != nil {
- return time.Time{}, false
- }
- return time.Unix(0, nanos), true
-}
-
-// ValidCsrfToken returns true if token is a valid and unexpired token returned by Generate.
-func ValidCsrfToken(token, key, userID, actionID string, now time.Time) bool {
- issueTime, ok := ParseCsrfToken(token)
- if !ok {
- return false
- }
-
- // Check that the token is not expired.
- if now.Sub(issueTime) >= CsrfTokenTimeout {
- return false
- }
-
- // Check that the token is not from the future.
- // Allow 1-minute grace period in case the token is being verified on a
- // machine whose clock is behind the machine that issued the token.
- if issueTime.After(now.Add(1 * time.Minute)) {
- return false
- }
-
- expected := GenerateCsrfToken(key, userID, actionID, issueTime)
-
- // Check that the token matches the expected value.
- // Use constant time comparison to avoid timing attacks.
- return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1
-}
+++ /dev/null
-// Copyright 2012 Google Inc. All Rights Reserved.
-// Copyright 2014 The Macaron Authors
-// Copyright 2020 The Gitea Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// SPDX-License-Identifier: Apache-2.0
-
-package context
-
-import (
- "encoding/base64"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-const (
- key = "quay"
- userID = "12345678"
- actionID = "POST /form"
-)
-
-var (
- now = time.Now()
- oneMinuteFromNow = now.Add(1 * time.Minute)
-)
-
-func Test_ValidToken(t *testing.T) {
- t.Run("Validate token", func(t *testing.T) {
- tok := GenerateCsrfToken(key, userID, actionID, now)
- assert.True(t, ValidCsrfToken(tok, key, userID, actionID, oneMinuteFromNow))
- assert.True(t, ValidCsrfToken(tok, key, userID, actionID, now.Add(CsrfTokenTimeout-1*time.Nanosecond)))
- assert.True(t, ValidCsrfToken(tok, key, userID, actionID, now.Add(-1*time.Minute)))
- })
-}
-
-// Test_SeparatorReplacement tests that separators are being correctly substituted
-func Test_SeparatorReplacement(t *testing.T) {
- t.Run("Test two separator replacements", func(t *testing.T) {
- assert.NotEqual(t, GenerateCsrfToken("foo:bar", "baz", "wah", now),
- GenerateCsrfToken("foo", "bar:baz", "wah", now))
- })
-}
-
-func Test_InvalidToken(t *testing.T) {
- t.Run("Test invalid tokens", func(t *testing.T) {
- invalidTokenTests := []struct {
- name, key, userID, actionID string
- t time.Time
- }{
- {"Bad key", "foobar", userID, actionID, oneMinuteFromNow},
- {"Bad userID", key, "foobar", actionID, oneMinuteFromNow},
- {"Bad actionID", key, userID, "foobar", oneMinuteFromNow},
- {"Expired", key, userID, actionID, now.Add(CsrfTokenTimeout)},
- {"More than 1 minute from the future", key, userID, actionID, now.Add(-1*time.Nanosecond - 1*time.Minute)},
- }
-
- tok := GenerateCsrfToken(key, userID, actionID, now)
- for _, itt := range invalidTokenTests {
- assert.False(t, ValidCsrfToken(tok, itt.key, itt.userID, itt.actionID, itt.t))
- }
- })
-}
-
-// Test_ValidateBadData primarily tests that no unexpected panics are triggered during parsing
-func Test_ValidateBadData(t *testing.T) {
- t.Run("Validate bad data", func(t *testing.T) {
- badDataTests := []struct {
- name, tok string
- }{
- {"Invalid Base64", "ASDab24(@)$*=="},
- {"No delimiter", base64.URLEncoding.EncodeToString([]byte("foobar12345678"))},
- {"Invalid time", base64.URLEncoding.EncodeToString([]byte("foobar:foobar"))},
- }
-
- for _, bdt := range badDataTests {
- assert.False(t, ValidCsrfToken(bdt.tok, key, userID, actionID, oneMinuteFromNow))
- }
- })
-}
+++ /dev/null
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-// Package contexttest provides utilities for testing Web/API contexts with models.
-package contexttest
-
-import (
- gocontext "context"
- "io"
- "net/http"
- "net/http/httptest"
- "net/url"
- "strings"
- "testing"
-
- access_model "code.gitea.io/gitea/models/perm/access"
- repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/gitrepo"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/translation"
- "code.gitea.io/gitea/modules/web/middleware"
-
- "github.com/go-chi/chi/v5"
- "github.com/stretchr/testify/assert"
-)
-
-func mockRequest(t *testing.T, reqPath string) *http.Request {
- method, path, found := strings.Cut(reqPath, " ")
- if !found {
- method = "GET"
- path = reqPath
- }
- requestURL, err := url.Parse(path)
- assert.NoError(t, err)
- req := &http.Request{Method: method, URL: requestURL, Form: url.Values{}}
- req = req.WithContext(middleware.WithContextData(req.Context()))
- return req
-}
-
-type MockContextOption struct {
- Render context.Render
-}
-
-// MockContext mock context for unit tests
-func MockContext(t *testing.T, reqPath string, opts ...MockContextOption) (*context.Context, *httptest.ResponseRecorder) {
- var opt MockContextOption
- if len(opts) > 0 {
- opt = opts[0]
- }
- if opt.Render == nil {
- opt.Render = &MockRender{}
- }
- resp := httptest.NewRecorder()
- req := mockRequest(t, reqPath)
- base, baseCleanUp := context.NewBaseContext(resp, req)
- _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
- base.Data = middleware.GetContextData(req.Context())
- base.Locale = &translation.MockLocale{}
-
- ctx := context.NewWebContext(base, opt.Render, nil)
-
- chiCtx := chi.NewRouteContext()
- ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
- return ctx, resp
-}
-
-// MockAPIContext mock context for unit tests
-func MockAPIContext(t *testing.T, reqPath string) (*context.APIContext, *httptest.ResponseRecorder) {
- resp := httptest.NewRecorder()
- req := mockRequest(t, reqPath)
- base, baseCleanUp := context.NewBaseContext(resp, req)
- base.Data = middleware.GetContextData(req.Context())
- base.Locale = &translation.MockLocale{}
- ctx := &context.APIContext{Base: base}
- _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
-
- chiCtx := chi.NewRouteContext()
- ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
- return ctx, resp
-}
-
-// LoadRepo load a repo into a test context.
-func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
- var doer *user_model.User
- repo := &context.Repository{}
- switch ctx := ctx.(type) {
- case *context.Context:
- ctx.Repo = repo
- doer = ctx.Doer
- case *context.APIContext:
- ctx.Repo = repo
- doer = ctx.Doer
- default:
- assert.FailNow(t, "context is not *context.Context or *context.APIContext")
- }
-
- repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
- var err error
- repo.Owner, err = user_model.GetUserByID(ctx, repo.Repository.OwnerID)
- assert.NoError(t, err)
- repo.RepoLink = repo.Repository.Link()
- repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo.Repository, doer)
- assert.NoError(t, err)
-}
-
-// LoadRepoCommit loads a repo's commit into a test context.
-func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
- var repo *context.Repository
- switch ctx := ctx.(type) {
- case *context.Context:
- repo = ctx.Repo
- case *context.APIContext:
- repo = ctx.Repo
- default:
- assert.FailNow(t, "context is not *context.Context or *context.APIContext")
- }
-
- gitRepo, err := gitrepo.OpenRepository(ctx, repo.Repository)
- assert.NoError(t, err)
- defer gitRepo.Close()
- branch, err := gitRepo.GetHEADBranch()
- assert.NoError(t, err)
- assert.NotNil(t, branch)
- if branch != nil {
- repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
- assert.NoError(t, err)
- }
-}
-
-// LoadUser load a user into a test context
-func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
- switch ctx := ctx.(type) {
- case *context.Context:
- ctx.Doer = doer
- case *context.APIContext:
- ctx.Doer = doer
- default:
- assert.FailNow(t, "context is not *context.Context or *context.APIContext")
- }
-}
-
-// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
-// already been populated.
-func LoadGitRepo(t *testing.T, ctx *context.Context) {
- assert.NoError(t, ctx.Repo.Repository.LoadOwner(ctx))
- var err error
- ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
- assert.NoError(t, err)
-}
-
-type MockRender struct{}
-
-func (tr *MockRender) TemplateLookup(tmpl string, _ gocontext.Context) (templates.TemplateExecutor, error) {
- return nil, nil
-}
-
-func (tr *MockRender) HTML(w io.Writer, status int, _ string, _ any, _ gocontext.Context) error {
- if resp, ok := w.(http.ResponseWriter); ok {
- resp.WriteHeader(status)
- }
- return nil
-}
+++ /dev/null
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package upload
-
-import (
- "mime"
- "net/http"
- "net/url"
- "path"
- "regexp"
- "strings"
-
- "code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
-)
-
-// ErrFileTypeForbidden not allowed file type error
-type ErrFileTypeForbidden struct {
- Type string
-}
-
-// IsErrFileTypeForbidden checks if an error is a ErrFileTypeForbidden.
-func IsErrFileTypeForbidden(err error) bool {
- _, ok := err.(ErrFileTypeForbidden)
- return ok
-}
-
-func (err ErrFileTypeForbidden) Error() string {
- return "This file extension or type is not allowed to be uploaded."
-}
-
-var wildcardTypeRe = regexp.MustCompile(`^[a-z]+/\*$`)
-
-// Verify validates whether a file is allowed to be uploaded.
-func Verify(buf []byte, fileName, allowedTypesStr string) error {
- allowedTypesStr = strings.ReplaceAll(allowedTypesStr, "|", ",") // compat for old config format
-
- allowedTypes := []string{}
- for _, entry := range strings.Split(allowedTypesStr, ",") {
- entry = strings.ToLower(strings.TrimSpace(entry))
- if entry != "" {
- allowedTypes = append(allowedTypes, entry)
- }
- }
-
- if len(allowedTypes) == 0 {
- return nil // everything is allowed
- }
-
- fullMimeType := http.DetectContentType(buf)
- mimeType, _, err := mime.ParseMediaType(fullMimeType)
- if err != nil {
- log.Warn("Detected attachment type could not be parsed %s", fullMimeType)
- return ErrFileTypeForbidden{Type: fullMimeType}
- }
- extension := strings.ToLower(path.Ext(fileName))
-
- // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers
- for _, allowEntry := range allowedTypes {
- if allowEntry == "*/*" {
- return nil // everything allowed
- } else if strings.HasPrefix(allowEntry, ".") && allowEntry == extension {
- return nil // extension is allowed
- } else if mimeType == allowEntry {
- return nil // mime type is allowed
- } else if wildcardTypeRe.MatchString(allowEntry) && strings.HasPrefix(mimeType, allowEntry[:len(allowEntry)-1]) {
- return nil // wildcard match, e.g. image/*
- }
- }
-
- log.Info("Attachment with type %s blocked from upload", fullMimeType)
- return ErrFileTypeForbidden{Type: fullMimeType}
-}
-
-// AddUploadContext renders template values for dropzone
-func AddUploadContext(ctx *context.Context, uploadType string) {
- if uploadType == "release" {
- ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
- ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/releases/attachments/remove"
- ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
- ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Release.AllowedTypes, "|", ",")
- ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
- ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
- } else if uploadType == "comment" {
- ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
- ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
- if len(ctx.Params(":index")) > 0 {
- ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/" + url.PathEscape(ctx.Params(":index")) + "/attachments"
- } else {
- ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
- }
- ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Attachment.AllowedTypes, "|", ",")
- ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
- ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
- } else if uploadType == "repo" {
- ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/upload-file"
- ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/upload-remove"
- ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/upload-file"
- ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Upload.AllowedTypes, "|", ",")
- ctx.Data["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
- ctx.Data["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
- }
-}
+++ /dev/null
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// SPDX-License-Identifier: MIT
-
-package upload
-
-import (
- "bytes"
- "compress/gzip"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestUpload(t *testing.T) {
- testContent := []byte(`This is a plain text file.`)
- var b bytes.Buffer
- w := gzip.NewWriter(&b)
- w.Write(testContent)
- w.Close()
-
- kases := []struct {
- data []byte
- fileName string
- allowedTypes string
- err error
- }{
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "",
- err: nil,
- },
- {
- data: testContent,
- fileName: "dir/test.txt",
- allowedTypes: "",
- err: nil,
- },
- {
- data: testContent,
- fileName: "../../../test.txt",
- allowedTypes: "",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: ",",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "|",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "*/*",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "*/*,",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "*/*|",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "text/plain",
- err: nil,
- },
- {
- data: testContent,
- fileName: "dir/test.txt",
- allowedTypes: "text/plain",
- err: nil,
- },
- {
- data: testContent,
- fileName: "/dir.txt/test.js",
- allowedTypes: ".js",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: " text/plain ",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: ".txt",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: " .txt,.js",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: " .txt|.js",
- err: nil,
- },
- {
- data: testContent,
- fileName: "../../test.txt",
- allowedTypes: " .txt|.js",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: " .txt ,.js ",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "text/plain, .txt",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "text/*",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "text/*,.js",
- err: nil,
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "text/**",
- err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: "application/x-gzip",
- err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: ".zip",
- err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: ".zip,.txtx",
- err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
- },
- {
- data: testContent,
- fileName: "test.txt",
- allowedTypes: ".zip|.txtx",
- err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
- },
- {
- data: b.Bytes(),
- fileName: "test.txt",
- allowedTypes: "application/x-gzip",
- err: nil,
- },
- }
-
- for _, kase := range kases {
- assert.Equal(t, kase.err, Verify(kase.data, kase.fileName, kase.allowedTypes))
- }
-}
"code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
web_types "code.gitea.io/gitea/modules/web/types"
actions_service "code.gitea.io/gitea/services/actions"
+ "code.gitea.io/gitea/services/context"
)
const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
packages_module "code.gitea.io/gitea/modules/packages"
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
alpine_service "code.gitea.io/gitea/services/packages/alpine"
)
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/perm"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/packages/swift"
"code.gitea.io/gitea/routers/api/packages/vagrant"
"code.gitea.io/gitea/services/auth"
- context_service "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context"
)
func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
})
})
}, reqPackageAccess(perm.AccessModeRead))
- }, context_service.UserAssignmentWeb(), context.PackageAssignment())
+ }, context.UserAssignmentWeb(), context.PackageAssignment())
return r
}
ctx.Status(http.StatusNotFound)
})
- }, container.ReqContainerAccess, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
+ }, container.ReqContainerAccess, context.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
return r
}
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
cargo_module "code.gitea.io/gitea/modules/packages/cargo"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
packages_service "code.gitea.io/gitea/services/packages"
cargo_service "code.gitea.io/gitea/services/packages/cargo"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
chef_module "code.gitea.io/gitea/modules/packages/chef"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
composer_module "code.gitea.io/gitea/modules/packages/composer"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
packages_service "code.gitea.io/gitea/services/packages"
packages_model "code.gitea.io/gitea/models/packages"
conan_model "code.gitea.io/gitea/models/packages/conan"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
conan_module "code.gitea.io/gitea/modules/packages/conan"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
)
conan_model "code.gitea.io/gitea/models/packages/conan"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
conan_module "code.gitea.io/gitea/modules/packages/conan"
+ "code.gitea.io/gitea/services/context"
)
// SearchResult contains the found recipe names
packages_model "code.gitea.io/gitea/models/packages"
conda_model "code.gitea.io/gitea/models/packages/conda"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
conda_module "code.gitea.io/gitea/modules/packages/conda"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
"github.com/dsnet/compress/bzip2"
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
container_service "code.gitea.io/gitea/services/packages/container"
packages_model "code.gitea.io/gitea/models/packages"
cran_model "code.gitea.io/gitea/models/packages/cran"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
cran_module "code.gitea.io/gitea/modules/packages/cran"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
debian_module "code.gitea.io/gitea/modules/packages/debian"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
debian_service "code.gitea.io/gitea/services/packages/debian"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"time"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
goproxy_module "code.gitea.io/gitea/modules/packages/goproxy"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"time"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
"gopkg.in/yaml.v3"
"net/url"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
// LogAndProcessError logs an error and calls a custom callback with the processed error message.
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
maven_module "code.gitea.io/gitea/modules/packages/maven"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
npm_module "code.gitea.io/gitea/modules/packages/npm"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
"github.com/hashicorp/go-version"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
nuget_model "code.gitea.io/gitea/models/packages/nuget"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
nuget_module "code.gitea.io/gitea/modules/packages/nuget"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"time"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
pypi_module "code.gitea.io/gitea/modules/packages/pypi"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
packages_module "code.gitea.io/gitea/modules/packages"
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
notify_service "code.gitea.io/gitea/services/notify"
packages_service "code.gitea.io/gitea/services/packages"
rpm_service "code.gitea.io/gitea/services/packages/rpm"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
rubygems_module "code.gitea.io/gitea/modules/packages/rubygems"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
)
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
"github.com/hashicorp/go-version"
"strings"
packages_model "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
vagrant_module "code.gitea.io/gitea/modules/packages/vagrant"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers/api/packages/helper"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
"github.com/hashicorp/go-version"
"strings"
"code.gitea.io/gitea/modules/activitypub"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
ap "github.com/go-ap/activitypub"
"github.com/go-ap/jsonld"
"net/url"
"code.gitea.io/gitea/modules/activitypub"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/setting"
+ gitea_context "code.gitea.io/gitea/services/context"
ap "github.com/go-ap/activitypub"
"github.com/go-fed/httpsig"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/cron"
)
"net/http"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"net/http"
"code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
webhook_service "code.gitea.io/gitea/services/webhook"
)
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
package admin
import (
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/repo"
+ "code.gitea.io/gitea/services/context"
)
// CreateRepo api for creating a repository
package admin
import (
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/shared"
+ "code.gitea.io/gitea/services/context"
)
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/mailer"
user_service "code.gitea.io/gitea/services/user"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/auth"
- context_service "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
m.Group("/user/{username}", func() {
m.Get("", activitypub.Person)
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
- }, context_service.UserAssignmentAPI())
+ }, context.UserAssignmentAPI())
m.Group("/user-id/{user-id}", func() {
m.Get("", activitypub.Person)
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
- }, context_service.UserIDAssignmentAPI())
+ }, context.UserIDAssignmentAPI())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub))
}
}, reqSelfOrAdmin(), reqBasicOrRevProxyAuth())
m.Get("/activities/feeds", user.ListUserActivityFeeds)
- }, context_service.UserAssignmentAPI(), individualPermsChecker)
+ }, context.UserAssignmentAPI(), individualPermsChecker)
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser))
// Users (requires user scope)
m.Get("/starred", user.GetStarredRepos)
m.Get("/subscriptions", user.GetWatchedRepos)
- }, context_service.UserAssignmentAPI())
+ }, context.UserAssignmentAPI())
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
// Users (requires user scope)
m.Get("", user.CheckMyFollowing)
m.Put("", user.Follow)
m.Delete("", user.Unfollow)
- }, context_service.UserAssignmentAPI())
+ }, context.UserAssignmentAPI())
})
// (admin:public_key scope)
m.Get("/files", reqToken(), packages.ListPackageFiles)
})
m.Get("/", reqToken(), packages.ListPackages)
- }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead))
+ }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead))
// Organizations
m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs)
m.Group("/users/{username}/orgs", func() {
m.Get("", reqToken(), org.ListUserOrgs)
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
- }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context_service.UserAssignmentAPI())
+ }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI())
m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create)
m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization))
m.Group("/orgs/{org}", func() {
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
m.Post("/repos", bind(api.CreateRepoOption{}), admin.CreateRepo)
m.Post("/rename", bind(api.RenameUserOption{}), admin.RenameUser)
- }, context_service.UserAssignmentAPI())
+ }, context.UserAssignmentAPI())
})
m.Group("/emails", func() {
m.Get("", admin.GetAllEmails)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/options"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
// Shows a list of all Gitignore templates
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"net/http"
"net/url"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/options"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
// Returns a list of all License templates
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
+ "code.gitea.io/gitea/services/context"
)
// Markup render markup document to HTML
"strings"
"testing"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
)
const cacheKeyNodeInfoUsage = "API_NodeInfoUsage"
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/context"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
)
// SigningKey returns the public key of the default signing key if it exists
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
)
// Version shows the version of the Gitea server
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
)
// NewAvailable check if unread notifications exist
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"encoding/base64"
"net/http"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
user_service "code.gitea.io/gitea/services/user"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
webhook_service "code.gitea.io/gitea/services/webhook"
)
"strings"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/org"
user_service "code.gitea.io/gitea/services/user"
package org
import (
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/shared"
+ "code.gitea.io/gitea/services/context"
)
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
secret_service "code.gitea.io/gitea/services/secrets"
)
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
org_service "code.gitea.io/gitea/services/org"
repo_service "code.gitea.io/gitea/services/repository"
"net/http"
"code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
packages_service "code.gitea.io/gitea/services/packages"
)
"errors"
"net/http"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
secret_service "code.gitea.io/gitea/services/secrets"
)
"encoding/base64"
"net/http"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
files_service "code.gitea.io/gitea/services/repository/files"
)
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/organization"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
repo_module "code.gitea.io/gitea/modules/repository"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
repo_service "code.gitea.io/gitea/services/repository"
)
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httpcache"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
+ "code.gitea.io/gitea/services/context"
archiver_service "code.gitea.io/gitea/services/repository/archiver"
files_service "code.gitea.io/gitea/services/repository/files"
)
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
repo_service "code.gitea.io/gitea/services/repository"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"net/http"
"net/url"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
)
// GetGitAllRefs get ref or an list all the refs of a repository
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
"code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
webhook_service "code.gitea.io/gitea/services/webhook"
)
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/contexttest"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
notify_service "code.gitea.io/gitea/services/notify"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/attachment"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
)
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/attachment"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
)
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
)
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"strconv"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"strconv"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
)
type languageResponse []*repo_model.LanguageStat
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/migrations"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/migrations"
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/repository/files"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/routers/api/v1/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/automerge"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/gitdiff"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/gitrepo"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
"code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
release_service "code.gitea.io/gitea/services/release"
)
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/attachment"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
releaseservice "code.gitea.io/gitea/services/release"
)
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/issue"
repo_service "code.gitea.io/gitea/services/repository"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
package repo
import (
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/shared"
+ "code.gitea.io/gitea/services/context"
)
// GetRegistrationToken returns the token to register repo runners
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
files_service "code.gitea.io/gitea/services/repository/files"
)
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
releaseservice "code.gitea.io/gitea/services/release"
)
"net/http"
"code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
org_service "code.gitea.io/gitea/services/org"
repo_service "code.gitea.io/gitea/services/repository"
"strings"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
repo_service "code.gitea.io/gitea/services/repository"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
files_service "code.gitea.io/gitea/services/repository/files"
)
"net/url"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
notify_service "code.gitea.io/gitea/services/notify"
wiki_service "code.gitea.io/gitea/services/wiki"
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
)
// GetGeneralUISettings returns instance's global settings for ui
"net/http"
actions_model "code.gitea.io/gitea/models/actions"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
// RegistrationToken is response related to registeration token
"errors"
"net/http"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
secret_service "code.gitea.io/gitea/services/secrets"
)
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"encoding/base64"
"net/http"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
user_service "code.gitea.io/gitea/services/user"
)
"net/http"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
user_service "code.gitea.io/gitea/services/user"
)
"net/http"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"net/http"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
)
// GetUserByParamsName get user by name
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
webhook_service "code.gitea.io/gitea/services/webhook"
)
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/repo"
"code.gitea.io/gitea/routers/api/v1/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
package user
import (
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/shared"
+ "code.gitea.io/gitea/services/context"
)
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
user_service "code.gitea.io/gitea/services/user"
)
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
activities_model "code.gitea.io/gitea/models/activities"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
)
// ResolveRefOrSha resolve ref to sha if exist
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
webhook_module "code.gitea.io/gitea/modules/webhook"
+ "code.gitea.io/gitea/services/context"
webhook_service "code.gitea.io/gitea/services/webhook"
)
import (
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
import (
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"
auth_service "code.gitea.io/gitea/services/auth"
+ "code.gitea.io/gitea/services/context"
)
type AuthResult struct {
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/modules/web/routing"
+ "code.gitea.io/gitea/services/context"
)
const tplStatus500 base.TplName = "status/500"
"net/http"
"strings"
- "code.gitea.io/gitea/modules/context"
"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"
+ "code.gitea.io/gitea/services/context"
"mvdan.cc/xurls/v2"
)
"strings"
"code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/modules/web/routing"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/session"
"github.com/chi-middleware/proxy"
"io"
"time"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
)
// ServeBlob download a git.Blob
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/generate"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/routers/common"
auth_service "code.gitea.io/gitea/services/auth"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"gitea.com/go-chi/session"
actions_model "code.gitea.io/gitea/models/actions"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
// GenerateActionsRunnerToken generates a new runner token for a given scope
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/private"
+ gitea_context "code.gitea.io/gitea/services/context"
)
// SetDefaultBranch updates the default branch
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ gitea_context "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
)
access_model "code.gitea.io/gitea/models/perm/access"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/web"
+ gitea_context "code.gitea.io/gitea/services/context"
pull_service "code.gitea.io/gitea/services/pull"
)
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/agit"
+ gitea_context "code.gitea.io/gitea/services/context"
)
// HookProcReceive proc-receive hook - only handles agit Proc-Receive requests at present
"net/http"
"strings"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
chi_middleware "github.com/go-chi/chi/v5/middleware"
"net/http"
repo_model "code.gitea.io/gitea/models/repo"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
+ gitea_context "code.gitea.io/gitea/services/context"
)
// This file contains common functions relating to setting the Repository for the internal routes
"net/http"
asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/services/context"
)
// UpdatePublicKeyInRepo update public key and deploy key updates
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/mailer"
)
"net/http"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/graceful/releasereopen"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
)
// ReloadTemplates reloads all the templates
"runtime"
"time"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
process_module "code.gitea.io/gitea/modules/process"
+ "code.gitea.io/gitea/services/context"
)
// Processes prints out the processes
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/services/context"
)
// Restart causes the server to perform a graceful restart
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/private"
+ "code.gitea.io/gitea/services/context"
)
// Restart is not implemented for Windows based servers as they can't fork
"io"
"net/http"
- myCtx "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/private"
+ myCtx "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/migrations"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
wiki_service "code.gitea.io/gitea/services/wiki"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
)
// SSHLog hook to response ssh log
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"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/updatechecker"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/cron"
"code.gitea.io/gitea/services/forms"
release_service "code.gitea.io/gitea/services/release"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
+ "code.gitea.io/gitea/services/context"
)
var (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/auth/pam"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
pam_service "code.gitea.io/gitea/services/auth/source/pam"
"code.gitea.io/gitea/services/auth/source/smtp"
"code.gitea.io/gitea/services/auth/source/sspi"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"xorm.io/xorm/convert"
system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/mailer"
"gitea.com/go-chi/session"
"runtime/pprof"
"time"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/services/context"
)
func MonitorDiagnosis(ctx *context.Context) {
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
const (
"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"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/web/explore"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
packages_service "code.gitea.io/gitea/services/packages"
packages_cleanup_service "code.gitea.io/gitea/services/packages/cleanup"
)
"net/http"
"strconv"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
func Queues(ctx *context.Context) {
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/explore"
+ "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
)
package admin
import (
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
func RedirectToDefaultSetting(ctx *context.Context) {
"net/http"
"runtime"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
// Stacktrace show admin monitor goroutines page
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/explore"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/mailer"
user_service "code.gitea.io/gitea/services/user"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/forms"
"github.com/stretchr/testify/assert"
"code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/externalaccount"
"code.gitea.io/gitea/services/forms"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/routers/utils"
auth_service "code.gitea.io/gitea/services/auth"
"code.gitea.io/gitea/services/auth/source/oauth2"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/externalaccount"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/mailer"
"code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
auth_service "code.gitea.io/gitea/services/auth"
"code.gitea.io/gitea/services/auth/source/oauth2"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/externalaccount"
"code.gitea.io/gitea/services/forms"
auth_module "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
auth_service "code.gitea.io/gitea/services/auth"
source_service "code.gitea.io/gitea/services/auth/source"
"code.gitea.io/gitea/services/auth/source/oauth2"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/externalaccount"
"code.gitea.io/gitea/services/forms"
user_service "code.gitea.io/gitea/services/user"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/openid"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/auth"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/routers/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/mailer"
user_service "code.gitea.io/gitea/services/user"
user_model "code.gitea.io/gitea/models/user"
wa "code.gitea.io/gitea/modules/auth/webauthn"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/externalaccount"
"github.com/go-webauthn/webauthn/protocol"
"time"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/services/context"
)
// List all devtest templates, they will be used for e2e tests for the UI components
"net/http"
"time"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/routers/web/auth"
+ "code.gitea.io/gitea/services/context"
)
// Events listens for events
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
const (
import (
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
)
// Organizations render explore organizations page
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sitemap"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sitemap"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
const (
"time"
"code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"github.com/gorilla/feeds"
)
activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
"github.com/gorilla/feeds"
)
"time"
"code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
"github.com/gorilla/feeds"
)
"time"
activities_model "code.gitea.io/gitea/models/activities"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
+ "code.gitea.io/gitea/services/context"
"github.com/gorilla/feeds"
)
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"github.com/gorilla/feeds"
)
package feed
import (
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
)
// RenderBranchFeed render format for branch or file
activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"github.com/gorilla/feeds"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/repo"
- context_service "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context"
)
func requireSignIn(ctx *context.Context) {
m.Methods("GET,OPTIONS", "/objects/{head:[0-9a-f]{2}}/{hash:[0-9a-f]{38,62}}", repo.GetLooseObject)
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.pack", repo.GetPackFile)
m.Methods("GET,OPTIONS", "/objects/pack/pack-{file:[0-9a-f]{40,64}}.idx", repo.GetIdxFile)
- }, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context_service.UserAssignmentWeb())
+ }, ignSignInAndCsrf, requireSignIn, repo.HTTPGitEnabledHandler, repo.CorsHandler(), context.UserAssignmentWeb())
}
"strings"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
func goGet(ctx *context.Context) {
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sitemap"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/routers/web/auth"
"code.gitea.io/gitea/routers/web/user"
+ "code.gitea.io/gitea/services/context"
)
const (
package misc
import (
- "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
+ "code.gitea.io/gitea/services/context"
)
// Markup render markup document to HTML
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
)
// tplSwagger swagger page template
"fmt"
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
type nodeInfoLinks struct {
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"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/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/organization"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
attachment_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/routers/web/org"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/web"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
org_service "code.gitea.io/gitea/services/org"
repo_service "code.gitea.io/gitea/services/repository"
package setting
import (
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
)
func RedirectToDefaultSetting(ctx *context.Context) {
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
+ "code.gitea.io/gitea/services/context"
)
const (
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
shared "code.gitea.io/gitea/routers/web/shared/packages"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
org_service "code.gitea.io/gitea/services/org"
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
type passkeyEndpointsType struct {
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/repo"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"github.com/nektos/act/pkg/model"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/base"
- context_module "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
actions_service "code.gitea.io/gitea/services/actions"
+ context_module "code.gitea.io/gitea/services/context"
"xorm.io/builder"
)
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
)
const (
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/attachment"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
repo_service "code.gitea.io/gitea/services/repository"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
files_service "code.gitea.io/gitea/services/repository/files"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
release_service "code.gitea.io/gitea/services/release"
repo_service "code.gitea.io/gitea/services/repository"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/repository/files"
)
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
contributors_service "code.gitea.io/gitea/services/repository"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitgraph"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/gitdiff"
git_service "code.gitea.io/gitea/services/repository"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/context"
csv_module "code.gitea.io/gitea/modules/csv"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/gitdiff"
)
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
contributors_service "code.gitea.io/gitea/services/repository"
)
"time"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/routers/common"
+ "code.gitea.io/gitea/services/context"
)
// ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/typesniffer"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/forms"
files_service "code.gitea.io/gitea/services/repository/files"
)
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
)
const (
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
"github.com/go-chi/cors"
"sort"
"code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/services/context"
)
func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User {
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/git"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
issue_service "code.gitea.io/gitea/services/issue"
"code.gitea.io/gitea/models/avatars"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/services/context"
"github.com/sergi/go-diff/diffmatchpatch"
)
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
// AddDependency adds new dependencies
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/label"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
issue_service "code.gitea.io/gitea/services/issue"
)
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/forms"
"github.com/stretchr/testify/assert"
import (
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
"net/http"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
)
// IssuePinOrUnpin pin or unpin a Issue
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/eventsource"
+ "code.gitea.io/gitea/services/context"
)
// IssueStopwatch creates or stops a stopwatch for the given issue.
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
)
const (
system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/optional"
+ "code.gitea.io/gitea/services/context"
user_service "code.gitea.io/gitea/services/user"
)
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/migrations"
"code.gitea.io/gitea/services/task"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/issue"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
const (
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/repository/files"
)
attachment_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"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"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/automerge"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/gitdiff"
notify_service "code.gitea.io/gitea/services/notify"
issues_model "code.gitea.io/gitea/models/issues"
pull_model "code.gitea.io/gitea/models/pull"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/forms"
pull_service "code.gitea.io/gitea/services/pull"
)
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/pull"
"github.com/stretchr/testify/assert"
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
contributors_service "code.gitea.io/gitea/services/repository"
)
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"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/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/feed"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context/upload"
"code.gitea.io/gitea/services/forms"
releaseservice "code.gitea.io/gitea/services/release"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/forms"
"github.com/stretchr/testify/assert"
"path"
"code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
// RenderFile renders a file by repos path
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
repo_service "code.gitea.io/gitea/services/repository"
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
const tplSearch base.TplName = "repo/search"
"fmt"
"io"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
repo_service "code.gitea.io/gitea/services/repository"
)
repo_model "code.gitea.io/gitea/models/repo"
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/mailer"
org_service "code.gitea.io/gitea/services/org"
repo_service "code.gitea.io/gitea/services/repository"
"net/http"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers/web/repo"
+ "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
)
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/services/context"
)
// GitHooks hooks of a repository
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/pipeline"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/repo"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
pull_service "code.gitea.io/gitea/services/pull"
"code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
actions_shared "code.gitea.io/gitea/routers/web/shared/actions"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
shared "code.gitea.io/gitea/routers/web/shared/secrets"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/indexer/stats"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/modules/web"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/forms"
repo_service "code.gitea.io/gitea/services/repository"
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
shared "code.gitea.io/gitea/routers/web/shared/actions"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
webhook_module "code.gitea.io/gitea/modules/webhook"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/forms"
webhook_service "code.gitea.io/gitea/services/webhook"
"strings"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
)
// TopicsPost response for creating repository
"net/http"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/services/context"
"github.com/go-enry/go-enry/v2"
)
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/typesniffer"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/feed"
+ "code.gitea.io/gitea/services/context"
issue_service "code.gitea.io/gitea/services/issue"
files_service "code.gitea.io/gitea/services/repository/files"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
notify_service "code.gitea.io/gitea/services/notify"
wiki_service "code.gitea.io/gitea/services/wiki"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/forms"
wiki_service "code.gitea.io/gitea/services/wiki"
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
secret_service "code.gitea.io/gitea/services/secrets"
)
packages_model "code.gitea.io/gitea/models/packages"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
cargo_service "code.gitea.io/gitea/services/packages/cargo"
container_service "code.gitea.io/gitea/services/packages/container"
import (
"code.gitea.io/gitea/models/db"
secret_model "code.gitea.io/gitea/models/secret"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/web/shared/actions"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
secret_service "code.gitea.io/gitea/services/secrets"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
)
// prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu)
package web
import (
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
)
// SwaggerV1Json render swagger v1 json
"code.gitea.io/gitea/models/avatars"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/httpcache"
+ "code.gitea.io/gitea/services/context"
)
func cacheableRedirect(ctx *context.Context, location string) {
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/setting"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/feed"
- context_service "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
username := ctx.Params("username")
reloadParam := func(suffix string) (success bool) {
ctx.SetParams("username", strings.TrimSuffix(username, suffix))
- context_service.UserAssignmentWeb()(ctx)
+ context.UserAssignmentWeb()(ctx)
// check view permissions
if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) {
ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name))
return
}
if reloadParam(".rss") {
- context_service.UserAssignmentWeb()(ctx)
+ context.UserAssignmentWeb()(ctx)
feed.ShowUserFeedRSS(ctx)
}
case strings.HasSuffix(username, ".atom"):
feed.ShowUserFeedAtom(ctx)
}
default:
- context_service.UserAssignmentWeb()(ctx)
+ context.UserAssignmentWeb()(ctx)
if !ctx.Written() {
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
OwnerProfile(ctx)
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
alpine_module "code.gitea.io/gitea/modules/packages/alpine"
debian_module "code.gitea.io/gitea/modules/packages/debian"
"code.gitea.io/gitea/modules/web"
packages_helper "code.gitea.io/gitea/routers/api/packages/helper"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
packages_service "code.gitea.io/gitea/services/packages"
)
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"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/markup"
"code.gitea.io/gitea/routers/web/feed"
"code.gitea.io/gitea/routers/web/org"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/auth"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/mailer"
"code.gitea.io/gitea/services/user"
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/forms"
"github.com/stretchr/testify/assert"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context"
repo_service "code.gitea.io/gitea/services/repository"
)
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
asymkey_service "code.gitea.io/gitea/services/asymkey"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
import (
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
shared_user "code.gitea.io/gitea/routers/web/shared/user"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
chef_module "code.gitea.io/gitea/modules/packages/chef"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
shared "code.gitea.io/gitea/routers/web/shared/packages"
+ "code.gitea.io/gitea/services/context"
)
const (
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
user_service "code.gitea.io/gitea/services/user"
)
package setting
import (
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
func RedirectToDefaultSetting(ctx *context.Context) {
"strings"
"code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"github.com/pquerna/otp"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/openid"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
)
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/oauth2"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/auth"
wa "code.gitea.io/gitea/modules/auth/webauthn"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"github.com/go-webauthn/webauthn/protocol"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
const (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
"strconv"
admin_model "code.gitea.io/gitea/models/admin"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/services/context"
)
// TaskStatus returns task's status
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/metrics"
"code.gitea.io/gitea/modules/public"
user_setting "code.gitea.io/gitea/routers/web/user/setting"
"code.gitea.io/gitea/routers/web/user/setting/security"
auth_service "code.gitea.io/gitea/services/auth"
- context_service "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/lfs"
m.Methods("GET, OPTIONS", "/attachments/{uuid}", optionsCorsHandler(), repo.GetAttachment)
}, ignSignIn)
- m.Post("/{username}", reqSignIn, context_service.UserAssignmentWeb(), user.Action)
+ m.Post("/{username}", reqSignIn, context.UserAssignmentWeb(), user.Action)
reqRepoAdmin := context.RequireRepoAdmin()
reqRepoCodeWriter := context.RequireRepoWriter(unit.TypeCode)
m.Group("", func() {
m.Get("/code", user.CodeSearch)
}, reqUnitAccess(unit.TypeCode, perm.AccessModeRead, false), individualPermsChecker)
- }, ignSignIn, context_service.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
+ }, ignSignIn, context.UserAssignmentWeb(), context.OrgAssignment()) // for "/{username}/-" (packages, projects, code)
m.Group("/{username}/{reponame}", func() {
m.Group("/settings", func() {
"strings"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
)
// https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-webfinger-14#section-4.4
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/storage"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/context/upload"
"github.com/google/uuid"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/webauthn"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware"
+ gitea_context "code.gitea.io/gitea/services/context"
user_service "code.gitea.io/gitea/services/user"
)
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/auth/source/sspi"
+ gitea_context "code.gitea.io/gitea/services/context"
gouuid "github.com/google/uuid"
)
--- /dev/null
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "bytes"
+ "fmt"
+ "net"
+ "net/http"
+ "strings"
+ "text/template"
+ "time"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/web/middleware"
+)
+
+type routerLoggerOptions struct {
+ req *http.Request
+ Identity *string
+ Start *time.Time
+ ResponseWriter http.ResponseWriter
+ Ctx map[string]any
+ RequestID *string
+}
+
+const keyOfRequestIDInTemplate = ".RequestID"
+
+// According to:
+// TraceId: A valid trace identifier is a 16-byte array with at least one non-zero byte
+// MD5 output is 16 or 32 bytes: md5-bytes is 16, md5-hex is 32
+// SHA1: similar, SHA1-bytes is 20, SHA1-hex is 40.
+// UUID is 128-bit, 32 hex chars, 36 ASCII chars with 4 dashes
+// So, we accept a Request ID with a maximum character length of 40
+const maxRequestIDByteLength = 40
+
+func parseRequestIDFromRequestHeader(req *http.Request) string {
+ requestID := "-"
+ for _, key := range setting.Log.RequestIDHeaders {
+ if req.Header.Get(key) != "" {
+ requestID = req.Header.Get(key)
+ break
+ }
+ }
+ if len(requestID) > maxRequestIDByteLength {
+ requestID = fmt.Sprintf("%s...", requestID[:maxRequestIDByteLength])
+ }
+ return requestID
+}
+
+// AccessLogger returns a middleware to log access logger
+func AccessLogger() func(http.Handler) http.Handler {
+ logger := log.GetLogger("access")
+ needRequestID := len(setting.Log.RequestIDHeaders) > 0 && strings.Contains(setting.Log.AccessLogTemplate, keyOfRequestIDInTemplate)
+ logTemplate, _ := template.New("log").Parse(setting.Log.AccessLogTemplate)
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ start := time.Now()
+
+ var requestID string
+ if needRequestID {
+ requestID = parseRequestIDFromRequestHeader(req)
+ }
+
+ reqHost, _, err := net.SplitHostPort(req.RemoteAddr)
+ if err != nil {
+ reqHost = req.RemoteAddr
+ }
+
+ next.ServeHTTP(w, req)
+ rw := w.(ResponseWriter)
+
+ identity := "-"
+ data := middleware.GetContextData(req.Context())
+ if signedUser, ok := data[middleware.ContextDataKeySignedUser].(*user_model.User); ok {
+ identity = signedUser.Name
+ }
+ buf := bytes.NewBuffer([]byte{})
+ err = logTemplate.Execute(buf, routerLoggerOptions{
+ req: req,
+ Identity: &identity,
+ Start: &start,
+ ResponseWriter: rw,
+ Ctx: map[string]any{
+ "RemoteAddr": req.RemoteAddr,
+ "RemoteHost": reqHost,
+ "Req": req,
+ },
+ RequestID: &requestID,
+ })
+ if err != nil {
+ log.Error("Could not execute access logger template: %v", err.Error())
+ }
+
+ logger.Info("%s", buf.String())
+ })
+ }
+}
--- /dev/null
+// Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ mc "code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/httpcache"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/web"
+ web_types "code.gitea.io/gitea/modules/web/types"
+
+ "gitea.com/go-chi/cache"
+)
+
+// APIContext is a specific context for API service
+type APIContext struct {
+ *Base
+
+ Cache cache.Cache
+
+ Doer *user_model.User // current signed-in user
+ IsSigned bool
+ IsBasicAuth bool
+
+ ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
+
+ Repo *Repository
+ Org *APIOrganization
+ Package *Package
+}
+
+func init() {
+ web.RegisterResponseStatusProvider[*APIContext](func(req *http.Request) web_types.ResponseStatusProvider {
+ return req.Context().Value(apiContextKey).(*APIContext)
+ })
+}
+
+// Currently, we have the following common fields in error response:
+// * message: the message for end users (it shouldn't be used for error type detection)
+// if we need to indicate some errors, we should introduce some new fields like ErrorCode or ErrorType
+// * url: the swagger document URL
+
+// APIError is error format response
+// swagger:response error
+type APIError struct {
+ Message string `json:"message"`
+ URL string `json:"url"`
+}
+
+// APIValidationError is error format response related to input validation
+// swagger:response validationError
+type APIValidationError struct {
+ Message string `json:"message"`
+ URL string `json:"url"`
+}
+
+// APIInvalidTopicsError is error format response to invalid topics
+// swagger:response invalidTopicsError
+type APIInvalidTopicsError struct {
+ Message string `json:"message"`
+ InvalidTopics []string `json:"invalidTopics"`
+}
+
+// APIEmpty is an empty response
+// swagger:response empty
+type APIEmpty struct{}
+
+// APIForbiddenError is a forbidden error response
+// swagger:response forbidden
+type APIForbiddenError struct {
+ APIError
+}
+
+// APINotFound is a not found empty response
+// swagger:response notFound
+type APINotFound struct{}
+
+// APIConflict is a conflict empty response
+// swagger:response conflict
+type APIConflict struct{}
+
+// APIRedirect is a redirect response
+// swagger:response redirect
+type APIRedirect struct{}
+
+// APIString is a string response
+// swagger:response string
+type APIString string
+
+// APIRepoArchivedError is an error that is raised when an archived repo should be modified
+// swagger:response repoArchivedError
+type APIRepoArchivedError struct {
+ APIError
+}
+
+// ServerError responds with error message, status is 500
+func (ctx *APIContext) ServerError(title string, err error) {
+ ctx.Error(http.StatusInternalServerError, title, err)
+}
+
+// Error responds with an error message to client with given obj as the message.
+// If status is 500, also it prints error to log.
+func (ctx *APIContext) Error(status int, title string, obj any) {
+ var message string
+ if err, ok := obj.(error); ok {
+ message = err.Error()
+ } else {
+ message = fmt.Sprintf("%s", obj)
+ }
+
+ if status == http.StatusInternalServerError {
+ log.ErrorWithSkip(1, "%s: %s", title, message)
+
+ if setting.IsProd && !(ctx.Doer != nil && ctx.Doer.IsAdmin) {
+ message = ""
+ }
+ }
+
+ ctx.JSON(status, APIError{
+ Message: message,
+ URL: setting.API.SwaggerURL,
+ })
+}
+
+// InternalServerError responds with an error message to the client with the error as a message
+// and the file and line of the caller.
+func (ctx *APIContext) InternalServerError(err error) {
+ log.ErrorWithSkip(1, "InternalServerError: %v", err)
+
+ var message string
+ if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
+ message = err.Error()
+ }
+
+ ctx.JSON(http.StatusInternalServerError, APIError{
+ Message: message,
+ URL: setting.API.SwaggerURL,
+ })
+}
+
+type apiContextKeyType struct{}
+
+var apiContextKey = apiContextKeyType{}
+
+// GetAPIContext returns a context for API routes
+func GetAPIContext(req *http.Request) *APIContext {
+ return req.Context().Value(apiContextKey).(*APIContext)
+}
+
+func genAPILinks(curURL *url.URL, total, pageSize, curPage int) []string {
+ page := NewPagination(total, pageSize, curPage, 0)
+ paginater := page.Paginater
+ links := make([]string, 0, 4)
+
+ if paginater.HasNext() {
+ u := *curURL
+ queries := u.Query()
+ queries.Set("page", fmt.Sprintf("%d", paginater.Next()))
+ u.RawQuery = queries.Encode()
+
+ links = append(links, fmt.Sprintf("<%s%s>; rel=\"next\"", setting.AppURL, u.RequestURI()[1:]))
+ }
+ if !paginater.IsLast() {
+ u := *curURL
+ queries := u.Query()
+ queries.Set("page", fmt.Sprintf("%d", paginater.TotalPages()))
+ u.RawQuery = queries.Encode()
+
+ links = append(links, fmt.Sprintf("<%s%s>; rel=\"last\"", setting.AppURL, u.RequestURI()[1:]))
+ }
+ if !paginater.IsFirst() {
+ u := *curURL
+ queries := u.Query()
+ queries.Set("page", "1")
+ u.RawQuery = queries.Encode()
+
+ links = append(links, fmt.Sprintf("<%s%s>; rel=\"first\"", setting.AppURL, u.RequestURI()[1:]))
+ }
+ if paginater.HasPrevious() {
+ u := *curURL
+ queries := u.Query()
+ queries.Set("page", fmt.Sprintf("%d", paginater.Previous()))
+ u.RawQuery = queries.Encode()
+
+ links = append(links, fmt.Sprintf("<%s%s>; rel=\"prev\"", setting.AppURL, u.RequestURI()[1:]))
+ }
+ return links
+}
+
+// SetLinkHeader sets pagination link header by given total number and page size.
+func (ctx *APIContext) SetLinkHeader(total, pageSize int) {
+ links := genAPILinks(ctx.Req.URL, total, pageSize, ctx.FormInt("page"))
+
+ if len(links) > 0 {
+ ctx.RespHeader().Set("Link", strings.Join(links, ","))
+ ctx.AppendAccessControlExposeHeaders("Link")
+ }
+}
+
+// APIContexter returns apicontext as middleware
+func APIContexter() func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ base, baseCleanUp := NewBaseContext(w, req)
+ ctx := &APIContext{
+ Base: base,
+ Cache: mc.GetCache(),
+ Repo: &Repository{PullRequest: &PullRequest{}},
+ Org: &APIOrganization{},
+ }
+ defer baseCleanUp()
+
+ ctx.Base.AppendContextValue(apiContextKey, ctx)
+ ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
+
+ // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
+ if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
+ if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
+ ctx.InternalServerError(err)
+ return
+ }
+ }
+
+ httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
+ ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
+
+ next.ServeHTTP(ctx.Resp, ctx.Req)
+ })
+ }
+}
+
+// NotFound handles 404s for APIContext
+// String will replace message, errors will be added to a slice
+func (ctx *APIContext) NotFound(objs ...any) {
+ message := ctx.Locale.TrString("error.not_found")
+ var errors []string
+ for _, obj := range objs {
+ // Ignore nil
+ if obj == nil {
+ continue
+ }
+
+ if err, ok := obj.(error); ok {
+ errors = append(errors, err.Error())
+ } else {
+ message = obj.(string)
+ }
+ }
+
+ ctx.JSON(http.StatusNotFound, map[string]any{
+ "message": message,
+ "url": setting.API.SwaggerURL,
+ "errors": errors,
+ })
+}
+
+// ReferencesGitRepo injects the GitRepo into the Context
+// you can optional skip the IsEmpty check
+func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context.CancelFunc) {
+ return func(ctx *APIContext) (cancel context.CancelFunc) {
+ // Empty repository does not have reference information.
+ if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) {
+ return nil
+ }
+
+ // For API calls.
+ if ctx.Repo.GitRepo == nil {
+ gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
+ return cancel
+ }
+ ctx.Repo.GitRepo = gitRepo
+ // We opened it, we should close it
+ return func() {
+ // If it's been set to nil then assume someone else has closed it.
+ if ctx.Repo.GitRepo != nil {
+ _ = ctx.Repo.GitRepo.Close()
+ }
+ }
+ }
+
+ return cancel
+ }
+}
+
+// RepoRefForAPI handles repository reference names when the ref name is not explicitly given
+func RepoRefForAPI(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ ctx := GetAPIContext(req)
+
+ if ctx.Repo.GitRepo == nil {
+ ctx.InternalServerError(fmt.Errorf("no open git repo"))
+ return
+ }
+
+ if ref := ctx.FormTrim("ref"); len(ref) > 0 {
+ commit, err := ctx.Repo.GitRepo.GetCommit(ref)
+ if err != nil {
+ if git.IsErrNotExist(err) {
+ ctx.NotFound()
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetCommit", err)
+ }
+ return
+ }
+ ctx.Repo.Commit = commit
+ ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
+ ctx.Repo.TreePath = ctx.Params("*")
+ next.ServeHTTP(w, req)
+ return
+ }
+
+ refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
+ var err error
+
+ if ctx.Repo.GitRepo.IsBranchExist(refName) {
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
+ if err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+ ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
+ } else if ctx.Repo.GitRepo.IsTagExist(refName) {
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
+ if err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+ ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
+ } else if len(refName) == ctx.Repo.GetObjectFormat().FullLength() {
+ ctx.Repo.CommitID = refName
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
+ if err != nil {
+ ctx.NotFound("GetCommit", err)
+ return
+ }
+ } else {
+ ctx.NotFound(fmt.Errorf("not exist: '%s'", ctx.Params("*")))
+ return
+ }
+
+ next.ServeHTTP(w, req)
+ })
+}
+
+// HasAPIError returns true if error occurs in form validation.
+func (ctx *APIContext) HasAPIError() bool {
+ hasErr, ok := ctx.Data["HasError"]
+ if !ok {
+ return false
+ }
+ return hasErr.(bool)
+}
+
+// GetErrMsg returns error message in form validation.
+func (ctx *APIContext) GetErrMsg() string {
+ msg, _ := ctx.Data["ErrorMsg"].(string)
+ if msg == "" {
+ msg = "invalid form data"
+ }
+ return msg
+}
+
+// NotFoundOrServerError use error check function to determine if the error
+// is about not found. It responds with 404 status code for not found error,
+// or error context description for logging purpose of 500 server error.
+func (ctx *APIContext) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
+ if errCheck(logErr) {
+ ctx.JSON(http.StatusNotFound, nil)
+ return
+ }
+ ctx.Error(http.StatusInternalServerError, "NotFoundOrServerError", logMsg)
+}
+
+// IsUserSiteAdmin returns true if current user is a site admin
+func (ctx *APIContext) IsUserSiteAdmin() bool {
+ return ctx.IsSigned && ctx.Doer.IsAdmin
+}
+
+// IsUserRepoAdmin returns true if current user is admin in current repo
+func (ctx *APIContext) IsUserRepoAdmin() bool {
+ return ctx.Repo.IsAdmin()
+}
+
+// IsUserRepoWriter returns true if current user has write privilege in current repo
+func (ctx *APIContext) IsUserRepoWriter(unitTypes []unit.Type) bool {
+ for _, unitType := range unitTypes {
+ if ctx.Repo.CanWrite(unitType) {
+ return true
+ }
+ }
+
+ return false
+}
--- /dev/null
+// Copyright 2016 The Gogs Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import "code.gitea.io/gitea/models/organization"
+
+// APIOrganization contains organization and team
+type APIOrganization struct {
+ Organization *organization.Organization
+ Team *organization.Team
+}
--- /dev/null
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "net/url"
+ "strconv"
+ "testing"
+
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGenAPILinks(t *testing.T) {
+ setting.AppURL = "http://localhost:3000/"
+ kases := map[string][]string{
+ "api/v1/repos/jerrykan/example-repo/issues?state=all": {
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=2&state=all>; rel="next"`,
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=5&state=all>; rel="last"`,
+ },
+ "api/v1/repos/jerrykan/example-repo/issues?state=all&page=1": {
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=2&state=all>; rel="next"`,
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=5&state=all>; rel="last"`,
+ },
+ "api/v1/repos/jerrykan/example-repo/issues?state=all&page=2": {
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=3&state=all>; rel="next"`,
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=5&state=all>; rel="last"`,
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=1&state=all>; rel="first"`,
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=1&state=all>; rel="prev"`,
+ },
+ "api/v1/repos/jerrykan/example-repo/issues?state=all&page=5": {
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=1&state=all>; rel="first"`,
+ `<http://localhost:3000/api/v1/repos/jerrykan/example-repo/issues?page=4&state=all>; rel="prev"`,
+ },
+ }
+
+ for req, response := range kases {
+ u, err := url.Parse(setting.AppURL + req)
+ assert.NoError(t, err)
+
+ p := u.Query().Get("page")
+ curPage, _ := strconv.Atoi(p)
+
+ links := genAPILinks(u, 100, 20, curPage)
+
+ assert.EqualValues(t, links, response)
+ }
+}
--- /dev/null
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "context"
+ "fmt"
+ "html/template"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/web/middleware"
+
+ "github.com/go-chi/chi/v5"
+)
+
+type contextValuePair struct {
+ key any
+ valueFn func() any
+}
+
+type Base struct {
+ originCtx context.Context
+ contextValues []contextValuePair
+
+ Resp ResponseWriter
+ Req *http.Request
+
+ // Data is prepared by ContextDataStore middleware, this field only refers to the pre-created/prepared ContextData.
+ // Although it's mainly used for MVC templates, sometimes it's also used to pass data between middlewares/handler
+ Data middleware.ContextData
+
+ // Locale is mainly for Web context, although the API context also uses it in some cases: message response, form validation
+ Locale translation.Locale
+}
+
+func (b *Base) Deadline() (deadline time.Time, ok bool) {
+ return b.originCtx.Deadline()
+}
+
+func (b *Base) Done() <-chan struct{} {
+ return b.originCtx.Done()
+}
+
+func (b *Base) Err() error {
+ return b.originCtx.Err()
+}
+
+func (b *Base) Value(key any) any {
+ for _, pair := range b.contextValues {
+ if pair.key == key {
+ return pair.valueFn()
+ }
+ }
+ return b.originCtx.Value(key)
+}
+
+func (b *Base) AppendContextValueFunc(key any, valueFn func() any) any {
+ b.contextValues = append(b.contextValues, contextValuePair{key, valueFn})
+ return b
+}
+
+func (b *Base) AppendContextValue(key, value any) any {
+ b.contextValues = append(b.contextValues, contextValuePair{key, func() any { return value }})
+ return b
+}
+
+func (b *Base) GetData() middleware.ContextData {
+ return b.Data
+}
+
+// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
+func (b *Base) AppendAccessControlExposeHeaders(names ...string) {
+ val := b.RespHeader().Get("Access-Control-Expose-Headers")
+ if len(val) != 0 {
+ b.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
+ } else {
+ b.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
+ }
+}
+
+// SetTotalCountHeader set "X-Total-Count" header
+func (b *Base) SetTotalCountHeader(total int64) {
+ b.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
+ b.AppendAccessControlExposeHeaders("X-Total-Count")
+}
+
+// Written returns true if there are something sent to web browser
+func (b *Base) Written() bool {
+ return b.Resp.WrittenStatus() != 0
+}
+
+func (b *Base) WrittenStatus() int {
+ return b.Resp.WrittenStatus()
+}
+
+// Status writes status code
+func (b *Base) Status(status int) {
+ b.Resp.WriteHeader(status)
+}
+
+// Write writes data to web browser
+func (b *Base) Write(bs []byte) (int, error) {
+ return b.Resp.Write(bs)
+}
+
+// RespHeader returns the response header
+func (b *Base) RespHeader() http.Header {
+ return b.Resp.Header()
+}
+
+// Error returned an error to web browser
+func (b *Base) Error(status int, contents ...string) {
+ v := http.StatusText(status)
+ if len(contents) > 0 {
+ v = contents[0]
+ }
+ http.Error(b.Resp, v, status)
+}
+
+// JSON render content as JSON
+func (b *Base) JSON(status int, content any) {
+ b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
+ b.Resp.WriteHeader(status)
+ if err := json.NewEncoder(b.Resp).Encode(content); err != nil {
+ log.Error("Render JSON failed: %v", err)
+ }
+}
+
+// RemoteAddr returns the client machine ip address
+func (b *Base) RemoteAddr() string {
+ return b.Req.RemoteAddr
+}
+
+// Params returns the param on route
+func (b *Base) Params(p string) string {
+ s, _ := url.PathUnescape(chi.URLParam(b.Req, strings.TrimPrefix(p, ":")))
+ return s
+}
+
+func (b *Base) PathParamRaw(p string) string {
+ return chi.URLParam(b.Req, strings.TrimPrefix(p, ":"))
+}
+
+// ParamsInt64 returns the param on route as int64
+func (b *Base) ParamsInt64(p string) int64 {
+ v, _ := strconv.ParseInt(b.Params(p), 10, 64)
+ return v
+}
+
+// SetParams set params into routes
+func (b *Base) SetParams(k, v string) {
+ chiCtx := chi.RouteContext(b)
+ chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
+}
+
+// FormString returns the first value matching the provided key in the form as a string
+func (b *Base) FormString(key string) string {
+ return b.Req.FormValue(key)
+}
+
+// FormStrings returns a string slice for the provided key from the form
+func (b *Base) FormStrings(key string) []string {
+ if b.Req.Form == nil {
+ if err := b.Req.ParseMultipartForm(32 << 20); err != nil {
+ return nil
+ }
+ }
+ if v, ok := b.Req.Form[key]; ok {
+ return v
+ }
+ return nil
+}
+
+// FormTrim returns the first value for the provided key in the form as a space trimmed string
+func (b *Base) FormTrim(key string) string {
+ return strings.TrimSpace(b.Req.FormValue(key))
+}
+
+// FormInt returns the first value for the provided key in the form as an int
+func (b *Base) FormInt(key string) int {
+ v, _ := strconv.Atoi(b.Req.FormValue(key))
+ return v
+}
+
+// FormInt64 returns the first value for the provided key in the form as an int64
+func (b *Base) FormInt64(key string) int64 {
+ v, _ := strconv.ParseInt(b.Req.FormValue(key), 10, 64)
+ return v
+}
+
+// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
+func (b *Base) FormBool(key string) bool {
+ s := b.Req.FormValue(key)
+ v, _ := strconv.ParseBool(s)
+ v = v || strings.EqualFold(s, "on")
+ return v
+}
+
+// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
+// for the provided key exists in the form else it returns OptionalBoolNone
+func (b *Base) FormOptionalBool(key string) util.OptionalBool {
+ value := b.Req.FormValue(key)
+ if len(value) == 0 {
+ return util.OptionalBoolNone
+ }
+ s := b.Req.FormValue(key)
+ v, _ := strconv.ParseBool(s)
+ v = v || strings.EqualFold(s, "on")
+ return util.OptionalBoolOf(v)
+}
+
+func (b *Base) SetFormString(key, value string) {
+ _ = b.Req.FormValue(key) // force parse form
+ b.Req.Form.Set(key, value)
+}
+
+// PlainTextBytes renders bytes as plain text
+func (b *Base) plainTextInternal(skip, status int, bs []byte) {
+ statusPrefix := status / 100
+ if statusPrefix == 4 || statusPrefix == 5 {
+ log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
+ }
+ b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
+ b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
+ b.Resp.WriteHeader(status)
+ if _, err := b.Resp.Write(bs); err != nil {
+ log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
+ }
+}
+
+// PlainTextBytes renders bytes as plain text
+func (b *Base) PlainTextBytes(status int, bs []byte) {
+ b.plainTextInternal(2, status, bs)
+}
+
+// PlainText renders content as plain text
+func (b *Base) PlainText(status int, text string) {
+ b.plainTextInternal(2, status, []byte(text))
+}
+
+// Redirect redirects the request
+func (b *Base) Redirect(location string, status ...int) {
+ code := http.StatusSeeOther
+ if len(status) == 1 {
+ code = status[0]
+ }
+
+ if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
+ // Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
+ // 1. the first request to "/my-path" contains cookie
+ // 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
+ // 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
+ // 4. then the browser accepts the empty session, then the user is logged out
+ // So in this case, we should remove the session cookie from the response header
+ removeSessionCookieHeader(b.Resp)
+ }
+ // in case the request is made by htmx, have it redirect the browser instead of trying to follow the redirect inside htmx
+ if b.Req.Header.Get("HX-Request") == "true" {
+ b.Resp.Header().Set("HX-Redirect", location)
+ // we have to return a non-redirect status code so XMLHTTPRequest will not immediately follow the redirect
+ // so as to give htmx redirect logic a chance to run
+ b.Status(http.StatusNoContent)
+ return
+ }
+ http.Redirect(b.Resp, b.Req, location, code)
+}
+
+type ServeHeaderOptions httplib.ServeHeaderOptions
+
+func (b *Base) SetServeHeaders(opt *ServeHeaderOptions) {
+ httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opt))
+}
+
+// ServeContent serves content to http request
+func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
+ httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opts))
+ http.ServeContent(b.Resp, b.Req, opts.Filename, opts.LastModified, r)
+}
+
+// Close frees all resources hold by Context
+func (b *Base) cleanUp() {
+ if b.Req != nil && b.Req.MultipartForm != nil {
+ _ = b.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
+ }
+}
+
+func (b *Base) Tr(msg string, args ...any) template.HTML {
+ return b.Locale.Tr(msg, args...)
+}
+
+func (b *Base) TrN(cnt any, key1, keyN string, args ...any) template.HTML {
+ return b.Locale.TrN(cnt, key1, keyN, args...)
+}
+
+func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, closeFunc func()) {
+ b = &Base{
+ originCtx: req.Context(),
+ Req: req,
+ Resp: WrapResponseWriter(resp),
+ Locale: middleware.Locale(resp, req),
+ Data: middleware.GetContextData(req.Context()),
+ }
+ b.AppendContextValue(translation.ContextKey, b.Locale)
+ b.Req = b.Req.WithContext(b)
+ return b, b.cleanUp
+}
--- /dev/null
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "fmt"
+ "sync"
+
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/hcaptcha"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/mcaptcha"
+ "code.gitea.io/gitea/modules/recaptcha"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/turnstile"
+
+ "gitea.com/go-chi/captcha"
+)
+
+var (
+ imageCaptchaOnce sync.Once
+ cpt *captcha.Captcha
+)
+
+// GetImageCaptcha returns global image captcha
+func GetImageCaptcha() *captcha.Captcha {
+ imageCaptchaOnce.Do(func() {
+ cpt = captcha.NewCaptcha(captcha.Options{
+ SubURL: setting.AppSubURL,
+ })
+ cpt.Store = cache.GetCache()
+ })
+ return cpt
+}
+
+// SetCaptchaData sets common captcha data
+func SetCaptchaData(ctx *Context) {
+ if !setting.Service.EnableCaptcha {
+ return
+ }
+ ctx.Data["EnableCaptcha"] = setting.Service.EnableCaptcha
+ ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
+ ctx.Data["Captcha"] = GetImageCaptcha()
+ ctx.Data["CaptchaType"] = setting.Service.CaptchaType
+ ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
+ ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
+ ctx.Data["CfTurnstileSitekey"] = setting.Service.CfTurnstileSitekey
+}
+
+const (
+ gRecaptchaResponseField = "g-recaptcha-response"
+ hCaptchaResponseField = "h-captcha-response"
+ mCaptchaResponseField = "m-captcha-response"
+ cfTurnstileResponseField = "cf-turnstile-response"
+)
+
+// VerifyCaptcha verifies Captcha data
+// No-op if captchas are not enabled
+func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) {
+ if !setting.Service.EnableCaptcha {
+ return
+ }
+
+ var valid bool
+ var err error
+ switch setting.Service.CaptchaType {
+ case setting.ImageCaptcha:
+ valid = GetImageCaptcha().VerifyReq(ctx.Req)
+ case setting.ReCaptcha:
+ valid, err = recaptcha.Verify(ctx, ctx.Req.Form.Get(gRecaptchaResponseField))
+ case setting.HCaptcha:
+ valid, err = hcaptcha.Verify(ctx, ctx.Req.Form.Get(hCaptchaResponseField))
+ case setting.MCaptcha:
+ valid, err = mcaptcha.Verify(ctx, ctx.Req.Form.Get(mCaptchaResponseField))
+ case setting.CfTurnstile:
+ valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField))
+ default:
+ ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
+ return
+ }
+ if err != nil {
+ log.Debug("%v", err)
+ }
+
+ if !valid {
+ ctx.Data["Err_Captcha"] = true
+ ctx.RenderWithErr(ctx.Tr("form.captcha_incorrect"), tpl, form)
+ }
+}
--- /dev/null
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "context"
+ "encoding/hex"
+ "fmt"
+ "html/template"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ mc "code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/httpcache"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/modules/web/middleware"
+ web_types "code.gitea.io/gitea/modules/web/types"
+
+ "gitea.com/go-chi/cache"
+ "gitea.com/go-chi/session"
+)
+
+// Render represents a template render
+type Render interface {
+ TemplateLookup(tmpl string, templateCtx context.Context) (templates.TemplateExecutor, error)
+ HTML(w io.Writer, status int, name string, data any, templateCtx context.Context) error
+}
+
+// Context represents context of a request.
+type Context struct {
+ *Base
+
+ TemplateContext TemplateContext
+
+ Render Render
+ PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
+
+ Cache cache.Cache
+ Csrf CSRFProtector
+ Flash *middleware.Flash
+ Session session.Store
+
+ Link string // current request URL (without query string)
+
+ Doer *user_model.User // current signed-in user
+ IsSigned bool
+ IsBasicAuth bool
+
+ ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
+
+ Repo *Repository
+ Org *Organization
+ Package *Package
+}
+
+type TemplateContext map[string]any
+
+func init() {
+ web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider {
+ return req.Context().Value(WebContextKey).(*Context)
+ })
+}
+
+type webContextKeyType struct{}
+
+var WebContextKey = webContextKeyType{}
+
+func GetWebContext(req *http.Request) *Context {
+ ctx, _ := req.Context().Value(WebContextKey).(*Context)
+ return ctx
+}
+
+// ValidateContext is a special context for form validation middleware. It may be different from other contexts.
+type ValidateContext struct {
+ *Base
+}
+
+// GetValidateContext gets a context for middleware form validation
+func GetValidateContext(req *http.Request) (ctx *ValidateContext) {
+ if ctxAPI, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
+ ctx = &ValidateContext{Base: ctxAPI.Base}
+ } else if ctxWeb, ok := req.Context().Value(WebContextKey).(*Context); ok {
+ ctx = &ValidateContext{Base: ctxWeb.Base}
+ } else {
+ panic("invalid context, expect either APIContext or Context")
+ }
+ return ctx
+}
+
+func NewTemplateContextForWeb(ctx *Context) TemplateContext {
+ tmplCtx := NewTemplateContext(ctx)
+ tmplCtx["Locale"] = ctx.Base.Locale
+ tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
+ return tmplCtx
+}
+
+func NewWebContext(base *Base, render Render, session session.Store) *Context {
+ ctx := &Context{
+ Base: base,
+ Render: render,
+ Session: session,
+
+ Cache: mc.GetCache(),
+ Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"),
+ Repo: &Repository{PullRequest: &PullRequest{}},
+ Org: &Organization{},
+ }
+ ctx.TemplateContext = NewTemplateContextForWeb(ctx)
+ ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}}
+ return ctx
+}
+
+// Contexter initializes a classic context for a request.
+func Contexter() func(next http.Handler) http.Handler {
+ rnd := templates.HTMLRenderer()
+ csrfOpts := CsrfOptions{
+ Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
+ Cookie: setting.CSRFCookieName,
+ SetCookie: true,
+ Secure: setting.SessionConfig.Secure,
+ CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
+ Header: "X-Csrf-Token",
+ CookieDomain: setting.SessionConfig.Domain,
+ CookiePath: setting.SessionConfig.CookiePath,
+ SameSite: setting.SessionConfig.SameSite,
+ }
+ if !setting.IsProd {
+ CsrfTokenRegenerationInterval = 5 * time.Second // in dev, re-generate the tokens more aggressively for debug purpose
+ }
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+ base, baseCleanUp := NewBaseContext(resp, req)
+ defer baseCleanUp()
+ ctx := NewWebContext(base, rnd, session.GetSession(req))
+
+ ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
+ ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
+ ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
+ ctx.Data["Link"] = ctx.Link
+
+ // PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
+ ctx.PageData = map[string]any{}
+ ctx.Data["PageData"] = ctx.PageData
+
+ ctx.Base.AppendContextValue(WebContextKey, ctx)
+ ctx.Base.AppendContextValueFunc(gitrepo.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
+
+ ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
+
+ // Get the last flash message from cookie
+ lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
+ if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
+ // store last Flash message into the template data, to render it
+ ctx.Data["Flash"] = &middleware.Flash{
+ DataStore: ctx,
+ Values: vals,
+ ErrorMsg: vals.Get("error"),
+ SuccessMsg: vals.Get("success"),
+ InfoMsg: vals.Get("info"),
+ WarningMsg: vals.Get("warning"),
+ }
+ }
+
+ // if there are new messages in the ctx.Flash, write them into cookie
+ ctx.Resp.Before(func(resp ResponseWriter) {
+ if val := ctx.Flash.Encode(); val != "" {
+ middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
+ } else if lastFlashCookie != "" {
+ middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, "", -1)
+ }
+ })
+
+ // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
+ if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
+ if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
+ ctx.ServerError("ParseMultipartForm", err)
+ return
+ }
+ }
+
+ httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
+ ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
+
+ ctx.Data["SystemConfig"] = setting.Config()
+ ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
+ ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
+
+ // FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
+ ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
+ ctx.Data["DisableStars"] = setting.Repository.DisableStars
+ ctx.Data["EnableActions"] = setting.Actions.Enabled
+
+ ctx.Data["ManifestData"] = setting.ManifestData
+
+ ctx.Data["UnitWikiGlobalDisabled"] = unit.TypeWiki.UnitGlobalDisabled()
+ ctx.Data["UnitIssuesGlobalDisabled"] = unit.TypeIssues.UnitGlobalDisabled()
+ ctx.Data["UnitPullsGlobalDisabled"] = unit.TypePullRequests.UnitGlobalDisabled()
+ ctx.Data["UnitProjectsGlobalDisabled"] = unit.TypeProjects.UnitGlobalDisabled()
+ ctx.Data["UnitActionsGlobalDisabled"] = unit.TypeActions.UnitGlobalDisabled()
+
+ ctx.Data["AllLangs"] = translation.AllLangs()
+
+ next.ServeHTTP(ctx.Resp, ctx.Req)
+ })
+ }
+}
+
+// HasError returns true if error occurs in form validation.
+// Attention: this function changes ctx.Data and ctx.Flash
+func (ctx *Context) HasError() bool {
+ hasErr, ok := ctx.Data["HasError"]
+ if !ok {
+ return false
+ }
+ ctx.Flash.ErrorMsg = ctx.GetErrMsg()
+ ctx.Data["Flash"] = ctx.Flash
+ return hasErr.(bool)
+}
+
+// GetErrMsg returns error message in form validation.
+func (ctx *Context) GetErrMsg() string {
+ msg, _ := ctx.Data["ErrorMsg"].(string)
+ if msg == "" {
+ msg = "invalid form data"
+ }
+ return msg
+}
+
+func (ctx *Context) JSONRedirect(redirect string) {
+ ctx.JSON(http.StatusOK, map[string]any{"redirect": redirect})
+}
+
+func (ctx *Context) JSONOK() {
+ ctx.JSON(http.StatusOK, map[string]any{"ok": true}) // this is only a dummy response, frontend seldom uses it
+}
+
+func (ctx *Context) JSONError(msg any) {
+ switch v := msg.(type) {
+ case string:
+ ctx.JSON(http.StatusBadRequest, map[string]any{"errorMessage": v, "renderFormat": "text"})
+ case template.HTML:
+ ctx.JSON(http.StatusBadRequest, map[string]any{"errorMessage": v, "renderFormat": "html"})
+ default:
+ panic(fmt.Sprintf("unsupported type: %T", msg))
+ }
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "net/http"
+ "strings"
+
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/web/middleware"
+)
+
+const CookieNameFlash = "gitea_flash"
+
+func removeSessionCookieHeader(w http.ResponseWriter) {
+ cookies := w.Header()["Set-Cookie"]
+ w.Header().Del("Set-Cookie")
+ for _, cookie := range cookies {
+ if strings.HasPrefix(cookie, setting.SessionConfig.CookieName+"=") {
+ continue
+ }
+ w.Header().Add("Set-Cookie", cookie)
+ }
+}
+
+// SetSiteCookie convenience function to set most cookies consistently
+// CSRF and a few others are the exception here
+func (ctx *Context) SetSiteCookie(name, value string, maxAge int) {
+ middleware.SetSiteCookie(ctx.Resp, name, value, maxAge)
+}
+
+// DeleteSiteCookie convenience function to delete most cookies consistently
+// CSRF and a few others are the exception here
+func (ctx *Context) DeleteSiteCookie(name string) {
+ middleware.SetSiteCookie(ctx.Resp, name, "", -1)
+}
+
+// GetSiteCookie returns given cookie value from request header.
+func (ctx *Context) GetSiteCookie(name string) string {
+ return middleware.GetSiteCookie(ctx.Req, name)
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "code.gitea.io/gitea/models/unit"
+)
+
+// IsUserSiteAdmin returns true if current user is a site admin
+func (ctx *Context) IsUserSiteAdmin() bool {
+ return ctx.IsSigned && ctx.Doer.IsAdmin
+}
+
+// IsUserRepoAdmin returns true if current user is admin in current repo
+func (ctx *Context) IsUserRepoAdmin() bool {
+ return ctx.Repo.IsAdmin()
+}
+
+// IsUserRepoWriter returns true if current user has write privilege in current repo
+func (ctx *Context) IsUserRepoWriter(unitTypes []unit.Type) bool {
+ for _, unitType := range unitTypes {
+ if ctx.Repo.CanWrite(unitType) {
+ return true
+ }
+ }
+
+ return false
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "io"
+ "net/http"
+ "strings"
+)
+
+// UploadStream returns the request body or the first form file
+// Only form files need to get closed.
+func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) {
+ contentType := strings.ToLower(ctx.Req.Header.Get("Content-Type"))
+ if strings.HasPrefix(contentType, "application/x-www-form-urlencoded") || strings.HasPrefix(contentType, "multipart/form-data") {
+ if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil {
+ return nil, false, err
+ }
+ if ctx.Req.MultipartForm.File == nil {
+ return nil, false, http.ErrMissingFile
+ }
+ for _, files := range ctx.Req.MultipartForm.File {
+ if len(files) > 0 {
+ r, err := files[0].Open()
+ return r, true, err
+ }
+ }
+ return nil, false, http.ErrMissingFile
+ }
+ return ctx.Req.Body, false, nil
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "net/http"
+ "net/url"
+ "path"
+ "strconv"
+ "strings"
+ "time"
+
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/httplib"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/web/middleware"
+)
+
+// RedirectToUser redirect to a differently-named user
+func RedirectToUser(ctx *Base, userName string, redirectUserID int64) {
+ user, err := user_model.GetUserByID(ctx, redirectUserID)
+ if err != nil {
+ ctx.Error(http.StatusInternalServerError, "unable to get user")
+ return
+ }
+
+ redirectPath := strings.Replace(
+ ctx.Req.URL.EscapedPath(),
+ url.PathEscape(userName),
+ url.PathEscape(user.Name),
+ 1,
+ )
+ if ctx.Req.URL.RawQuery != "" {
+ redirectPath += "?" + ctx.Req.URL.RawQuery
+ }
+ ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect)
+}
+
+// RedirectToFirst redirects to first not empty URL
+func (ctx *Context) RedirectToFirst(location ...string) {
+ for _, loc := range location {
+ if len(loc) == 0 {
+ continue
+ }
+
+ if httplib.IsRiskyRedirectURL(loc) {
+ continue
+ }
+
+ ctx.Redirect(loc)
+ return
+ }
+
+ ctx.Redirect(setting.AppSubURL + "/")
+}
+
+const tplStatus500 base.TplName = "status/500"
+
+// HTML calls Context.HTML and renders the template to HTTP response
+func (ctx *Context) HTML(status int, name base.TplName) {
+ log.Debug("Template: %s", name)
+
+ tmplStartTime := time.Now()
+ if !setting.IsProd {
+ ctx.Data["TemplateName"] = name
+ }
+ ctx.Data["TemplateLoadTimes"] = func() string {
+ return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms"
+ }
+
+ err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext)
+ if err == nil {
+ return
+ }
+
+ // if rendering fails, show error page
+ if name != tplStatus500 {
+ err = fmt.Errorf("failed to render template: %s, error: %s", name, templates.HandleTemplateRenderingError(err))
+ ctx.ServerError("Render failed", err) // show the 500 error page
+ } else {
+ ctx.PlainText(http.StatusInternalServerError, "Unable to render status/500 page, the template system is broken, or Gitea can't find your template files.")
+ return
+ }
+}
+
+// JSONTemplate renders the template as JSON response
+// keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape
+func (ctx *Context) JSONTemplate(tmpl base.TplName) {
+ t, err := ctx.Render.TemplateLookup(string(tmpl), nil)
+ if err != nil {
+ ctx.ServerError("unable to find template", err)
+ return
+ }
+ ctx.Resp.Header().Set("Content-Type", "application/json")
+ if err = t.Execute(ctx.Resp, ctx.Data); err != nil {
+ ctx.ServerError("unable to execute template", err)
+ }
+}
+
+// RenderToString renders the template content to a string
+func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) {
+ var buf strings.Builder
+ err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data, ctx.TemplateContext)
+ return buf.String(), err
+}
+
+// RenderWithErr used for page has form validation but need to prompt error to users.
+func (ctx *Context) RenderWithErr(msg any, tpl base.TplName, form any) {
+ if form != nil {
+ middleware.AssignForm(form, ctx.Data)
+ }
+ ctx.Flash.Error(msg, true)
+ ctx.HTML(http.StatusOK, tpl)
+}
+
+// NotFound displays a 404 (Not Found) page and prints the given error, if any.
+func (ctx *Context) NotFound(logMsg string, logErr error) {
+ ctx.notFoundInternal(logMsg, logErr)
+}
+
+func (ctx *Context) notFoundInternal(logMsg string, logErr error) {
+ if logErr != nil {
+ log.Log(2, log.DEBUG, "%s: %v", logMsg, logErr)
+ if !setting.IsProd {
+ ctx.Data["ErrorMsg"] = logErr
+ }
+ }
+
+ // response simple message if Accept isn't text/html
+ showHTML := false
+ for _, part := range ctx.Req.Header["Accept"] {
+ if strings.Contains(part, "text/html") {
+ showHTML = true
+ break
+ }
+ }
+
+ if !showHTML {
+ ctx.plainTextInternal(3, http.StatusNotFound, []byte("Not found.\n"))
+ return
+ }
+
+ ctx.Data["IsRepo"] = ctx.Repo.Repository != nil
+ ctx.Data["Title"] = "Page Not Found"
+ ctx.HTML(http.StatusNotFound, base.TplName("status/404"))
+}
+
+// ServerError displays a 500 (Internal Server Error) page and prints the given error, if any.
+func (ctx *Context) ServerError(logMsg string, logErr error) {
+ ctx.serverErrorInternal(logMsg, logErr)
+}
+
+func (ctx *Context) serverErrorInternal(logMsg string, logErr error) {
+ if logErr != nil {
+ log.ErrorWithSkip(2, "%s: %v", logMsg, logErr)
+ if _, ok := logErr.(*net.OpError); ok || errors.Is(logErr, &net.OpError{}) {
+ // This is an error within the underlying connection
+ // and further rendering will not work so just return
+ return
+ }
+
+ // it's safe to show internal error to admin users, and it helps
+ if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
+ ctx.Data["ErrorMsg"] = fmt.Sprintf("%s, %s", logMsg, logErr)
+ }
+ }
+
+ ctx.Data["Title"] = "Internal Server Error"
+ ctx.HTML(http.StatusInternalServerError, tplStatus500)
+}
+
+// NotFoundOrServerError use error check function to determine if the error
+// is about not found. It responds with 404 status code for not found error,
+// or error context description for logging purpose of 500 server error.
+// TODO: remove the "errCheck" and use util.ErrNotFound to check
+func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
+ if errCheck(logErr) {
+ ctx.notFoundInternal(logMsg, logErr)
+ return
+ }
+ ctx.serverErrorInternal(logMsg, logErr)
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "context"
+ "time"
+)
+
+var _ context.Context = TemplateContext(nil)
+
+func NewTemplateContext(ctx context.Context) TemplateContext {
+ return TemplateContext{"_ctx": ctx}
+}
+
+func (c TemplateContext) parentContext() context.Context {
+ return c["_ctx"].(context.Context)
+}
+
+func (c TemplateContext) Deadline() (deadline time.Time, ok bool) {
+ return c.parentContext().Deadline()
+}
+
+func (c TemplateContext) Done() <-chan struct{} {
+ return c.parentContext().Done()
+}
+
+func (c TemplateContext) Err() error {
+ return c.parentContext().Err()
+}
+
+func (c TemplateContext) Value(key any) any {
+ return c.parentContext().Value(key)
+}
--- /dev/null
+// Copyright 2023 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestRemoveSessionCookieHeader(t *testing.T) {
+ w := httptest.NewRecorder()
+ w.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "foo"}).String())
+ w.Header().Add("Set-Cookie", (&http.Cookie{Name: "other", Value: "bar"}).String())
+ assert.Len(t, w.Header().Values("Set-Cookie"), 2)
+ removeSessionCookieHeader(w)
+ assert.Len(t, w.Header().Values("Set-Cookie"), 1)
+ assert.Contains(t, "other=bar", w.Header().Get("Set-Cookie"))
+}
--- /dev/null
+// Copyright 2013 Martini Authors
+// Copyright 2014 The Macaron Authors
+// Copyright 2021 The Gitea Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+// SPDX-License-Identifier: Apache-2.0
+
+// a middleware that generates and validates CSRF tokens.
+
+package context
+
+import (
+ "encoding/base32"
+ "fmt"
+ "net/http"
+ "strconv"
+ "time"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/web/middleware"
+)
+
+// CSRFProtector represents a CSRF protector and is used to get the current token and validate the token.
+type CSRFProtector interface {
+ // GetHeaderName returns HTTP header to search for token.
+ GetHeaderName() string
+ // GetFormName returns form value to search for token.
+ GetFormName() string
+ // GetToken returns the token.
+ GetToken() string
+ // Validate validates the token in http context.
+ Validate(ctx *Context)
+ // DeleteCookie deletes the cookie
+ DeleteCookie(ctx *Context)
+}
+
+type csrfProtector struct {
+ opt CsrfOptions
+ // Token generated to pass via header, cookie, or hidden form value.
+ Token string
+ // This value must be unique per user.
+ ID string
+}
+
+// GetHeaderName returns the name of the HTTP header for csrf token.
+func (c *csrfProtector) GetHeaderName() string {
+ return c.opt.Header
+}
+
+// GetFormName returns the name of the form value for csrf token.
+func (c *csrfProtector) GetFormName() string {
+ return c.opt.Form
+}
+
+// GetToken returns the current token. This is typically used
+// to populate a hidden form in an HTML template.
+func (c *csrfProtector) GetToken() string {
+ return c.Token
+}
+
+// CsrfOptions maintains options to manage behavior of Generate.
+type CsrfOptions struct {
+ // The global secret value used to generate Tokens.
+ Secret string
+ // HTTP header used to set and get token.
+ Header string
+ // Form value used to set and get token.
+ Form string
+ // Cookie value used to set and get token.
+ Cookie string
+ // Cookie domain.
+ CookieDomain string
+ // Cookie path.
+ CookiePath string
+ CookieHTTPOnly bool
+ // SameSite set the cookie SameSite type
+ SameSite http.SameSite
+ // Key used for getting the unique ID per user.
+ SessionKey string
+ // oldSessionKey saves old value corresponding to SessionKey.
+ oldSessionKey string
+ // If true, send token via X-Csrf-Token header.
+ SetHeader bool
+ // If true, send token via _csrf cookie.
+ SetCookie bool
+ // Set the Secure flag to true on the cookie.
+ Secure bool
+ // Disallow Origin appear in request header.
+ Origin bool
+ // Cookie lifetime. Default is 0
+ CookieLifeTime int
+}
+
+func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions {
+ if opt.Secret == "" {
+ randBytes, err := util.CryptoRandomBytes(8)
+ if err != nil {
+ // this panic can be handled by the recover() in http handlers
+ panic(fmt.Errorf("failed to generate random bytes: %w", err))
+ }
+ opt.Secret = base32.StdEncoding.EncodeToString(randBytes)
+ }
+ if opt.Header == "" {
+ opt.Header = "X-Csrf-Token"
+ }
+ if opt.Form == "" {
+ opt.Form = "_csrf"
+ }
+ if opt.Cookie == "" {
+ opt.Cookie = "_csrf"
+ }
+ if opt.CookiePath == "" {
+ opt.CookiePath = "/"
+ }
+ if opt.SessionKey == "" {
+ opt.SessionKey = "uid"
+ }
+ if opt.CookieLifeTime == 0 {
+ opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
+ }
+
+ opt.oldSessionKey = "_old_" + opt.SessionKey
+ return opt
+}
+
+func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
+ return &http.Cookie{
+ Name: c.opt.Cookie,
+ Value: value,
+ Path: c.opt.CookiePath,
+ Domain: c.opt.CookieDomain,
+ MaxAge: c.opt.CookieLifeTime,
+ Secure: c.opt.Secure,
+ HttpOnly: c.opt.CookieHTTPOnly,
+ SameSite: c.opt.SameSite,
+ }
+}
+
+// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
+// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
+func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
+ opt = prepareDefaultCsrfOptions(opt)
+ x := &csrfProtector{opt: opt}
+
+ if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
+ return x
+ }
+
+ x.ID = "0"
+ uidAny := ctx.Session.Get(opt.SessionKey)
+ if uidAny != nil {
+ switch uidVal := uidAny.(type) {
+ case string:
+ x.ID = uidVal
+ case int64:
+ x.ID = strconv.FormatInt(uidVal, 10)
+ default:
+ log.Error("invalid uid type in session: %T", uidAny)
+ }
+ }
+
+ oldUID := ctx.Session.Get(opt.oldSessionKey)
+ uidChanged := oldUID == nil || oldUID.(string) != x.ID
+ cookieToken := ctx.GetSiteCookie(opt.Cookie)
+
+ needsNew := true
+ if uidChanged {
+ _ = ctx.Session.Set(opt.oldSessionKey, x.ID)
+ } else if cookieToken != "" {
+ // If cookie token presents, re-use existing unexpired token, else generate a new one.
+ if issueTime, ok := ParseCsrfToken(cookieToken); ok {
+ dur := time.Since(issueTime) // issueTime is not a monotonic-clock, the server time may change a lot to an early time.
+ if dur >= -CsrfTokenRegenerationInterval && dur <= CsrfTokenRegenerationInterval {
+ x.Token = cookieToken
+ needsNew = false
+ }
+ }
+ }
+
+ if needsNew {
+ // FIXME: actionId.
+ x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now())
+ if opt.SetCookie {
+ cookie := newCsrfCookie(x, x.Token)
+ ctx.Resp.Header().Add("Set-Cookie", cookie.String())
+ }
+ }
+
+ if opt.SetHeader {
+ ctx.Resp.Header().Add(opt.Header, x.Token)
+ }
+ return x
+}
+
+func (c *csrfProtector) validateToken(ctx *Context, token string) {
+ if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) {
+ c.DeleteCookie(ctx)
+ if middleware.IsAPIPath(ctx.Req) {
+ // currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
+ http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
+ } else {
+ ctx.Flash.Error(ctx.Tr("error.invalid_csrf"))
+ ctx.Redirect(setting.AppSubURL + "/")
+ }
+ }
+}
+
+// Validate should be used as a per route middleware. It attempts to get a token from an "X-Csrf-Token"
+// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated.
+// If this validation fails, custom Error is sent in the reply.
+// If neither a header nor form value is found, http.StatusBadRequest is sent.
+func (c *csrfProtector) Validate(ctx *Context) {
+ if token := ctx.Req.Header.Get(c.GetHeaderName()); token != "" {
+ c.validateToken(ctx, token)
+ return
+ }
+ if token := ctx.Req.FormValue(c.GetFormName()); token != "" {
+ c.validateToken(ctx, token)
+ return
+ }
+ c.validateToken(ctx, "") // no csrf token, use an empty token to respond error
+}
+
+func (c *csrfProtector) DeleteCookie(ctx *Context) {
+ if c.opt.SetCookie {
+ cookie := newCsrfCookie(c, "")
+ cookie.MaxAge = -1
+ ctx.Resp.Header().Add("Set-Cookie", cookie.String())
+ }
+}
--- /dev/null
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2020 The Gitea Authors.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "strings"
+
+ "code.gitea.io/gitea/models/organization"
+ "code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/markdown"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
+)
+
+// Organization contains organization context
+type Organization struct {
+ IsOwner bool
+ IsMember bool
+ IsTeamMember bool // Is member of team.
+ IsTeamAdmin bool // In owner team or team that has admin permission level.
+ Organization *organization.Organization
+ OrgLink string
+ CanCreateOrgRepo bool
+ PublicMemberOnly bool // Only display public members
+
+ Team *organization.Team
+ Teams []*organization.Team
+}
+
+func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool {
+ return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeWrite
+}
+
+func (org *Organization) CanReadUnit(ctx *Context, unitType unit.Type) bool {
+ return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeRead
+}
+
+func GetOrganizationByParams(ctx *Context) {
+ orgName := ctx.Params(":org")
+
+ var err error
+
+ ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName)
+ if err != nil {
+ if organization.IsErrOrgNotExist(err) {
+ redirectUserID, err := user_model.LookupUserRedirect(ctx, orgName)
+ if err == nil {
+ RedirectToUser(ctx.Base, orgName, redirectUserID)
+ } else if user_model.IsErrUserRedirectNotExist(err) {
+ ctx.NotFound("GetUserByName", err)
+ } else {
+ ctx.ServerError("LookupUserRedirect", err)
+ }
+ } else {
+ ctx.ServerError("GetUserByName", err)
+ }
+ return
+ }
+}
+
+// HandleOrgAssignment handles organization assignment
+func HandleOrgAssignment(ctx *Context, args ...bool) {
+ var (
+ requireMember bool
+ requireOwner bool
+ requireTeamMember bool
+ requireTeamAdmin bool
+ )
+ if len(args) >= 1 {
+ requireMember = args[0]
+ }
+ if len(args) >= 2 {
+ requireOwner = args[1]
+ }
+ if len(args) >= 3 {
+ requireTeamMember = args[2]
+ }
+ if len(args) >= 4 {
+ requireTeamAdmin = args[3]
+ }
+
+ var err error
+
+ if ctx.ContextUser == nil {
+ // if Organization is not defined, get it from params
+ if ctx.Org.Organization == nil {
+ GetOrganizationByParams(ctx)
+ if ctx.Written() {
+ return
+ }
+ }
+ } else if ctx.ContextUser.IsOrganization() {
+ if ctx.Org == nil {
+ ctx.Org = &Organization{}
+ }
+ ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser)
+ } else {
+ // ContextUser is an individual User
+ return
+ }
+
+ org := ctx.Org.Organization
+
+ // Handle Visibility
+ if org.Visibility != structs.VisibleTypePublic && !ctx.IsSigned {
+ // We must be signed in to see limited or private organizations
+ ctx.NotFound("OrgAssignment", err)
+ return
+ }
+
+ if org.Visibility == structs.VisibleTypePrivate {
+ requireMember = true
+ } else if ctx.IsSigned && ctx.Doer.IsRestricted {
+ requireMember = true
+ }
+
+ ctx.ContextUser = org.AsUser()
+ ctx.Data["Org"] = org
+
+ // Admin has super access.
+ if ctx.IsSigned && ctx.Doer.IsAdmin {
+ ctx.Org.IsOwner = true
+ ctx.Org.IsMember = true
+ ctx.Org.IsTeamMember = true
+ ctx.Org.IsTeamAdmin = true
+ ctx.Org.CanCreateOrgRepo = true
+ } else if ctx.IsSigned {
+ ctx.Org.IsOwner, err = org.IsOwnedBy(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("IsOwnedBy", err)
+ return
+ }
+
+ if ctx.Org.IsOwner {
+ ctx.Org.IsMember = true
+ ctx.Org.IsTeamMember = true
+ ctx.Org.IsTeamAdmin = true
+ ctx.Org.CanCreateOrgRepo = true
+ } else {
+ ctx.Org.IsMember, err = org.IsOrgMember(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("IsOrgMember", err)
+ return
+ }
+ ctx.Org.CanCreateOrgRepo, err = org.CanCreateOrgRepo(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("CanCreateOrgRepo", err)
+ return
+ }
+ }
+ } else {
+ // Fake data.
+ ctx.Data["SignedUser"] = &user_model.User{}
+ }
+ if (requireMember && !ctx.Org.IsMember) ||
+ (requireOwner && !ctx.Org.IsOwner) {
+ ctx.NotFound("OrgAssignment", err)
+ return
+ }
+ ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
+ ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
+ ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+ ctx.Data["IsPublicMember"] = func(uid int64) bool {
+ is, _ := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, uid)
+ return is
+ }
+ ctx.Data["CanCreateOrgRepo"] = ctx.Org.CanCreateOrgRepo
+
+ ctx.Org.OrgLink = org.AsUser().OrganisationLink()
+ ctx.Data["OrgLink"] = ctx.Org.OrgLink
+
+ // Member
+ ctx.Org.PublicMemberOnly = ctx.Doer == nil || !ctx.Org.IsMember && !ctx.Doer.IsAdmin
+ opts := &organization.FindOrgMembersOpts{
+ OrgID: org.ID,
+ PublicOnly: ctx.Org.PublicMemberOnly,
+ }
+ ctx.Data["NumMembers"], err = organization.CountOrgMembers(ctx, opts)
+ if err != nil {
+ ctx.ServerError("CountOrgMembers", err)
+ return
+ }
+
+ // Team.
+ if ctx.Org.IsMember {
+ shouldSeeAllTeams := false
+ if ctx.Org.IsOwner {
+ shouldSeeAllTeams = true
+ } else {
+ teams, err := org.GetUserTeams(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("GetUserTeams", err)
+ return
+ }
+ for _, team := range teams {
+ if team.IncludesAllRepositories && team.AccessMode >= perm.AccessModeAdmin {
+ shouldSeeAllTeams = true
+ break
+ }
+ }
+ }
+ if shouldSeeAllTeams {
+ ctx.Org.Teams, err = org.LoadTeams(ctx)
+ if err != nil {
+ ctx.ServerError("LoadTeams", err)
+ return
+ }
+ } else {
+ ctx.Org.Teams, err = org.GetUserTeams(ctx, ctx.Doer.ID)
+ if err != nil {
+ ctx.ServerError("GetUserTeams", err)
+ return
+ }
+ }
+ ctx.Data["NumTeams"] = len(ctx.Org.Teams)
+ }
+
+ teamName := ctx.Params(":team")
+ if len(teamName) > 0 {
+ teamExists := false
+ for _, team := range ctx.Org.Teams {
+ if team.LowerName == strings.ToLower(teamName) {
+ teamExists = true
+ ctx.Org.Team = team
+ ctx.Org.IsTeamMember = true
+ ctx.Data["Team"] = ctx.Org.Team
+ break
+ }
+ }
+
+ if !teamExists {
+ ctx.NotFound("OrgAssignment", err)
+ return
+ }
+
+ ctx.Data["IsTeamMember"] = ctx.Org.IsTeamMember
+ if requireTeamMember && !ctx.Org.IsTeamMember {
+ ctx.NotFound("OrgAssignment", err)
+ return
+ }
+
+ ctx.Org.IsTeamAdmin = ctx.Org.Team.IsOwnerTeam() || ctx.Org.Team.AccessMode >= perm.AccessModeAdmin
+ ctx.Data["IsTeamAdmin"] = ctx.Org.IsTeamAdmin
+ if requireTeamAdmin && !ctx.Org.IsTeamAdmin {
+ ctx.NotFound("OrgAssignment", err)
+ return
+ }
+ }
+ ctx.Data["ContextUser"] = ctx.ContextUser
+
+ ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
+ ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages)
+ ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode)
+
+ ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
+ if len(ctx.ContextUser.Description) != 0 {
+ content, err := markdown.RenderString(&markup.RenderContext{
+ Metas: map[string]string{"mode": "document"},
+ Ctx: ctx,
+ }, ctx.ContextUser.Description)
+ if err != nil {
+ ctx.ServerError("RenderString", err)
+ return
+ }
+ ctx.Data["RenderedDescription"] = content
+ }
+}
+
+// OrgAssignment returns a middleware to handle organization assignment
+func OrgAssignment(args ...bool) func(ctx *Context) {
+ return func(ctx *Context) {
+ HandleOrgAssignment(ctx, args...)
+ }
+}
--- /dev/null
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "fmt"
+ "net/http"
+
+ "code.gitea.io/gitea/models/organization"
+ packages_model "code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/templates"
+)
+
+// Package contains owner, access mode and optional the package descriptor
+type Package struct {
+ Owner *user_model.User
+ AccessMode perm.AccessMode
+ Descriptor *packages_model.PackageDescriptor
+}
+
+type packageAssignmentCtx struct {
+ *Base
+ Doer *user_model.User
+ ContextUser *user_model.User
+}
+
+// PackageAssignment returns a middleware to handle Context.Package assignment
+func PackageAssignment() func(ctx *Context) {
+ return func(ctx *Context) {
+ errorFn := func(status int, title string, obj any) {
+ err, ok := obj.(error)
+ if !ok {
+ err = fmt.Errorf("%s", obj)
+ }
+ if status == http.StatusNotFound {
+ ctx.NotFound(title, err)
+ } else {
+ ctx.ServerError(title, err)
+ }
+ }
+ paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
+ ctx.Package = packageAssignment(paCtx, errorFn)
+ }
+}
+
+// PackageAssignmentAPI returns a middleware to handle Context.Package assignment
+func PackageAssignmentAPI() func(ctx *APIContext) {
+ return func(ctx *APIContext) {
+ paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
+ ctx.Package = packageAssignment(paCtx, ctx.Error)
+ }
+}
+
+func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any)) *Package {
+ pkg := &Package{
+ Owner: ctx.ContextUser,
+ }
+ var err error
+ pkg.AccessMode, err = determineAccessMode(ctx.Base, pkg, ctx.Doer)
+ if err != nil {
+ errCb(http.StatusInternalServerError, "determineAccessMode", err)
+ return pkg
+ }
+
+ packageType := ctx.Params("type")
+ name := ctx.Params("name")
+ version := ctx.Params("version")
+ if packageType != "" && name != "" && version != "" {
+ pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
+ if err != nil {
+ if err == packages_model.ErrPackageNotExist {
+ errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err)
+ } else {
+ errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err)
+ }
+ return pkg
+ }
+
+ pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
+ if err != nil {
+ errCb(http.StatusInternalServerError, "GetPackageDescriptor", err)
+ return pkg
+ }
+ }
+
+ return pkg
+}
+
+func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
+ if setting.Service.RequireSignInView && (doer == nil || doer.IsGhost()) {
+ return perm.AccessModeNone, nil
+ }
+
+ if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) {
+ return perm.AccessModeNone, nil
+ }
+
+ // TODO: ActionUser permission check
+ accessMode := perm.AccessModeNone
+ if pkg.Owner.IsOrganization() {
+ org := organization.OrgFromUser(pkg.Owner)
+
+ if doer != nil && !doer.IsGhost() {
+ // 1. If user is logged in, check all team packages permissions
+ var err error
+ accessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx, doer.ID)
+ if err != nil {
+ return accessMode, err
+ }
+ // If access mode is less than write check every team for more permissions
+ // The minimum possible access mode is read for org members
+ if accessMode < perm.AccessModeWrite {
+ teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
+ if err != nil {
+ return accessMode, err
+ }
+ for _, t := range teams {
+ perm := t.UnitAccessMode(ctx, unit.TypePackages)
+ if accessMode < perm {
+ accessMode = perm
+ }
+ }
+ }
+ }
+ if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
+ // 2. If user is unauthorized or no org member, check if org is visible
+ accessMode = perm.AccessModeRead
+ }
+ } else {
+ if doer != nil && !doer.IsGhost() {
+ // 1. Check if user is package owner
+ if doer.ID == pkg.Owner.ID {
+ accessMode = perm.AccessModeOwner
+ } else if pkg.Owner.Visibility == structs.VisibleTypePublic || pkg.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
+ accessMode = perm.AccessModeRead
+ }
+ } else if pkg.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
+ accessMode = perm.AccessModeRead
+ }
+ }
+
+ return accessMode, nil
+}
+
+// PackageContexter initializes a package context for a request.
+func PackageContexter() func(next http.Handler) http.Handler {
+ renderer := templates.HTMLRenderer()
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+ base, baseCleanUp := NewBaseContext(resp, req)
+ defer baseCleanUp()
+
+ // it is still needed when rendering 500 page in a package handler
+ ctx := NewWebContext(base, renderer, nil)
+ ctx.Base.AppendContextValue(WebContextKey, ctx)
+ next.ServeHTTP(ctx.Resp, ctx.Req)
+ })
+ }
+}
--- /dev/null
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "fmt"
+ "html/template"
+ "net/url"
+ "strings"
+
+ "code.gitea.io/gitea/modules/paginator"
+)
+
+// Pagination provides a pagination via paginator.Paginator and additional configurations for the link params used in rendering
+type Pagination struct {
+ Paginater *paginator.Paginator
+ urlParams []string
+}
+
+// NewPagination creates a new instance of the Pagination struct.
+// "pagingNum" is "page size" or "limit", "current" is "page"
+func NewPagination(total, pagingNum, current, numPages int) *Pagination {
+ p := &Pagination{}
+ p.Paginater = paginator.New(total, pagingNum, current, numPages)
+ return p
+}
+
+// AddParam adds a value from context identified by ctxKey as link param under a given paramKey
+func (p *Pagination) AddParam(ctx *Context, paramKey, ctxKey string) {
+ _, exists := ctx.Data[ctxKey]
+ if !exists {
+ return
+ }
+ paramData := fmt.Sprintf("%v", ctx.Data[ctxKey]) // cast any to string
+ urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(paramKey), url.QueryEscape(paramData))
+ p.urlParams = append(p.urlParams, urlParam)
+}
+
+// AddParamString adds a string parameter directly
+func (p *Pagination) AddParamString(key, value string) {
+ urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(key), url.QueryEscape(value))
+ p.urlParams = append(p.urlParams, urlParam)
+}
+
+// GetParams returns the configured URL params
+func (p *Pagination) GetParams() template.URL {
+ return template.URL(strings.Join(p.urlParams, "&"))
+}
+
+// SetDefaultParams sets common pagination params that are often used
+func (p *Pagination) SetDefaultParams(ctx *Context) {
+ p.AddParam(ctx, "sort", "SortType")
+ p.AddParam(ctx, "q", "Keyword")
+ // do not add any more uncommon params here!
+ p.AddParam(ctx, "t", "queryType")
+}
--- /dev/null
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "net/http"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/modules/log"
+)
+
+// RequireRepoAdmin returns a middleware for requiring repository admin permission
+func RequireRepoAdmin() func(ctx *Context) {
+ return func(ctx *Context) {
+ if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ }
+}
+
+// RequireRepoWriter returns a middleware for requiring repository write to the specify unitType
+func RequireRepoWriter(unitType unit.Type) func(ctx *Context) {
+ return func(ctx *Context) {
+ if !ctx.Repo.CanWrite(unitType) {
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ }
+}
+
+// CanEnableEditor checks if the user is allowed to write to the branch of the repo
+func CanEnableEditor() func(ctx *Context) {
+ return func(ctx *Context) {
+ if !ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, ctx.Repo.BranchName) {
+ ctx.NotFound("CanWriteToBranch denies permission", nil)
+ return
+ }
+ }
+}
+
+// RequireRepoWriterOr returns a middleware for requiring repository write to one of the unit permission
+func RequireRepoWriterOr(unitTypes ...unit.Type) func(ctx *Context) {
+ return func(ctx *Context) {
+ for _, unitType := range unitTypes {
+ if ctx.Repo.CanWrite(unitType) {
+ return
+ }
+ }
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ }
+}
+
+// RequireRepoReader returns a middleware for requiring repository read to the specify unitType
+func RequireRepoReader(unitType unit.Type) func(ctx *Context) {
+ return func(ctx *Context) {
+ if !ctx.Repo.CanRead(unitType) {
+ if log.IsTrace() {
+ if ctx.IsSigned {
+ log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+
+ "User in Repo has Permissions: %-+v",
+ ctx.Doer,
+ unitType,
+ ctx.Repo.Repository,
+ ctx.Repo.Permission)
+ } else {
+ log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+
+ "Anonymous user in Repo has Permissions: %-+v",
+ unitType,
+ ctx.Repo.Repository,
+ ctx.Repo.Permission)
+ }
+ }
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ }
+}
+
+// RequireRepoReaderOr returns a middleware for requiring repository write to one of the unit permission
+func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) {
+ return func(ctx *Context) {
+ for _, unitType := range unitTypes {
+ if ctx.Repo.CanRead(unitType) {
+ return
+ }
+ }
+ if log.IsTrace() {
+ var format string
+ var args []any
+ if ctx.IsSigned {
+ format = "Permission Denied: User %-v cannot read ["
+ args = append(args, ctx.Doer)
+ } else {
+ format = "Permission Denied: Anonymous user cannot read ["
+ }
+ for _, unit := range unitTypes {
+ format += "%-v, "
+ args = append(args, unit)
+ }
+
+ format = format[:len(format)-2] + "] in Repo %-v\n" +
+ "User in Repo has Permissions: %-+v"
+ args = append(args, ctx.Repo.Repository, ctx.Repo.Permission)
+ log.Trace(format, args...)
+ }
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ }
+}
+
+// CheckRepoScopedToken check whether personal access token has repo scope
+func CheckRepoScopedToken(ctx *Context, repo *repo_model.Repository, level auth_model.AccessTokenScopeLevel) {
+ if !ctx.IsBasicAuth || ctx.Data["IsApiToken"] != true {
+ return
+ }
+
+ scope, ok := ctx.Data["ApiTokenScope"].(auth_model.AccessTokenScope)
+ if ok { // it's a personal access token but not oauth2 token
+ var scopeMatched bool
+
+ requiredScopes := auth_model.GetRequiredScopes(level, auth_model.AccessTokenScopeCategoryRepository)
+
+ // check if scope only applies to public resources
+ publicOnly, err := scope.PublicOnly()
+ if err != nil {
+ ctx.ServerError("HasScope", err)
+ return
+ }
+
+ if publicOnly && repo.IsPrivate {
+ ctx.Error(http.StatusForbidden)
+ return
+ }
+
+ scopeMatched, err = scope.HasScope(requiredScopes...)
+ if err != nil {
+ ctx.ServerError("HasScope", err)
+ return
+ }
+
+ if !scopeMatched {
+ ctx.Error(http.StatusForbidden)
+ return
+ }
+ }
+}
--- /dev/null
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "time"
+
+ "code.gitea.io/gitea/modules/graceful"
+ "code.gitea.io/gitea/modules/process"
+ "code.gitea.io/gitea/modules/web"
+ web_types "code.gitea.io/gitea/modules/web/types"
+)
+
+// PrivateContext represents a context for private routes
+type PrivateContext struct {
+ *Base
+ Override context.Context
+
+ Repo *Repository
+}
+
+func init() {
+ web.RegisterResponseStatusProvider[*PrivateContext](func(req *http.Request) web_types.ResponseStatusProvider {
+ return req.Context().Value(privateContextKey).(*PrivateContext)
+ })
+}
+
+// Deadline is part of the interface for context.Context and we pass this to the request context
+func (ctx *PrivateContext) Deadline() (deadline time.Time, ok bool) {
+ if ctx.Override != nil {
+ return ctx.Override.Deadline()
+ }
+ return ctx.Base.Deadline()
+}
+
+// Done is part of the interface for context.Context and we pass this to the request context
+func (ctx *PrivateContext) Done() <-chan struct{} {
+ if ctx.Override != nil {
+ return ctx.Override.Done()
+ }
+ return ctx.Base.Done()
+}
+
+// Err is part of the interface for context.Context and we pass this to the request context
+func (ctx *PrivateContext) Err() error {
+ if ctx.Override != nil {
+ return ctx.Override.Err()
+ }
+ return ctx.Base.Err()
+}
+
+var privateContextKey any = "default_private_context"
+
+// GetPrivateContext returns a context for Private routes
+func GetPrivateContext(req *http.Request) *PrivateContext {
+ return req.Context().Value(privateContextKey).(*PrivateContext)
+}
+
+// PrivateContexter returns apicontext as middleware
+func PrivateContexter() func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+ base, baseCleanUp := NewBaseContext(w, req)
+ ctx := &PrivateContext{Base: base}
+ defer baseCleanUp()
+ ctx.Base.AppendContextValue(privateContextKey, ctx)
+
+ next.ServeHTTP(ctx.Resp, ctx.Req)
+ })
+ }
+}
+
+// OverrideContext overrides the underlying request context for Done() etc.
+// This function should be used when there is a need for work to continue even if the request has been cancelled.
+// Primarily this affects hook/post-receive and hook/proc-receive both of which need to continue working even if
+// the underlying request has timed out from the ssh/http push
+func OverrideContext(ctx *PrivateContext) (cancel context.CancelFunc) {
+ // We now need to override the request context as the base for our work because even if the request is cancelled we have to continue this work
+ ctx.Override, _, cancel = process.GetManager().AddTypedContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PrivateContext: %s", ctx.Req.RequestURI), process.RequestProcessType, true)
+ return cancel
+}
--- /dev/null
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "html"
+ "net/http"
+ "net/url"
+ "path"
+ "strings"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/models/db"
+ git_model "code.gitea.io/gitea/models/git"
+ issues_model "code.gitea.io/gitea/models/issues"
+ access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
+ unit_model "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/cache"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/gitrepo"
+ code_indexer "code.gitea.io/gitea/modules/indexer/code"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/optional"
+ repo_module "code.gitea.io/gitea/modules/repository"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+ asymkey_service "code.gitea.io/gitea/services/asymkey"
+
+ "github.com/editorconfig/editorconfig-core-go/v2"
+)
+
+// PullRequest contains information to make a pull request
+type PullRequest struct {
+ BaseRepo *repo_model.Repository
+ Allowed bool
+ SameRepo bool
+ HeadInfoSubURL string // [<user>:]<branch> url segment
+}
+
+// Repository contains information to operate a repository
+type Repository struct {
+ access_model.Permission
+ IsWatching bool
+ IsViewBranch bool
+ IsViewTag bool
+ IsViewCommit bool
+ Repository *repo_model.Repository
+ Owner *user_model.User
+ Commit *git.Commit
+ Tag *git.Tag
+ GitRepo *git.Repository
+ RefName string
+ BranchName string
+ TagName string
+ TreePath string
+ CommitID string
+ RepoLink string
+ CloneLink repo_model.CloneLink
+ CommitsCount int64
+
+ PullRequest *PullRequest
+}
+
+// CanWriteToBranch checks if the branch is writable by the user
+func (r *Repository) CanWriteToBranch(ctx context.Context, user *user_model.User, branch string) bool {
+ return issues_model.CanMaintainerWriteToBranch(ctx, r.Permission, branch, user)
+}
+
+// CanEnableEditor returns true if repository is editable and user has proper access level.
+func (r *Repository) CanEnableEditor(ctx context.Context, user *user_model.User) bool {
+ return r.IsViewBranch && r.CanWriteToBranch(ctx, user, r.BranchName) && r.Repository.CanEnableEditor() && !r.Repository.IsArchived
+}
+
+// CanCreateBranch returns true if repository is editable and user has proper access level.
+func (r *Repository) CanCreateBranch() bool {
+ return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch()
+}
+
+func (r *Repository) GetObjectFormat() git.ObjectFormat {
+ return git.ObjectFormatFromName(r.Repository.ObjectFormatName)
+}
+
+// RepoMustNotBeArchived checks if a repo is archived
+func RepoMustNotBeArchived() func(ctx *Context) {
+ return func(ctx *Context) {
+ if ctx.Repo.Repository.IsArchived {
+ ctx.NotFound("IsArchived", errors.New(ctx.Locale.TrString("repo.archive.title")))
+ }
+ }
+}
+
+// CanCommitToBranchResults represents the results of CanCommitToBranch
+type CanCommitToBranchResults struct {
+ CanCommitToBranch bool
+ EditorEnabled bool
+ UserCanPush bool
+ RequireSigned bool
+ WillSign bool
+ SigningKey string
+ WontSignReason string
+}
+
+// CanCommitToBranch returns true if repository is editable and user has proper access level
+//
+// and branch is not protected for push
+func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.User) (CanCommitToBranchResults, error) {
+ protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, r.Repository.ID, r.BranchName)
+ if err != nil {
+ return CanCommitToBranchResults{}, err
+ }
+ userCanPush := true
+ requireSigned := false
+ if protectedBranch != nil {
+ protectedBranch.Repo = r.Repository
+ userCanPush = protectedBranch.CanUserPush(ctx, doer)
+ requireSigned = protectedBranch.RequireSignedCommits
+ }
+
+ sign, keyID, _, err := asymkey_service.SignCRUDAction(ctx, r.Repository.RepoPath(), doer, r.Repository.RepoPath(), git.BranchPrefix+r.BranchName)
+
+ canCommit := r.CanEnableEditor(ctx, doer) && userCanPush
+ if requireSigned {
+ canCommit = canCommit && sign
+ }
+ wontSignReason := ""
+ if err != nil {
+ if asymkey_service.IsErrWontSign(err) {
+ wontSignReason = string(err.(*asymkey_service.ErrWontSign).Reason)
+ err = nil
+ } else {
+ wontSignReason = "error"
+ }
+ }
+
+ return CanCommitToBranchResults{
+ CanCommitToBranch: canCommit,
+ EditorEnabled: r.CanEnableEditor(ctx, doer),
+ UserCanPush: userCanPush,
+ RequireSigned: requireSigned,
+ WillSign: sign,
+ SigningKey: keyID,
+ WontSignReason: wontSignReason,
+ }, err
+}
+
+// CanUseTimetracker returns whether or not a user can use the timetracker.
+func (r *Repository) CanUseTimetracker(ctx context.Context, issue *issues_model.Issue, user *user_model.User) bool {
+ // Checking for following:
+ // 1. Is timetracker enabled
+ // 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
+ isAssigned, _ := issues_model.IsUserAssignedToIssue(ctx, issue, user)
+ return r.Repository.IsTimetrackerEnabled(ctx) && (!r.Repository.AllowOnlyContributorsToTrackTime(ctx) ||
+ r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned)
+}
+
+// CanCreateIssueDependencies returns whether or not a user can create dependencies.
+func (r *Repository) CanCreateIssueDependencies(ctx context.Context, user *user_model.User, isPull bool) bool {
+ return r.Repository.IsDependenciesEnabled(ctx) && r.Permission.CanWriteIssuesOrPulls(isPull)
+}
+
+// GetCommitsCount returns cached commit count for current view
+func (r *Repository) GetCommitsCount() (int64, error) {
+ if r.Commit == nil {
+ return 0, nil
+ }
+ var contextName string
+ if r.IsViewBranch {
+ contextName = r.BranchName
+ } else if r.IsViewTag {
+ contextName = r.TagName
+ } else {
+ contextName = r.CommitID
+ }
+ return cache.GetInt64(r.Repository.GetCommitsCountCacheKey(contextName, r.IsViewBranch || r.IsViewTag), func() (int64, error) {
+ return r.Commit.CommitsCount()
+ })
+}
+
+// GetCommitGraphsCount returns cached commit count for current view
+func (r *Repository) GetCommitGraphsCount(ctx context.Context, hidePRRefs bool, branches, files []string) (int64, error) {
+ cacheKey := fmt.Sprintf("commits-count-%d-graph-%t-%s-%s", r.Repository.ID, hidePRRefs, branches, files)
+
+ return cache.GetInt64(cacheKey, func() (int64, error) {
+ if len(branches) == 0 {
+ return git.AllCommitsCount(ctx, r.Repository.RepoPath(), hidePRRefs, files...)
+ }
+ return git.CommitsCount(ctx,
+ git.CommitsCountOptions{
+ RepoPath: r.Repository.RepoPath(),
+ Revision: branches,
+ RelPath: files,
+ })
+ })
+}
+
+// BranchNameSubURL sub-URL for the BranchName field
+func (r *Repository) BranchNameSubURL() string {
+ switch {
+ case r.IsViewBranch:
+ return "branch/" + util.PathEscapeSegments(r.BranchName)
+ case r.IsViewTag:
+ return "tag/" + util.PathEscapeSegments(r.TagName)
+ case r.IsViewCommit:
+ return "commit/" + util.PathEscapeSegments(r.CommitID)
+ }
+ log.Error("Unknown view type for repo: %v", r)
+ return ""
+}
+
+// FileExists returns true if a file exists in the given repo branch
+func (r *Repository) FileExists(path, branch string) (bool, error) {
+ if branch == "" {
+ branch = r.Repository.DefaultBranch
+ }
+ commit, err := r.GitRepo.GetBranchCommit(branch)
+ if err != nil {
+ return false, err
+ }
+ if _, err := commit.GetTreeEntryByPath(path); err != nil {
+ return false, err
+ }
+ return true, nil
+}
+
+// GetEditorconfig returns the .editorconfig definition if found in the
+// HEAD of the default repo branch.
+func (r *Repository) GetEditorconfig(optCommit ...*git.Commit) (cfg *editorconfig.Editorconfig, warning, err error) {
+ if r.GitRepo == nil {
+ return nil, nil, nil
+ }
+
+ var commit *git.Commit
+
+ if len(optCommit) != 0 {
+ commit = optCommit[0]
+ } else {
+ commit, err = r.GitRepo.GetBranchCommit(r.Repository.DefaultBranch)
+ if err != nil {
+ return nil, nil, err
+ }
+ }
+ treeEntry, err := commit.GetTreeEntryByPath(".editorconfig")
+ if err != nil {
+ return nil, nil, err
+ }
+ if treeEntry.Blob().Size() >= setting.UI.MaxDisplayFileSize {
+ return nil, nil, git.ErrNotExist{ID: "", RelPath: ".editorconfig"}
+ }
+ reader, err := treeEntry.Blob().DataAsync()
+ if err != nil {
+ return nil, nil, err
+ }
+ defer reader.Close()
+ return editorconfig.ParseGraceful(reader)
+}
+
+// RetrieveBaseRepo retrieves base repository
+func RetrieveBaseRepo(ctx *Context, repo *repo_model.Repository) {
+ // Non-fork repository will not return error in this method.
+ if err := repo.GetBaseRepo(ctx); err != nil {
+ if repo_model.IsErrRepoNotExist(err) {
+ repo.IsFork = false
+ repo.ForkID = 0
+ return
+ }
+ ctx.ServerError("GetBaseRepo", err)
+ return
+ } else if err = repo.BaseRepo.LoadOwner(ctx); err != nil {
+ ctx.ServerError("BaseRepo.LoadOwner", err)
+ return
+ }
+}
+
+// RetrieveTemplateRepo retrieves template repository used to generate this repository
+func RetrieveTemplateRepo(ctx *Context, repo *repo_model.Repository) {
+ // Non-generated repository will not return error in this method.
+ templateRepo, err := repo_model.GetTemplateRepo(ctx, repo)
+ if err != nil {
+ if repo_model.IsErrRepoNotExist(err) {
+ repo.TemplateID = 0
+ return
+ }
+ ctx.ServerError("GetTemplateRepo", err)
+ return
+ } else if err = templateRepo.LoadOwner(ctx); err != nil {
+ ctx.ServerError("TemplateRepo.LoadOwner", err)
+ return
+ }
+
+ perm, err := access_model.GetUserRepoPermission(ctx, templateRepo, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("GetUserRepoPermission", err)
+ return
+ }
+
+ if !perm.CanRead(unit_model.TypeCode) {
+ repo.TemplateID = 0
+ }
+}
+
+// ComposeGoGetImport returns go-get-import meta content.
+func ComposeGoGetImport(owner, repo string) string {
+ /// setting.AppUrl is guaranteed to be parse as url
+ appURL, _ := url.Parse(setting.AppURL)
+
+ return path.Join(appURL.Host, setting.AppSubURL, url.PathEscape(owner), url.PathEscape(repo))
+}
+
+// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
+// if user does not have actual access to the requested repository,
+// or the owner or repository does not exist at all.
+// This is particular a workaround for "go get" command which does not respect
+// .netrc file.
+func EarlyResponseForGoGetMeta(ctx *Context) {
+ username := ctx.Params(":username")
+ reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git")
+ if username == "" || reponame == "" {
+ ctx.PlainText(http.StatusBadRequest, "invalid repository path")
+ return
+ }
+
+ var cloneURL string
+ if setting.Repository.GoGetCloneURLProtocol == "ssh" {
+ cloneURL = repo_model.ComposeSSHCloneURL(username, reponame)
+ } else {
+ cloneURL = repo_model.ComposeHTTPSCloneURL(username, reponame)
+ }
+ goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(username, reponame), cloneURL)
+ htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
+ ctx.PlainText(http.StatusOK, htmlMeta)
+}
+
+// RedirectToRepo redirect to a differently-named repository
+func RedirectToRepo(ctx *Base, redirectRepoID int64) {
+ ownerName := ctx.Params(":username")
+ previousRepoName := ctx.Params(":reponame")
+
+ repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID)
+ if err != nil {
+ log.Error("GetRepositoryByID: %v", err)
+ ctx.Error(http.StatusInternalServerError, "GetRepositoryByID")
+ return
+ }
+
+ redirectPath := strings.Replace(
+ ctx.Req.URL.EscapedPath(),
+ url.PathEscape(ownerName)+"/"+url.PathEscape(previousRepoName),
+ url.PathEscape(repo.OwnerName)+"/"+url.PathEscape(repo.Name),
+ 1,
+ )
+ if ctx.Req.URL.RawQuery != "" {
+ redirectPath += "?" + ctx.Req.URL.RawQuery
+ }
+ ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect)
+}
+
+func repoAssignment(ctx *Context, repo *repo_model.Repository) {
+ var err error
+ if err = repo.LoadOwner(ctx); err != nil {
+ ctx.ServerError("LoadOwner", err)
+ return
+ }
+
+ ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("GetUserRepoPermission", err)
+ return
+ }
+
+ // Check access.
+ if !ctx.Repo.Permission.HasAccess() {
+ if ctx.FormString("go-get") == "1" {
+ EarlyResponseForGoGetMeta(ctx)
+ return
+ }
+ ctx.NotFound("no access right", nil)
+ return
+ }
+ ctx.Data["HasAccess"] = true
+ ctx.Data["Permission"] = &ctx.Repo.Permission
+
+ if repo.IsMirror {
+ pullMirror, err := repo_model.GetMirrorByRepoID(ctx, repo.ID)
+ if err == nil {
+ ctx.Data["PullMirror"] = pullMirror
+ } else if err != repo_model.ErrMirrorNotExist {
+ ctx.ServerError("GetMirrorByRepoID", err)
+ return
+ }
+ }
+
+ pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
+ if err != nil {
+ ctx.ServerError("GetPushMirrorsByRepoID", err)
+ return
+ }
+
+ ctx.Repo.Repository = repo
+ ctx.Data["PushMirrors"] = pushMirrors
+ ctx.Data["RepoName"] = ctx.Repo.Repository.Name
+ ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty
+}
+
+// RepoIDAssignment returns a handler which assigns the repo to the context.
+func RepoIDAssignment() func(ctx *Context) {
+ return func(ctx *Context) {
+ repoID := ctx.ParamsInt64(":repoid")
+
+ // Get repository.
+ repo, err := repo_model.GetRepositoryByID(ctx, repoID)
+ if err != nil {
+ if repo_model.IsErrRepoNotExist(err) {
+ ctx.NotFound("GetRepositoryByID", nil)
+ } else {
+ ctx.ServerError("GetRepositoryByID", err)
+ }
+ return
+ }
+
+ repoAssignment(ctx, repo)
+ }
+}
+
+// RepoAssignment returns a middleware to handle repository assignment
+func RepoAssignment(ctx *Context) context.CancelFunc {
+ if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
+ log.Trace("RepoAssignment was exec already, skipping second call ...")
+ return nil
+ }
+ ctx.Data["repoAssignmentExecuted"] = true
+
+ var (
+ owner *user_model.User
+ err error
+ )
+
+ userName := ctx.Params(":username")
+ repoName := ctx.Params(":reponame")
+ repoName = strings.TrimSuffix(repoName, ".git")
+ if setting.Other.EnableFeed {
+ repoName = strings.TrimSuffix(repoName, ".rss")
+ repoName = strings.TrimSuffix(repoName, ".atom")
+ }
+
+ // Check if the user is the same as the repository owner
+ if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(userName) {
+ owner = ctx.Doer
+ } else {
+ owner, err = user_model.GetUserByName(ctx, userName)
+ if err != nil {
+ if user_model.IsErrUserNotExist(err) {
+ // go-get does not support redirects
+ // https://github.com/golang/go/issues/19760
+ if ctx.FormString("go-get") == "1" {
+ EarlyResponseForGoGetMeta(ctx)
+ return nil
+ }
+
+ if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil {
+ RedirectToUser(ctx.Base, userName, redirectUserID)
+ } else if user_model.IsErrUserRedirectNotExist(err) {
+ ctx.NotFound("GetUserByName", nil)
+ } else {
+ ctx.ServerError("LookupUserRedirect", err)
+ }
+ } else {
+ ctx.ServerError("GetUserByName", err)
+ }
+ return nil
+ }
+ }
+ ctx.Repo.Owner = owner
+ ctx.ContextUser = owner
+ ctx.Data["ContextUser"] = ctx.ContextUser
+ ctx.Data["Username"] = ctx.Repo.Owner.Name
+
+ // redirect link to wiki
+ if strings.HasSuffix(repoName, ".wiki") {
+ // ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added
+ // Now we happen to know that all of our paths are: /:username/:reponame/whatever_else
+ originalRepoName := ctx.Params(":reponame")
+ redirectRepoName := strings.TrimSuffix(repoName, ".wiki")
+ redirectRepoName += originalRepoName[len(redirectRepoName)+5:]
+ redirectPath := strings.Replace(
+ ctx.Req.URL.EscapedPath(),
+ url.PathEscape(userName)+"/"+url.PathEscape(originalRepoName),
+ url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki",
+ 1,
+ )
+ if ctx.Req.URL.RawQuery != "" {
+ redirectPath += "?" + ctx.Req.URL.RawQuery
+ }
+ ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
+ return nil
+ }
+
+ // Get repository.
+ repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName)
+ if err != nil {
+ if repo_model.IsErrRepoNotExist(err) {
+ redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName)
+ if err == nil {
+ RedirectToRepo(ctx.Base, redirectRepoID)
+ } else if repo_model.IsErrRedirectNotExist(err) {
+ if ctx.FormString("go-get") == "1" {
+ EarlyResponseForGoGetMeta(ctx)
+ return nil
+ }
+ ctx.NotFound("GetRepositoryByName", nil)
+ } else {
+ ctx.ServerError("LookupRepoRedirect", err)
+ }
+ } else {
+ ctx.ServerError("GetRepositoryByName", err)
+ }
+ return nil
+ }
+ repo.Owner = owner
+
+ repoAssignment(ctx, repo)
+ if ctx.Written() {
+ return nil
+ }
+
+ ctx.Repo.RepoLink = repo.Link()
+ ctx.Data["RepoLink"] = ctx.Repo.RepoLink
+ ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
+
+ if setting.Other.EnableFeed {
+ ctx.Data["EnableFeed"] = true
+ ctx.Data["FeedURL"] = ctx.Repo.RepoLink
+ }
+
+ unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker)
+ if err == nil {
+ ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
+ }
+
+ ctx.Data["NumTags"], err = db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
+ IncludeDrafts: true,
+ IncludeTags: true,
+ HasSha1: util.OptionalBoolTrue, // only draft releases which are created with existing tags
+ RepoID: ctx.Repo.Repository.ID,
+ })
+ if err != nil {
+ ctx.ServerError("GetReleaseCountByRepoID", err)
+ return nil
+ }
+ ctx.Data["NumReleases"], err = db.Count[repo_model.Release](ctx, repo_model.FindReleasesOptions{
+ // only show draft releases for users who can write, read-only users shouldn't see draft releases.
+ IncludeDrafts: ctx.Repo.CanWrite(unit_model.TypeReleases),
+ RepoID: ctx.Repo.Repository.ID,
+ })
+ if err != nil {
+ ctx.ServerError("GetReleaseCountByRepoID", err)
+ return nil
+ }
+
+ ctx.Data["Title"] = owner.Name + "/" + repo.Name
+ ctx.Data["Repository"] = repo
+ ctx.Data["Owner"] = ctx.Repo.Repository.Owner
+ ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner()
+ ctx.Data["IsRepositoryAdmin"] = ctx.Repo.IsAdmin()
+ ctx.Data["RepoOwnerIsOrganization"] = repo.Owner.IsOrganization()
+ ctx.Data["CanWriteCode"] = ctx.Repo.CanWrite(unit_model.TypeCode)
+ ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(unit_model.TypeIssues)
+ ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(unit_model.TypePullRequests)
+ ctx.Data["CanWriteActions"] = ctx.Repo.CanWrite(unit_model.TypeActions)
+
+ canSignedUserFork, err := repo_module.CanUserForkRepo(ctx, ctx.Doer, ctx.Repo.Repository)
+ if err != nil {
+ ctx.ServerError("CanUserForkRepo", err)
+ return nil
+ }
+ ctx.Data["CanSignedUserFork"] = canSignedUserFork
+
+ userAndOrgForks, err := repo_model.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository)
+ if err != nil {
+ ctx.ServerError("GetForksByUserAndOrgs", err)
+ return nil
+ }
+ ctx.Data["UserAndOrgForks"] = userAndOrgForks
+
+ // canSignedUserFork is true if the current user doesn't have a fork of this repo yet or
+ // if he owns an org that doesn't have a fork of this repo yet
+ // If multiple forks are available or if the user can fork to another account, but there is already a fork: open selection dialog
+ ctx.Data["ShowForkModal"] = len(userAndOrgForks) > 1 || (canSignedUserFork && len(userAndOrgForks) > 0)
+
+ ctx.Data["RepoCloneLink"] = repo.CloneLink()
+
+ cloneButtonShowHTTPS := !setting.Repository.DisableHTTPGit
+ cloneButtonShowSSH := !setting.SSH.Disabled && (ctx.IsSigned || setting.SSH.ExposeAnonymous)
+ if !cloneButtonShowHTTPS && !cloneButtonShowSSH {
+ // We have to show at least one link, so we just show the HTTPS
+ cloneButtonShowHTTPS = true
+ }
+ ctx.Data["CloneButtonShowHTTPS"] = cloneButtonShowHTTPS
+ ctx.Data["CloneButtonShowSSH"] = cloneButtonShowSSH
+ ctx.Data["CloneButtonOriginLink"] = ctx.Data["RepoCloneLink"] // it may be rewritten to the WikiCloneLink by the router middleware
+
+ ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled
+ if setting.Indexer.RepoIndexerEnabled {
+ ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx)
+ }
+
+ if ctx.IsSigned {
+ ctx.Data["IsWatchingRepo"] = repo_model.IsWatching(ctx, ctx.Doer.ID, repo.ID)
+ ctx.Data["IsStaringRepo"] = repo_model.IsStaring(ctx, ctx.Doer.ID, repo.ID)
+ }
+
+ if repo.IsFork {
+ RetrieveBaseRepo(ctx, repo)
+ if ctx.Written() {
+ return nil
+ }
+ }
+
+ if repo.IsGenerated() {
+ RetrieveTemplateRepo(ctx, repo)
+ if ctx.Written() {
+ return nil
+ }
+ }
+
+ isHomeOrSettings := ctx.Link == ctx.Repo.RepoLink || ctx.Link == ctx.Repo.RepoLink+"/settings" || strings.HasPrefix(ctx.Link, ctx.Repo.RepoLink+"/settings/")
+
+ // Disable everything when the repo is being created
+ if ctx.Repo.Repository.IsBeingCreated() || ctx.Repo.Repository.IsBroken() {
+ ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
+ if !isHomeOrSettings {
+ ctx.Redirect(ctx.Repo.RepoLink)
+ }
+ return nil
+ }
+
+ gitRepo, err := gitrepo.OpenRepository(ctx, repo)
+ if err != nil {
+ if strings.Contains(err.Error(), "repository does not exist") || strings.Contains(err.Error(), "no such file or directory") {
+ log.Error("Repository %-v has a broken repository on the file system: %s Error: %v", ctx.Repo.Repository, ctx.Repo.Repository.RepoPath(), err)
+ ctx.Repo.Repository.MarkAsBrokenEmpty()
+ ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
+ // Only allow access to base of repo or settings
+ if !isHomeOrSettings {
+ ctx.Redirect(ctx.Repo.RepoLink)
+ }
+ return nil
+ }
+ ctx.ServerError("RepoAssignment Invalid repo "+repo.FullName(), err)
+ return nil
+ }
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ }
+ ctx.Repo.GitRepo = gitRepo
+
+ // We opened it, we should close it
+ cancel := func() {
+ // If it's been set to nil then assume someone else has closed it.
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ }
+ }
+
+ // Stop at this point when the repo is empty.
+ if ctx.Repo.Repository.IsEmpty {
+ ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
+ return cancel
+ }
+
+ branchOpts := git_model.FindBranchOptions{
+ RepoID: ctx.Repo.Repository.ID,
+ IsDeletedBranch: optional.Some(false),
+ ListOptions: db.ListOptionsAll,
+ }
+ branchesTotal, err := db.Count[git_model.Branch](ctx, branchOpts)
+ if err != nil {
+ ctx.ServerError("CountBranches", err)
+ return cancel
+ }
+
+ // non-empty repo should have at least 1 branch, so this repository's branches haven't been synced yet
+ if branchesTotal == 0 { // fallback to do a sync immediately
+ branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0)
+ if err != nil {
+ ctx.ServerError("SyncRepoBranches", err)
+ return cancel
+ }
+ }
+
+ ctx.Data["BranchesCount"] = branchesTotal
+
+ // If no branch is set in the request URL, try to guess a default one.
+ if len(ctx.Repo.BranchName) == 0 {
+ if len(ctx.Repo.Repository.DefaultBranch) > 0 && gitRepo.IsBranchExist(ctx.Repo.Repository.DefaultBranch) {
+ ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
+ } else {
+ ctx.Repo.BranchName, _ = gitRepo.GetDefaultBranch()
+ if ctx.Repo.BranchName == "" {
+ // If it still can't get a default branch, fall back to default branch from setting.
+ // Something might be wrong. Either site admin should fix the repo sync or Gitea should fix a potential bug.
+ ctx.Repo.BranchName = setting.Repository.DefaultBranch
+ }
+ }
+ ctx.Repo.RefName = ctx.Repo.BranchName
+ }
+ ctx.Data["BranchName"] = ctx.Repo.BranchName
+
+ // People who have push access or have forked repository can propose a new pull request.
+ canPush := ctx.Repo.CanWrite(unit_model.TypeCode) ||
+ (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))
+ canCompare := false
+
+ // Pull request is allowed if this is a fork repository
+ // and base repository accepts pull requests.
+ if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls(ctx) {
+ canCompare = true
+ ctx.Data["BaseRepo"] = repo.BaseRepo
+ ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
+ ctx.Repo.PullRequest.Allowed = canPush
+ ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.Repo.Owner.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
+ } else if repo.AllowsPulls(ctx) {
+ // Or, this is repository accepts pull requests between branches.
+ canCompare = true
+ ctx.Data["BaseRepo"] = repo
+ ctx.Repo.PullRequest.BaseRepo = repo
+ ctx.Repo.PullRequest.Allowed = canPush
+ ctx.Repo.PullRequest.SameRepo = true
+ ctx.Repo.PullRequest.HeadInfoSubURL = util.PathEscapeSegments(ctx.Repo.BranchName)
+ }
+ ctx.Data["CanCompareOrPull"] = canCompare
+ ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
+
+ if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer {
+ repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository)
+ if err != nil {
+ ctx.ServerError("GetPendingRepositoryTransfer", err)
+ return cancel
+ }
+
+ if err := repoTransfer.LoadAttributes(ctx); err != nil {
+ ctx.ServerError("LoadRecipient", err)
+ return cancel
+ }
+
+ ctx.Data["RepoTransfer"] = repoTransfer
+ if ctx.Doer != nil {
+ ctx.Data["CanUserAcceptTransfer"] = repoTransfer.CanUserAcceptTransfer(ctx, ctx.Doer)
+ }
+ }
+
+ if ctx.FormString("go-get") == "1" {
+ ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
+ fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
+ ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}"
+ ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}"
+ }
+ return cancel
+}
+
+// RepoRefType type of repo reference
+type RepoRefType int
+
+const (
+ // RepoRefLegacy unknown type, make educated guess and redirect.
+ // for backward compatibility with previous URL scheme
+ RepoRefLegacy RepoRefType = iota
+ // RepoRefAny is for usage where educated guess is needed
+ // but redirect can not be made
+ RepoRefAny
+ // RepoRefBranch branch
+ RepoRefBranch
+ // RepoRefTag tag
+ RepoRefTag
+ // RepoRefCommit commit
+ RepoRefCommit
+ // RepoRefBlob blob
+ RepoRefBlob
+)
+
+const headRefName = "HEAD"
+
+// RepoRef handles repository reference names when the ref name is not
+// explicitly given
+func RepoRef() func(*Context) context.CancelFunc {
+ // since no ref name is explicitly specified, ok to just use branch
+ return RepoRefByType(RepoRefBranch)
+}
+
+// RefTypeIncludesBranches returns true if ref type can be a branch
+func (rt RepoRefType) RefTypeIncludesBranches() bool {
+ if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefBranch {
+ return true
+ }
+ return false
+}
+
+// RefTypeIncludesTags returns true if ref type can be a tag
+func (rt RepoRefType) RefTypeIncludesTags() bool {
+ if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefTag {
+ return true
+ }
+ return false
+}
+
+func getRefNameFromPath(ctx *Base, repo *Repository, path string, isExist func(string) bool) string {
+ refName := ""
+ parts := strings.Split(path, "/")
+ for i, part := range parts {
+ refName = strings.TrimPrefix(refName+"/"+part, "/")
+ if isExist(refName) {
+ repo.TreePath = strings.Join(parts[i+1:], "/")
+ return refName
+ }
+ }
+ return ""
+}
+
+func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
+ path := ctx.Params("*")
+ switch pathType {
+ case RepoRefLegacy, RepoRefAny:
+ if refName := getRefName(ctx, repo, RepoRefBranch); len(refName) > 0 {
+ return refName
+ }
+ if refName := getRefName(ctx, repo, RepoRefTag); len(refName) > 0 {
+ return refName
+ }
+ // For legacy and API support only full commit sha
+ parts := strings.Split(path, "/")
+
+ if len(parts) > 0 && len(parts[0]) == git.ObjectFormatFromName(repo.Repository.ObjectFormatName).FullLength() {
+ repo.TreePath = strings.Join(parts[1:], "/")
+ return parts[0]
+ }
+ if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
+ return refName
+ }
+ repo.TreePath = path
+ return repo.Repository.DefaultBranch
+ case RepoRefBranch:
+ ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist)
+ if len(ref) == 0 {
+
+ // check if ref is HEAD
+ parts := strings.Split(path, "/")
+ if parts[0] == headRefName {
+ repo.TreePath = strings.Join(parts[1:], "/")
+ return repo.Repository.DefaultBranch
+ }
+
+ // maybe it's a renamed branch
+ return getRefNameFromPath(ctx, repo, path, func(s string) bool {
+ b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s)
+ if err != nil {
+ log.Error("FindRenamedBranch: %v", err)
+ return false
+ }
+
+ if !exist {
+ return false
+ }
+
+ ctx.Data["IsRenamedBranch"] = true
+ ctx.Data["RenamedBranchName"] = b.To
+
+ return true
+ })
+ }
+
+ return ref
+ case RepoRefTag:
+ return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
+ case RepoRefCommit:
+ parts := strings.Split(path, "/")
+
+ if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= repo.GetObjectFormat().FullLength() {
+ repo.TreePath = strings.Join(parts[1:], "/")
+ return parts[0]
+ }
+
+ if len(parts) > 0 && parts[0] == headRefName {
+ // HEAD ref points to last default branch commit
+ commit, err := repo.GitRepo.GetBranchCommit(repo.Repository.DefaultBranch)
+ if err != nil {
+ return ""
+ }
+ repo.TreePath = strings.Join(parts[1:], "/")
+ return commit.ID.String()
+ }
+ case RepoRefBlob:
+ _, err := repo.GitRepo.GetBlob(path)
+ if err != nil {
+ return ""
+ }
+ return path
+ default:
+ log.Error("Unrecognized path type: %v", path)
+ }
+ return ""
+}
+
+// RepoRefByType handles repository reference name for a specific type
+// of repository reference
+func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context) context.CancelFunc {
+ return func(ctx *Context) (cancel context.CancelFunc) {
+ // Empty repository does not have reference information.
+ if ctx.Repo.Repository.IsEmpty {
+ // assume the user is viewing the (non-existent) default branch
+ ctx.Repo.IsViewBranch = true
+ ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch
+ ctx.Data["TreePath"] = ""
+ return nil
+ }
+
+ var (
+ refName string
+ err error
+ )
+
+ if ctx.Repo.GitRepo == nil {
+ ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
+ if err != nil {
+ ctx.ServerError(fmt.Sprintf("Open Repository %v failed", ctx.Repo.Repository.FullName()), err)
+ return nil
+ }
+ // We opened it, we should close it
+ cancel = func() {
+ // If it's been set to nil then assume someone else has closed it.
+ if ctx.Repo.GitRepo != nil {
+ ctx.Repo.GitRepo.Close()
+ }
+ }
+ }
+
+ // Get default branch.
+ if len(ctx.Params("*")) == 0 {
+ refName = ctx.Repo.Repository.DefaultBranch
+ if !ctx.Repo.GitRepo.IsBranchExist(refName) {
+ brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 1)
+ if err == nil && len(brs) != 0 {
+ refName = brs[0].Name
+ } else if len(brs) == 0 {
+ log.Error("No branches in non-empty repository %s", ctx.Repo.GitRepo.Path)
+ ctx.Repo.Repository.MarkAsBrokenEmpty()
+ } else {
+ log.Error("GetBranches error: %v", err)
+ ctx.Repo.Repository.MarkAsBrokenEmpty()
+ }
+ }
+ ctx.Repo.RefName = refName
+ ctx.Repo.BranchName = refName
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
+ if err == nil {
+ ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
+ } else if strings.Contains(err.Error(), "fatal: not a git repository") || strings.Contains(err.Error(), "object does not exist") {
+ // if the repository is broken, we can continue to the handler code, to show "Settings -> Delete Repository" for end users
+ log.Error("GetBranchCommit: %v", err)
+ ctx.Repo.Repository.MarkAsBrokenEmpty()
+ } else {
+ ctx.ServerError("GetBranchCommit", err)
+ return cancel
+ }
+ ctx.Repo.IsViewBranch = true
+ } else {
+ refName = getRefName(ctx.Base, ctx.Repo, refType)
+ ctx.Repo.RefName = refName
+ isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
+ if isRenamedBranch && has {
+ renamedBranchName := ctx.Data["RenamedBranchName"].(string)
+ ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName))
+ link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1)
+ ctx.Redirect(link)
+ return cancel
+ }
+
+ if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
+ ctx.Repo.IsViewBranch = true
+ ctx.Repo.BranchName = refName
+
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
+ if err != nil {
+ ctx.ServerError("GetBranchCommit", err)
+ return cancel
+ }
+ ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
+
+ } else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
+ ctx.Repo.IsViewTag = true
+ ctx.Repo.TagName = refName
+
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
+ if err != nil {
+ if git.IsErrNotExist(err) {
+ ctx.NotFound("GetTagCommit", err)
+ return cancel
+ }
+ ctx.ServerError("GetTagCommit", err)
+ return cancel
+ }
+ ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
+ } else if len(refName) >= 7 && len(refName) <= ctx.Repo.GetObjectFormat().FullLength() {
+ ctx.Repo.IsViewCommit = true
+ ctx.Repo.CommitID = refName
+
+ ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
+ if err != nil {
+ ctx.NotFound("GetCommit", err)
+ return cancel
+ }
+ // If short commit ID add canonical link header
+ if len(refName) < ctx.Repo.GetObjectFormat().FullLength() {
+ ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
+ util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
+ }
+ } else {
+ if len(ignoreNotExistErr) > 0 && ignoreNotExistErr[0] {
+ return cancel
+ }
+ ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
+ return cancel
+ }
+
+ if refType == RepoRefLegacy {
+ // redirect from old URL scheme to new URL scheme
+ prefix := strings.TrimPrefix(setting.AppSubURL+strings.ToLower(strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*"))), strings.ToLower(ctx.Repo.RepoLink))
+
+ ctx.Redirect(path.Join(
+ ctx.Repo.RepoLink,
+ util.PathEscapeSegments(prefix),
+ ctx.Repo.BranchNameSubURL(),
+ util.PathEscapeSegments(ctx.Repo.TreePath)))
+ return cancel
+ }
+ }
+
+ ctx.Data["BranchName"] = ctx.Repo.BranchName
+ ctx.Data["RefName"] = ctx.Repo.RefName
+ ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
+ ctx.Data["TagName"] = ctx.Repo.TagName
+ ctx.Data["CommitID"] = ctx.Repo.CommitID
+ ctx.Data["TreePath"] = ctx.Repo.TreePath
+ ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
+ ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
+ ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit
+ ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch()
+
+ ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
+ if err != nil {
+ ctx.ServerError("GetCommitsCount", err)
+ return cancel
+ }
+ ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
+ ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
+
+ return cancel
+ }
+}
+
+// GitHookService checks if repository Git hooks service has been enabled.
+func GitHookService() func(ctx *Context) {
+ return func(ctx *Context) {
+ if !ctx.Doer.CanEditGitHook() {
+ ctx.NotFound("GitHookService", nil)
+ return
+ }
+ }
+}
+
+// UnitTypes returns a middleware to set unit types to context variables.
+func UnitTypes() func(ctx *Context) {
+ return func(ctx *Context) {
+ ctx.Data["UnitTypeCode"] = unit_model.TypeCode
+ ctx.Data["UnitTypeIssues"] = unit_model.TypeIssues
+ ctx.Data["UnitTypePullRequests"] = unit_model.TypePullRequests
+ ctx.Data["UnitTypeReleases"] = unit_model.TypeReleases
+ ctx.Data["UnitTypeWiki"] = unit_model.TypeWiki
+ ctx.Data["UnitTypeExternalWiki"] = unit_model.TypeExternalWiki
+ ctx.Data["UnitTypeExternalTracker"] = unit_model.TypeExternalTracker
+ ctx.Data["UnitTypeProjects"] = unit_model.TypeProjects
+ ctx.Data["UnitTypePackages"] = unit_model.TypePackages
+ ctx.Data["UnitTypeActions"] = unit_model.TypeActions
+ }
+}
--- /dev/null
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "net/http"
+
+ web_types "code.gitea.io/gitea/modules/web/types"
+)
+
+// ResponseWriter represents a response writer for HTTP
+type ResponseWriter interface {
+ http.ResponseWriter
+ http.Flusher
+ web_types.ResponseStatusProvider
+
+ Before(func(ResponseWriter))
+
+ Status() int // used by access logger template
+ Size() int // used by access logger template
+}
+
+var _ ResponseWriter = &Response{}
+
+// Response represents a response
+type Response struct {
+ http.ResponseWriter
+ written int
+ status int
+ befores []func(ResponseWriter)
+ beforeExecuted bool
+}
+
+// Write writes bytes to HTTP endpoint
+func (r *Response) Write(bs []byte) (int, error) {
+ if !r.beforeExecuted {
+ for _, before := range r.befores {
+ before(r)
+ }
+ r.beforeExecuted = true
+ }
+ size, err := r.ResponseWriter.Write(bs)
+ r.written += size
+ if err != nil {
+ return size, err
+ }
+ if r.status == 0 {
+ r.status = http.StatusOK
+ }
+ return size, nil
+}
+
+func (r *Response) Status() int {
+ return r.status
+}
+
+func (r *Response) Size() int {
+ return r.written
+}
+
+// WriteHeader write status code
+func (r *Response) WriteHeader(statusCode int) {
+ if !r.beforeExecuted {
+ for _, before := range r.befores {
+ before(r)
+ }
+ r.beforeExecuted = true
+ }
+ if r.status == 0 {
+ r.status = statusCode
+ r.ResponseWriter.WriteHeader(statusCode)
+ }
+}
+
+// Flush flushes cached data
+func (r *Response) Flush() {
+ if f, ok := r.ResponseWriter.(http.Flusher); ok {
+ f.Flush()
+ }
+}
+
+// WrittenStatus returned status code written
+func (r *Response) WrittenStatus() int {
+ return r.status
+}
+
+// Before allows for a function to be called before the ResponseWriter has been written to. This is
+// useful for setting headers or any other operations that must happen before a response has been written.
+func (r *Response) Before(f func(ResponseWriter)) {
+ r.befores = append(r.befores, f)
+}
+
+func WrapResponseWriter(resp http.ResponseWriter) *Response {
+ if v, ok := resp.(*Response); ok {
+ return v
+ }
+ return &Response{
+ ResponseWriter: resp,
+ status: 0,
+ befores: make([]func(ResponseWriter), 0),
+ }
+}
--- /dev/null
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package upload
+
+import (
+ "mime"
+ "net/http"
+ "net/url"
+ "path"
+ "regexp"
+ "strings"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/services/context"
+)
+
+// ErrFileTypeForbidden not allowed file type error
+type ErrFileTypeForbidden struct {
+ Type string
+}
+
+// IsErrFileTypeForbidden checks if an error is a ErrFileTypeForbidden.
+func IsErrFileTypeForbidden(err error) bool {
+ _, ok := err.(ErrFileTypeForbidden)
+ return ok
+}
+
+func (err ErrFileTypeForbidden) Error() string {
+ return "This file extension or type is not allowed to be uploaded."
+}
+
+var wildcardTypeRe = regexp.MustCompile(`^[a-z]+/\*$`)
+
+// Verify validates whether a file is allowed to be uploaded.
+func Verify(buf []byte, fileName, allowedTypesStr string) error {
+ allowedTypesStr = strings.ReplaceAll(allowedTypesStr, "|", ",") // compat for old config format
+
+ allowedTypes := []string{}
+ for _, entry := range strings.Split(allowedTypesStr, ",") {
+ entry = strings.ToLower(strings.TrimSpace(entry))
+ if entry != "" {
+ allowedTypes = append(allowedTypes, entry)
+ }
+ }
+
+ if len(allowedTypes) == 0 {
+ return nil // everything is allowed
+ }
+
+ fullMimeType := http.DetectContentType(buf)
+ mimeType, _, err := mime.ParseMediaType(fullMimeType)
+ if err != nil {
+ log.Warn("Detected attachment type could not be parsed %s", fullMimeType)
+ return ErrFileTypeForbidden{Type: fullMimeType}
+ }
+ extension := strings.ToLower(path.Ext(fileName))
+
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers
+ for _, allowEntry := range allowedTypes {
+ if allowEntry == "*/*" {
+ return nil // everything allowed
+ } else if strings.HasPrefix(allowEntry, ".") && allowEntry == extension {
+ return nil // extension is allowed
+ } else if mimeType == allowEntry {
+ return nil // mime type is allowed
+ } else if wildcardTypeRe.MatchString(allowEntry) && strings.HasPrefix(mimeType, allowEntry[:len(allowEntry)-1]) {
+ return nil // wildcard match, e.g. image/*
+ }
+ }
+
+ log.Info("Attachment with type %s blocked from upload", fullMimeType)
+ return ErrFileTypeForbidden{Type: fullMimeType}
+}
+
+// AddUploadContext renders template values for dropzone
+func AddUploadContext(ctx *context.Context, uploadType string) {
+ if uploadType == "release" {
+ ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
+ ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/releases/attachments/remove"
+ ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/releases/attachments"
+ ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Release.AllowedTypes, "|", ",")
+ ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
+ ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
+ } else if uploadType == "comment" {
+ ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
+ ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/issues/attachments/remove"
+ if len(ctx.Params(":index")) > 0 {
+ ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/" + url.PathEscape(ctx.Params(":index")) + "/attachments"
+ } else {
+ ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/issues/attachments"
+ }
+ ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Attachment.AllowedTypes, "|", ",")
+ ctx.Data["UploadMaxFiles"] = setting.Attachment.MaxFiles
+ ctx.Data["UploadMaxSize"] = setting.Attachment.MaxSize
+ } else if uploadType == "repo" {
+ ctx.Data["UploadUrl"] = ctx.Repo.RepoLink + "/upload-file"
+ ctx.Data["UploadRemoveUrl"] = ctx.Repo.RepoLink + "/upload-remove"
+ ctx.Data["UploadLinkUrl"] = ctx.Repo.RepoLink + "/upload-file"
+ ctx.Data["UploadAccepts"] = strings.ReplaceAll(setting.Repository.Upload.AllowedTypes, "|", ",")
+ ctx.Data["UploadMaxFiles"] = setting.Repository.Upload.MaxFiles
+ ctx.Data["UploadMaxSize"] = setting.Repository.Upload.FileMaxSize
+ }
+}
--- /dev/null
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package upload
+
+import (
+ "bytes"
+ "compress/gzip"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestUpload(t *testing.T) {
+ testContent := []byte(`This is a plain text file.`)
+ var b bytes.Buffer
+ w := gzip.NewWriter(&b)
+ w.Write(testContent)
+ w.Close()
+
+ kases := []struct {
+ data []byte
+ fileName string
+ allowedTypes string
+ err error
+ }{
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "dir/test.txt",
+ allowedTypes: "",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "../../../test.txt",
+ allowedTypes: "",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: ",",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "|",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "*/*",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "*/*,",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "*/*|",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "text/plain",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "dir/test.txt",
+ allowedTypes: "text/plain",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "/dir.txt/test.js",
+ allowedTypes: ".js",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: " text/plain ",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: ".txt",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: " .txt,.js",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: " .txt|.js",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "../../test.txt",
+ allowedTypes: " .txt|.js",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: " .txt ,.js ",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "text/plain, .txt",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "text/*",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "text/*,.js",
+ err: nil,
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "text/**",
+ err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: "application/x-gzip",
+ err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: ".zip",
+ err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: ".zip,.txtx",
+ err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
+ },
+ {
+ data: testContent,
+ fileName: "test.txt",
+ allowedTypes: ".zip|.txtx",
+ err: ErrFileTypeForbidden{"text/plain; charset=utf-8"},
+ },
+ {
+ data: b.Bytes(),
+ fileName: "test.txt",
+ allowedTypes: "application/x-gzip",
+ err: nil,
+ },
+ }
+
+ for _, kase := range kases {
+ assert.Equal(t, kase.err, Verify(kase.data, kase.fileName, kase.allowedTypes))
+ }
+}
"strings"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
)
// UserAssignmentWeb returns a middleware to handle context-user assignment for web routes
-func UserAssignmentWeb() func(ctx *context.Context) {
- return func(ctx *context.Context) {
+func UserAssignmentWeb() func(ctx *Context) {
+ return func(ctx *Context) {
errorFn := func(status int, title string, obj any) {
err, ok := obj.(error)
if !ok {
}
// UserIDAssignmentAPI returns a middleware to handle context-user assignment for api routes
-func UserIDAssignmentAPI() func(ctx *context.APIContext) {
- return func(ctx *context.APIContext) {
+func UserIDAssignmentAPI() func(ctx *APIContext) {
+ return func(ctx *APIContext) {
userID := ctx.ParamsInt64(":user-id")
if ctx.IsSigned && ctx.Doer.ID == userID {
}
// UserAssignmentAPI returns a middleware to handle context-user assignment for api routes
-func UserAssignmentAPI() func(ctx *context.APIContext) {
- return func(ctx *context.APIContext) {
+func UserAssignmentAPI() func(ctx *APIContext) {
+ return func(ctx *APIContext) {
ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, ctx.Error)
}
}
-func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, string, any)) (contextUser *user_model.User) {
+func userAssignment(ctx *Base, doer *user_model.User, errCb func(int, string, any)) (contextUser *user_model.User) {
username := ctx.Params(":username")
if doer != nil && doer.LowerName == strings.ToLower(username) {
if err != nil {
if user_model.IsErrUserNotExist(err) {
if redirectUserID, err := user_model.LookupUserRedirect(ctx, username); err == nil {
- context.RedirectToUser(ctx, username, redirectUserID)
+ RedirectToUser(ctx, username, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) {
errCb(http.StatusNotFound, "GetUserByName", err)
} else {
--- /dev/null
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+ "strings"
+ "time"
+)
+
+// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
+func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) {
+ before, err = parseFormTime(ctx, "before")
+ if err != nil {
+ return 0, 0, err
+ }
+
+ since, err = parseFormTime(ctx, "since")
+ if err != nil {
+ return 0, 0, err
+ }
+ return before, since, nil
+}
+
+// parseTime parse time and return unix timestamp
+func parseFormTime(ctx *Base, name string) (int64, error) {
+ value := strings.TrimSpace(ctx.FormString(name))
+ if len(value) != 0 {
+ t, err := time.Parse(time.RFC3339, value)
+ if err != nil {
+ return 0, err
+ }
+ if !t.IsZero() {
+ return t.Unix(), nil
+ }
+ }
+ return 0, nil
+}
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+// Copyright 2014 The Macaron Authors
+// Copyright 2020 The Gitea Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// SPDX-License-Identifier: Apache-2.0
+
+package context
+
+import (
+ "bytes"
+ "crypto/hmac"
+ "crypto/sha1"
+ "crypto/subtle"
+ "encoding/base64"
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// CsrfTokenTimeout represents the duration that XSRF tokens are valid.
+// It is exported so clients may set cookie timeouts that match generated tokens.
+const CsrfTokenTimeout = 24 * time.Hour
+
+// CsrfTokenRegenerationInterval is the interval between token generations, old tokens are still valid before CsrfTokenTimeout
+var CsrfTokenRegenerationInterval = 10 * time.Minute
+
+var csrfTokenSep = []byte(":")
+
+// GenerateCsrfToken returns a URL-safe secure XSRF token that expires in CsrfTokenTimeout hours.
+// key is a secret key for your application.
+// userID is a unique identifier for the user.
+// actionID is the action the user is taking (e.g. POSTing to a particular path).
+func GenerateCsrfToken(key, userID, actionID string, now time.Time) string {
+ nowUnixNano := now.UnixNano()
+ nowUnixNanoStr := strconv.FormatInt(nowUnixNano, 10)
+ h := hmac.New(sha1.New, []byte(key))
+ h.Write([]byte(strings.ReplaceAll(userID, ":", "_")))
+ h.Write(csrfTokenSep)
+ h.Write([]byte(strings.ReplaceAll(actionID, ":", "_")))
+ h.Write(csrfTokenSep)
+ h.Write([]byte(nowUnixNanoStr))
+ tok := fmt.Sprintf("%s:%s", h.Sum(nil), nowUnixNanoStr)
+ return base64.RawURLEncoding.EncodeToString([]byte(tok))
+}
+
+func ParseCsrfToken(token string) (issueTime time.Time, ok bool) {
+ data, err := base64.RawURLEncoding.DecodeString(token)
+ if err != nil {
+ return time.Time{}, false
+ }
+
+ pos := bytes.LastIndex(data, csrfTokenSep)
+ if pos == -1 {
+ return time.Time{}, false
+ }
+ nanos, err := strconv.ParseInt(string(data[pos+1:]), 10, 64)
+ if err != nil {
+ return time.Time{}, false
+ }
+ return time.Unix(0, nanos), true
+}
+
+// ValidCsrfToken returns true if token is a valid and unexpired token returned by Generate.
+func ValidCsrfToken(token, key, userID, actionID string, now time.Time) bool {
+ issueTime, ok := ParseCsrfToken(token)
+ if !ok {
+ return false
+ }
+
+ // Check that the token is not expired.
+ if now.Sub(issueTime) >= CsrfTokenTimeout {
+ return false
+ }
+
+ // Check that the token is not from the future.
+ // Allow 1-minute grace period in case the token is being verified on a
+ // machine whose clock is behind the machine that issued the token.
+ if issueTime.After(now.Add(1 * time.Minute)) {
+ return false
+ }
+
+ expected := GenerateCsrfToken(key, userID, actionID, issueTime)
+
+ // Check that the token matches the expected value.
+ // Use constant time comparison to avoid timing attacks.
+ return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1
+}
--- /dev/null
+// Copyright 2012 Google Inc. All Rights Reserved.
+// Copyright 2014 The Macaron Authors
+// Copyright 2020 The Gitea Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// SPDX-License-Identifier: Apache-2.0
+
+package context
+
+import (
+ "encoding/base64"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ key = "quay"
+ userID = "12345678"
+ actionID = "POST /form"
+)
+
+var (
+ now = time.Now()
+ oneMinuteFromNow = now.Add(1 * time.Minute)
+)
+
+func Test_ValidToken(t *testing.T) {
+ t.Run("Validate token", func(t *testing.T) {
+ tok := GenerateCsrfToken(key, userID, actionID, now)
+ assert.True(t, ValidCsrfToken(tok, key, userID, actionID, oneMinuteFromNow))
+ assert.True(t, ValidCsrfToken(tok, key, userID, actionID, now.Add(CsrfTokenTimeout-1*time.Nanosecond)))
+ assert.True(t, ValidCsrfToken(tok, key, userID, actionID, now.Add(-1*time.Minute)))
+ })
+}
+
+// Test_SeparatorReplacement tests that separators are being correctly substituted
+func Test_SeparatorReplacement(t *testing.T) {
+ t.Run("Test two separator replacements", func(t *testing.T) {
+ assert.NotEqual(t, GenerateCsrfToken("foo:bar", "baz", "wah", now),
+ GenerateCsrfToken("foo", "bar:baz", "wah", now))
+ })
+}
+
+func Test_InvalidToken(t *testing.T) {
+ t.Run("Test invalid tokens", func(t *testing.T) {
+ invalidTokenTests := []struct {
+ name, key, userID, actionID string
+ t time.Time
+ }{
+ {"Bad key", "foobar", userID, actionID, oneMinuteFromNow},
+ {"Bad userID", key, "foobar", actionID, oneMinuteFromNow},
+ {"Bad actionID", key, userID, "foobar", oneMinuteFromNow},
+ {"Expired", key, userID, actionID, now.Add(CsrfTokenTimeout)},
+ {"More than 1 minute from the future", key, userID, actionID, now.Add(-1*time.Nanosecond - 1*time.Minute)},
+ }
+
+ tok := GenerateCsrfToken(key, userID, actionID, now)
+ for _, itt := range invalidTokenTests {
+ assert.False(t, ValidCsrfToken(tok, itt.key, itt.userID, itt.actionID, itt.t))
+ }
+ })
+}
+
+// Test_ValidateBadData primarily tests that no unexpected panics are triggered during parsing
+func Test_ValidateBadData(t *testing.T) {
+ t.Run("Validate bad data", func(t *testing.T) {
+ badDataTests := []struct {
+ name, tok string
+ }{
+ {"Invalid Base64", "ASDab24(@)$*=="},
+ {"No delimiter", base64.URLEncoding.EncodeToString([]byte("foobar12345678"))},
+ {"Invalid time", base64.URLEncoding.EncodeToString([]byte("foobar:foobar"))},
+ }
+
+ for _, bdt := range badDataTests {
+ assert.False(t, ValidCsrfToken(bdt.tok, key, userID, actionID, oneMinuteFromNow))
+ }
+ })
+}
--- /dev/null
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+// Package contexttest provides utilities for testing Web/API contexts with models.
+package contexttest
+
+import (
+ gocontext "context"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "strings"
+ "testing"
+
+ access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/gitrepo"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
+
+ "github.com/go-chi/chi/v5"
+ "github.com/stretchr/testify/assert"
+)
+
+func mockRequest(t *testing.T, reqPath string) *http.Request {
+ method, path, found := strings.Cut(reqPath, " ")
+ if !found {
+ method = "GET"
+ path = reqPath
+ }
+ requestURL, err := url.Parse(path)
+ assert.NoError(t, err)
+ req := &http.Request{Method: method, URL: requestURL, Form: url.Values{}}
+ req = req.WithContext(middleware.WithContextData(req.Context()))
+ return req
+}
+
+type MockContextOption struct {
+ Render context.Render
+}
+
+// MockContext mock context for unit tests
+func MockContext(t *testing.T, reqPath string, opts ...MockContextOption) (*context.Context, *httptest.ResponseRecorder) {
+ var opt MockContextOption
+ if len(opts) > 0 {
+ opt = opts[0]
+ }
+ if opt.Render == nil {
+ opt.Render = &MockRender{}
+ }
+ resp := httptest.NewRecorder()
+ req := mockRequest(t, reqPath)
+ base, baseCleanUp := context.NewBaseContext(resp, req)
+ _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
+ base.Data = middleware.GetContextData(req.Context())
+ base.Locale = &translation.MockLocale{}
+
+ ctx := context.NewWebContext(base, opt.Render, nil)
+
+ chiCtx := chi.NewRouteContext()
+ ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+ return ctx, resp
+}
+
+// MockAPIContext mock context for unit tests
+func MockAPIContext(t *testing.T, reqPath string) (*context.APIContext, *httptest.ResponseRecorder) {
+ resp := httptest.NewRecorder()
+ req := mockRequest(t, reqPath)
+ base, baseCleanUp := context.NewBaseContext(resp, req)
+ base.Data = middleware.GetContextData(req.Context())
+ base.Locale = &translation.MockLocale{}
+ ctx := &context.APIContext{Base: base}
+ _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
+
+ chiCtx := chi.NewRouteContext()
+ ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
+ return ctx, resp
+}
+
+// LoadRepo load a repo into a test context.
+func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
+ var doer *user_model.User
+ repo := &context.Repository{}
+ switch ctx := ctx.(type) {
+ case *context.Context:
+ ctx.Repo = repo
+ doer = ctx.Doer
+ case *context.APIContext:
+ ctx.Repo = repo
+ doer = ctx.Doer
+ default:
+ assert.FailNow(t, "context is not *context.Context or *context.APIContext")
+ }
+
+ repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
+ var err error
+ repo.Owner, err = user_model.GetUserByID(ctx, repo.Repository.OwnerID)
+ assert.NoError(t, err)
+ repo.RepoLink = repo.Repository.Link()
+ repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo.Repository, doer)
+ assert.NoError(t, err)
+}
+
+// LoadRepoCommit loads a repo's commit into a test context.
+func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
+ var repo *context.Repository
+ switch ctx := ctx.(type) {
+ case *context.Context:
+ repo = ctx.Repo
+ case *context.APIContext:
+ repo = ctx.Repo
+ default:
+ assert.FailNow(t, "context is not *context.Context or *context.APIContext")
+ }
+
+ gitRepo, err := gitrepo.OpenRepository(ctx, repo.Repository)
+ assert.NoError(t, err)
+ defer gitRepo.Close()
+ branch, err := gitRepo.GetHEADBranch()
+ assert.NoError(t, err)
+ assert.NotNil(t, branch)
+ if branch != nil {
+ repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
+ assert.NoError(t, err)
+ }
+}
+
+// LoadUser load a user into a test context
+func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
+ switch ctx := ctx.(type) {
+ case *context.Context:
+ ctx.Doer = doer
+ case *context.APIContext:
+ ctx.Doer = doer
+ default:
+ assert.FailNow(t, "context is not *context.Context or *context.APIContext")
+ }
+}
+
+// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
+// already been populated.
+func LoadGitRepo(t *testing.T, ctx *context.Context) {
+ assert.NoError(t, ctx.Repo.Repository.LoadOwner(ctx))
+ var err error
+ ctx.Repo.GitRepo, err = gitrepo.OpenRepository(ctx, ctx.Repo.Repository)
+ assert.NoError(t, err)
+}
+
+type MockRender struct{}
+
+func (tr *MockRender) TemplateLookup(tmpl string, _ gocontext.Context) (templates.TemplateExecutor, error) {
+ return nil, nil
+}
+
+func (tr *MockRender) HTML(w io.Writer, status int, _ string, _ any, _ gocontext.Context) error {
+ if resp, ok := w.(http.ResponseWriter); ok {
+ resp.WriteHeader(status)
+ }
+ return nil
+}
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- ctx "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ ctx "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/gitdiff"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
project_model "code.gitea.io/gitea/models/project"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/webhook"
"gitea.com/go-chi/binding"
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
"strings"
auth_model "code.gitea.io/gitea/models/auth"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
import (
"net/http"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/web/middleware"
+ "code.gitea.io/gitea/services/context"
"gitea.com/go-chi/binding"
)
"math/big"
issues_model "code.gitea.io/gitea/models/issues"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/services/context"
)
type hiddenCommentTypeGroupsType map[string][]issues_model.CommentType
auth_model "code.gitea.io/gitea/models/auth"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
lfs_module "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
lfs_module "code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
+ "code.gitea.io/gitea/services/context"
"github.com/golang-jwt/jwt/v5"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
attachment_service "code.gitea.io/gitea/services/attachment"
+ "code.gitea.io/gitea/services/context/upload"
issue_service "code.gitea.io/gitea/services/issue"
incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
"code.gitea.io/gitea/services/mailer/token"
"context"
"code.gitea.io/gitea/models/user"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup"
+ gitea_context "code.gitea.io/gitea/services/context"
)
func ProcessorHelper() *markup.ProcessorHelper {
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/models/user"
- gitea_context "code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/contexttest"
+ gitea_context "code.gitea.io/gitea/services/context"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/container"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/sync"
"code.gitea.io/gitea/modules/util"
+ gitea_context "code.gitea.io/gitea/services/context"
issue_service "code.gitea.io/gitea/services/issue"
notify_service "code.gitea.io/gitea/services/notify"
)
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
+ "code.gitea.io/gitea/services/contexttest"
_ "code.gitea.io/gitea/models/actions"
"context"
"fmt"
- gitea_ctx "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/util"
+ gitea_ctx "code.gitea.io/gitea/services/context"
)
type ContainedLinks struct { // TODO: better name?
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/gitrepo"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/contexttest"
_ "code.gitea.io/gitea/models/actions"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/services/contexttest"
"code.gitea.io/gitea/services/gitdiff"
"github.com/stretchr/testify/assert"
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/contexttest"
"github.com/stretchr/testify/assert"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"github.com/stretchr/testify/assert"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"github.com/stretchr/testify/assert"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/context"
"github.com/stretchr/testify/assert"
)
"path"
"testing"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
+ gitea_context "code.gitea.io/gitea/services/context"
"github.com/stretchr/testify/assert"
)
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ gitea_context "code.gitea.io/gitea/services/context"
files_service "code.gitea.io/gitea/services/repository/files"
"code.gitea.io/gitea/tests"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/unittest"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers"
+ gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/PuerkitoBio/goquery"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
- gitea_context "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
+ gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror"
repo_service "code.gitea.io/gitea/services/repository"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/contexttest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/services/contexttest"
files_service "code.gitea.io/gitea/services/repository/files"
"github.com/stretchr/testify/assert"