mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-29 05:06:11 +01:00
Move macaron to chi (#14293)
Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
parent
3adbbb4255
commit
6433ba0ec3
|
@ -70,7 +70,7 @@ issues:
|
||||||
- path: modules/log/
|
- path: modules/log/
|
||||||
linters:
|
linters:
|
||||||
- errcheck
|
- errcheck
|
||||||
- path: routers/routes/macaron.go
|
- path: routers/routes/web.go
|
||||||
linters:
|
linters:
|
||||||
- dupl
|
- dupl
|
||||||
- path: routers/api/v1/repo/issue_subscription.go
|
- path: routers/api/v1/repo/issue_subscription.go
|
||||||
|
|
|
@ -21,7 +21,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"gitea.com/macaron/session"
|
"gitea.com/go-chi/session"
|
||||||
archiver "github.com/mholt/archiver/v3"
|
archiver "github.com/mholt/archiver/v3"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
|
@ -102,8 +102,7 @@ func runWeb(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c := routes.NewChi()
|
c := routes.InstallRoutes()
|
||||||
routes.RegisterInstallRoute(c)
|
|
||||||
err := listen(c, false)
|
err := listen(c, false)
|
||||||
select {
|
select {
|
||||||
case <-graceful.GetManager().IsShutdown():
|
case <-graceful.GetManager().IsShutdown():
|
||||||
|
@ -134,11 +133,9 @@ func runWeb(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set up Chi routes
|
|
||||||
c := routes.NewChi()
|
|
||||||
c.Mount("/", routes.NormalRoutes())
|
|
||||||
routes.DelegateToMacaron(c)
|
|
||||||
|
|
||||||
|
// Set up Chi routes
|
||||||
|
c := routes.NormalRoutes()
|
||||||
err := listen(c, true)
|
err := listen(c, true)
|
||||||
<-graceful.GetManager().Done()
|
<-graceful.GetManager().Done()
|
||||||
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
log.Info("PID: %d Gitea Web Finished", os.Getpid())
|
||||||
|
|
|
@ -116,9 +116,7 @@ func runPR() {
|
||||||
//routers.GlobalInit()
|
//routers.GlobalInit()
|
||||||
external.RegisterParsers()
|
external.RegisterParsers()
|
||||||
markup.Init()
|
markup.Init()
|
||||||
c := routes.NewChi()
|
c := routes.NormalRoutes()
|
||||||
c.Mount("/", routes.NormalRoutes())
|
|
||||||
routes.DelegateToMacaron(c)
|
|
||||||
|
|
||||||
log.Printf("[PR] Ready for testing !\n")
|
log.Printf("[PR] Ready for testing !\n")
|
||||||
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
log.Printf("[PR] Login with user1, user2, user3, ... with pass: password\n")
|
||||||
|
|
|
@ -549,7 +549,7 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
|
||||||
|
|
||||||
## Session (`session`)
|
## Session (`session`)
|
||||||
|
|
||||||
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, mysql, couchbase, memcache, nodb, postgres\].
|
- `PROVIDER`: **memory**: Session engine provider \[memory, file, redis, mysql, couchbase, memcache, postgres\].
|
||||||
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for others, the connection string.
|
- `PROVIDER_CONFIG`: **data/sessions**: For file, the root path; for others, the connection string.
|
||||||
- `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access.
|
- `COOKIE_SECURE`: **false**: Enable this to force using HTTPS for all session access.
|
||||||
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
|
- `COOKIE_NAME`: **i\_like\_gitea**: The name of the cookie used for the session ID.
|
||||||
|
@ -609,8 +609,6 @@ Default templates for project boards:
|
||||||
- `MODE`: **console**: Logging mode. For multiple modes, use a comma to separate values. You can configure each mode in per mode log subsections `\[log.modename\]`. By default the file mode will log to `$ROOT_PATH/gitea.log`.
|
- `MODE`: **console**: Logging mode. For multiple modes, use a comma to separate values. You can configure each mode in per mode log subsections `\[log.modename\]`. By default the file mode will log to `$ROOT_PATH/gitea.log`.
|
||||||
- `LEVEL`: **Info**: General log level. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\]
|
- `LEVEL`: **Info**: General log level. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\]
|
||||||
- `STACKTRACE_LEVEL`: **None**: Default log level at which to log create stack traces. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\]
|
- `STACKTRACE_LEVEL`: **None**: Default log level at which to log create stack traces. \[Trace, Debug, Info, Warn, Error, Critical, Fatal, None\]
|
||||||
- `REDIRECT_MACARON_LOG`: **false**: Redirects the Macaron log to its own logger or the default logger.
|
|
||||||
- `MACARON`: **file**: Logging mode for the macaron logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.macaron\]`. By default the file mode will log to `$ROOT_PATH/macaron.log`. (If you set this to `,` it will log to default gitea logger.)
|
|
||||||
- `ROUTER_LOG_LEVEL`: **Info**: The log level that the router should log at. (If you are setting the access log, its recommended to place this at Debug.)
|
- `ROUTER_LOG_LEVEL`: **Info**: The log level that the router should log at. (If you are setting the access log, its recommended to place this at Debug.)
|
||||||
- `ROUTER`: **console**: The mode or name of the log the router should log to. (If you set this to `,` it will log to default gitea logger.)
|
- `ROUTER`: **console**: The mode or name of the log the router should log to. (If you set this to `,` it will log to default gitea logger.)
|
||||||
NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` for this option to take effect. Configure each mode in per mode log subsections `\[log.modename.router\]`.
|
NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false` for this option to take effect. Configure each mode in per mode log subsections `\[log.modename.router\]`.
|
||||||
|
@ -618,7 +616,7 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
|
||||||
- `ACCESS`: **file**: Logging mode for the access logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.access\]`. By default the file mode will log to `$ROOT_PATH/access.log`. (If you set this to `,` it will log to the default gitea logger.)
|
- `ACCESS`: **file**: Logging mode for the access logger, use a comma to separate values. Configure each mode in per mode log subsections `\[log.modename.access\]`. By default the file mode will log to `$ROOT_PATH/access.log`. (If you set this to `,` it will log to the default gitea logger.)
|
||||||
- `ACCESS_LOG_TEMPLATE`: **`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**: Sets the template used to create the access log.
|
- `ACCESS_LOG_TEMPLATE`: **`{{.Ctx.RemoteAddr}} - {{.Identity}} {{.Start.Format "[02/Jan/2006:15:04:05 -0700]" }} "{{.Ctx.Req.Method}} {{.Ctx.Req.URL.RequestURI}} {{.Ctx.Req.Proto}}" {{.ResponseWriter.Status}} {{.ResponseWriter.Size}} "{{.Ctx.Req.Referer}}\" \"{{.Ctx.Req.UserAgent}}"`**: Sets the template used to create the access log.
|
||||||
- The following variables are available:
|
- The following variables are available:
|
||||||
- `Ctx`: the `macaron.Context` of the request.
|
- `Ctx`: the `context.Context` of the request.
|
||||||
- `Identity`: the SignedUserName or `"-"` if not logged in.
|
- `Identity`: the SignedUserName or `"-"` if not logged in.
|
||||||
- `Start`: the start time of the request.
|
- `Start`: the start time of the request.
|
||||||
- `ResponseWriter`: the responseWriter from the request.
|
- `ResponseWriter`: the responseWriter from the request.
|
||||||
|
|
|
@ -67,40 +67,11 @@ The provider type of the sublogger can be set using the `MODE` value in
|
||||||
its subsection, but will default to the name. This allows you to have
|
its subsection, but will default to the name. This allows you to have
|
||||||
multiple subloggers that will log to files.
|
multiple subloggers that will log to files.
|
||||||
|
|
||||||
### The "Macaron" logger
|
|
||||||
|
|
||||||
By default Macaron will log to its own go `log` instance. This writes
|
|
||||||
to `os.Stdout`. You can redirect this log to a Gitea configurable logger
|
|
||||||
through setting the `REDIRECT_MACARON_LOG` setting in the `[log]`
|
|
||||||
section which you can configure the outputs of by setting the `MACARON`
|
|
||||||
value in the `[log]` section of the configuration. `MACARON` defaults
|
|
||||||
to `file` if unset.
|
|
||||||
|
|
||||||
Please note, the macaron logger will log at `INFO` level, setting the
|
|
||||||
`LEVEL` of this logger to `WARN` or above will result in no macaron logs.
|
|
||||||
|
|
||||||
Each output sublogger for this logger is configured in
|
|
||||||
`[log.sublogger.macaron]` sections. There are certain default values
|
|
||||||
which will not be inherited from the `[log]` or relevant
|
|
||||||
`[log.sublogger]` sections:
|
|
||||||
|
|
||||||
- `FLAGS` is `stdflags` (Equal to
|
|
||||||
`date,time,medfile,shortfuncname,levelinitial`)
|
|
||||||
- `FILE_NAME` will default to `%(ROOT_PATH)/macaron.log`
|
|
||||||
- `EXPRESSION` will default to `""`
|
|
||||||
- `PREFIX` will default to `""`
|
|
||||||
|
|
||||||
NB: You can redirect the macaron logger to send its events to the gitea
|
|
||||||
log using the value: `MACARON = ,`
|
|
||||||
|
|
||||||
### The "Router" logger
|
### The "Router" logger
|
||||||
|
|
||||||
There are two types of Router log. By default Macaron send its own
|
You can disable Router log by setting `DISABLE_ROUTER_LOG`.
|
||||||
router log which will be directed to Macaron's go `log`, however if you
|
|
||||||
`REDIRECT_MACARON_LOG` you will enable Gitea's router log. You can
|
|
||||||
disable both types of Router log by setting `DISABLE_ROUTER_LOG`.
|
|
||||||
|
|
||||||
If you enable the redirect, you can configure the outputs of this
|
You can configure the outputs of this
|
||||||
router log by setting the `ROUTER` value in the `[log]` section of the
|
router log by setting the `ROUTER` value in the `[log]` section of the
|
||||||
configuration. `ROUTER` will default to `console` if unset. The Gitea
|
configuration. `ROUTER` will default to `console` if unset. The Gitea
|
||||||
Router logs the same data as the Macaron log but has slightly different
|
Router logs the same data as the Macaron log but has slightly different
|
||||||
|
@ -162,11 +133,11 @@ This value represent a go template. It's default value is:
|
||||||
|
|
||||||
The template is passed following options:
|
The template is passed following options:
|
||||||
|
|
||||||
- `Ctx` is the `macaron.Context`
|
- `Ctx` is the `context.Context`
|
||||||
- `Identity` is the `SignedUserName` or `"-"` if the user is not logged
|
- `Identity` is the `SignedUserName` or `"-"` if the user is not logged
|
||||||
in
|
in
|
||||||
- `Start` is the start time of the request
|
- `Start` is the start time of the request
|
||||||
- `ResponseWriter` is the `macaron.ResponseWriter`
|
- `ResponseWriter` is the `http.ResponseWriter`
|
||||||
|
|
||||||
Caution must be taken when changing this template as it runs outside of
|
Caution must be taken when changing this template as it runs outside of
|
||||||
the standard panic recovery trap. The template should also be as simple
|
the standard panic recovery trap. The template should also be as simple
|
||||||
|
|
|
@ -267,7 +267,7 @@ Windows, on architectures like amd64, i386, ARM, PowerPC, and others.
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
* Web framework: [Macaron](http://go-macaron.com/)
|
* Web framework: [Chi](http://github.com/go-chi/chi)
|
||||||
* ORM: [XORM](https://xorm.io)
|
* ORM: [XORM](https://xorm.io)
|
||||||
* UI components:
|
* UI components:
|
||||||
* [Semantic UI](http://semantic-ui.com/)
|
* [Semantic UI](http://semantic-ui.com/)
|
||||||
|
|
|
@ -254,7 +254,7 @@ Le but de ce projet est de fournir de la manière la plus simple, la plus rapide
|
||||||
|
|
||||||
## Composants
|
## Composants
|
||||||
|
|
||||||
* Framework web : [Macaron](http://go-macaron.com/)
|
* Framework web : [Chi](http://github.com/go-chi/chi)
|
||||||
* ORM: [XORM](https://xorm.io)
|
* ORM: [XORM](https://xorm.io)
|
||||||
* Interface graphique :
|
* Interface graphique :
|
||||||
* [Semantic UI](http://semantic-ui.com/)
|
* [Semantic UI](http://semantic-ui.com/)
|
||||||
|
|
|
@ -47,7 +47,7 @@ Gitea的首要目标是创建一个极易安装,运行非常快速,安装和
|
||||||
|
|
||||||
## 组件
|
## 组件
|
||||||
|
|
||||||
* Web框架: [Macaron](http://go-macaron.com/)
|
* Web框架: [Chi](http://github.com/go-chi/chi)
|
||||||
* ORM: [XORM](https://xorm.io)
|
* ORM: [XORM](https://xorm.io)
|
||||||
* UI组件:
|
* UI组件:
|
||||||
* [Semantic UI](http://semantic-ui.com/)
|
* [Semantic UI](http://semantic-ui.com/)
|
||||||
|
|
|
@ -47,7 +47,7 @@ Gitea 的首要目標是建立一個容易安裝,運行快速,安装和使
|
||||||
|
|
||||||
## 元件
|
## 元件
|
||||||
|
|
||||||
* Web 框架: [Macaron](http://go-macaron.com/)
|
* Web 框架: [Chi](http://github.com/go-chi/chi)
|
||||||
* ORM: [XORM](https://xorm.io)
|
* ORM: [XORM](https://xorm.io)
|
||||||
* UI 元件:
|
* UI 元件:
|
||||||
* [Semantic UI](http://semantic-ui.com/)
|
* [Semantic UI](http://semantic-ui.com/)
|
||||||
|
|
16
go.mod
16
go.mod
|
@ -5,19 +5,12 @@ go 1.14
|
||||||
require (
|
require (
|
||||||
code.gitea.io/gitea-vet v0.2.1
|
code.gitea.io/gitea-vet v0.2.1
|
||||||
code.gitea.io/sdk/gitea v0.13.1
|
code.gitea.io/sdk/gitea v0.13.1
|
||||||
|
gitea.com/go-chi/binding v0.0.0-20210113025129-03f1d313373c
|
||||||
|
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e
|
||||||
|
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e
|
||||||
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee
|
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee
|
||||||
gitea.com/lunny/levelqueue v0.3.0
|
gitea.com/lunny/levelqueue v0.3.0
|
||||||
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b
|
|
||||||
gitea.com/macaron/captcha v0.0.0-20200825161008-e8597820aaca
|
|
||||||
gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4
|
|
||||||
gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439
|
|
||||||
gitea.com/macaron/gzip v0.0.0-20200827120000-efa5e8477cf5
|
|
||||||
gitea.com/macaron/i18n v0.0.0-20200911004404-4ca3dd0cbd60
|
|
||||||
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a
|
|
||||||
gitea.com/macaron/macaron v1.5.1-0.20201027213641-0db5d4584804
|
|
||||||
gitea.com/macaron/session v0.0.0-20201103015045-a177a2701dee
|
|
||||||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7
|
|
||||||
github.com/PuerkitoBio/goquery v1.5.1
|
github.com/PuerkitoBio/goquery v1.5.1
|
||||||
github.com/RoaringBitmap/roaring v0.5.5 // indirect
|
github.com/RoaringBitmap/roaring v0.5.5 // indirect
|
||||||
github.com/alecthomas/chroma v0.8.2
|
github.com/alecthomas/chroma v0.8.2
|
||||||
|
@ -36,6 +29,7 @@ require (
|
||||||
github.com/gliderlabs/ssh v0.3.1
|
github.com/gliderlabs/ssh v0.3.1
|
||||||
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
|
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
|
||||||
github.com/go-chi/chi v1.5.1
|
github.com/go-chi/chi v1.5.1
|
||||||
|
github.com/go-chi/cors v1.1.1
|
||||||
github.com/go-enry/go-enry/v2 v2.6.0
|
github.com/go-enry/go-enry/v2 v2.6.0
|
||||||
github.com/go-git/go-billy/v5 v5.0.0
|
github.com/go-git/go-billy/v5 v5.0.0
|
||||||
github.com/go-git/go-git/v5 v5.2.0
|
github.com/go-git/go-git/v5 v5.2.0
|
||||||
|
|
60
go.sum
60
go.sum
|
@ -40,44 +40,16 @@ code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFj
|
||||||
code.gitea.io/sdk/gitea v0.13.1 h1:Y7bpH2iO6Q0KhhMJfjP/LZ0AmiYITeRQlCD8b0oYqhk=
|
code.gitea.io/sdk/gitea v0.13.1 h1:Y7bpH2iO6Q0KhhMJfjP/LZ0AmiYITeRQlCD8b0oYqhk=
|
||||||
code.gitea.io/sdk/gitea v0.13.1/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
|
code.gitea.io/sdk/gitea v0.13.1/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
gitea.com/go-chi/binding v0.0.0-20210113025129-03f1d313373c h1:NTtrGYjR40WUdkCdn26Y5LGFT52rIkFPkjmtgCAyiTs=
|
||||||
|
gitea.com/go-chi/binding v0.0.0-20210113025129-03f1d313373c/go.mod h1:9bGA9dIsrz+wVQKH1DzvxuAvrudHaQ8Wx8hLme/GVGQ=
|
||||||
|
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e h1:zgPGaf3kXP0cVm9J0l8ZA2+XDzILYATg0CXbihR6N+o=
|
||||||
|
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||||
|
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e h1:YjaQU6XFicdhPN+MlGolcXO8seYY2+EY5g7vZPB17CQ=
|
||||||
|
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e/go.mod h1:nfA7JaGv3hbGQ1ktdhAsZhdS84qKffI8NMlHr+Opsog=
|
||||||
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee h1:9U6HuKUBt/cGK6T/64dEuz0r7Yp97WAAEJvXHDlY3ws=
|
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee h1:9U6HuKUBt/cGK6T/64dEuz0r7Yp97WAAEJvXHDlY3ws=
|
||||||
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0=
|
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0=
|
||||||
gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I=
|
gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I=
|
||||||
gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
gitea.com/lunny/levelqueue v0.3.0/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
||||||
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e h1:r1en/D7xJmcY24VkHkjkcJFa+7ZWubVWPBrvsHkmHxk=
|
|
||||||
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0=
|
|
||||||
gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727 h1:ZF2Bd6rqVlwhIDhYiS0uGYcT+GaVNGjuKVJkTNqWMIs=
|
|
||||||
gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY=
|
|
||||||
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b h1:vXt85uYV17KURaUlhU7v4GbCShkqRZDSfo0TkC0YCjQ=
|
|
||||||
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b/go.mod h1:Cxadig6POWpPYYSfg23E7jo35Yf0yvsdC1lifoKWmPo=
|
|
||||||
gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76 h1:mMsMEg90c5KXQgRWsH8D6GHXfZIW1RAe5S9VYIb12lM=
|
|
||||||
gitea.com/macaron/cache v0.0.0-20190822004001-a6e7fee4ee76/go.mod h1:NFHb9Of+LUnU86bU20CiXXg6ZlgCJ4XytP14UsHOXFs=
|
|
||||||
gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b h1:2ZE0JE3bKVBcP1VTrWeE1jqWwCAMIzfOQm1U9EGbBKU=
|
|
||||||
gitea.com/macaron/cache v0.0.0-20200924044943-905232fba10b/go.mod h1:W5hKG8T1GBfypp5CRQlgoJU4figIL0jhx02y4XA/NOA=
|
|
||||||
gitea.com/macaron/captcha v0.0.0-20200825161008-e8597820aaca h1:f5P41nXmXd/YOh8f6098Q0F1Y0QfpyRPSSIkni2XH4Q=
|
|
||||||
gitea.com/macaron/captcha v0.0.0-20200825161008-e8597820aaca/go.mod h1:J5h3N+1nKTXtU1x4GxexaQKgAz8UiWecNwi/CfX7CtQ=
|
|
||||||
gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4 h1:e2rAFDejB0qN8OrY4xP4XSu8/yT6QmWxDZpB3J7r2GU=
|
|
||||||
gitea.com/macaron/cors v0.0.0-20190826180238-95aec09ea8b4/go.mod h1:rtOK4J20kpMD9XcNsnO5YA843YSTe/MUMbDj/TJ/Q7A=
|
|
||||||
gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439 h1:88c34YM29a1GlWLrLBaG/GTT2htDdJz1u3n9+lmPolg=
|
|
||||||
gitea.com/macaron/csrf v0.0.0-20190822024205-3dc5a4474439/go.mod h1:IsQPHx73HnnqFBYiVHjg87q4XBZyGXXu77xANukvZuk=
|
|
||||||
gitea.com/macaron/gzip v0.0.0-20200827120000-efa5e8477cf5 h1:6rbhThlqfOb+sSmhrsVFz3bZoAeoloe7TZqyeiPbbWI=
|
|
||||||
gitea.com/macaron/gzip v0.0.0-20200827120000-efa5e8477cf5/go.mod h1:z8vCjuhqDfvzPUJDowGqbsgoeYBvDbl95S5k6y43Pxo=
|
|
||||||
gitea.com/macaron/i18n v0.0.0-20200911004404-4ca3dd0cbd60 h1:tNWNe5HBIlsfapFMtT4twTbXQmInRQWmdWNi8Di1ct0=
|
|
||||||
gitea.com/macaron/i18n v0.0.0-20200911004404-4ca3dd0cbd60/go.mod h1:g5ope1b+iWhBdHzAn6EJ9u9Gp3FRESxpG+CDf7HYc/A=
|
|
||||||
gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM=
|
|
||||||
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok=
|
|
||||||
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM=
|
|
||||||
gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827/go.mod h1:/rvxMjIkOq4BM8uPUb+VHuU02ZfAO6R4+wD//tiCiRw=
|
|
||||||
gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs=
|
|
||||||
gitea.com/macaron/macaron v1.5.0 h1:TvWEcHw1/zaHlo0GTuKEukLh3A99+QsU2mjBrXLXjVQ=
|
|
||||||
gitea.com/macaron/macaron v1.5.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY=
|
|
||||||
gitea.com/macaron/macaron v1.5.1-0.20201027213641-0db5d4584804 h1:yUiJVZKzdXsBe2tumTAXHBZa1qPGoGXM3fBG4RJ5fQg=
|
|
||||||
gitea.com/macaron/macaron v1.5.1-0.20201027213641-0db5d4584804/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY=
|
|
||||||
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA=
|
|
||||||
gitea.com/macaron/session v0.0.0-20201103015045-a177a2701dee h1:8/N3a56RXRJ66nnep0z+T7oHCB0bY6lpvtjv9Y9FPhE=
|
|
||||||
gitea.com/macaron/session v0.0.0-20201103015045-a177a2701dee/go.mod h1:5tJCkDbrwpGv+MQUSIZSOW0wFrkh0exsonJgOvBs1Dw=
|
|
||||||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 h1:N9QFoeNsUXLhl14mefLzGluqV7w2mGU3u+iZU+jCeWk=
|
|
||||||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks=
|
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
|
||||||
github.com/6543-forks/go-gogs-client v0.0.0-20210116182316-f2f8bc0ea9cc h1:FLylYVXDwK+YtrmXYnv2Q1Y5lQ9TU1Xp5F2vndIOTb4=
|
github.com/6543-forks/go-gogs-client v0.0.0-20210116182316-f2f8bc0ea9cc h1:FLylYVXDwK+YtrmXYnv2Q1Y5lQ9TU1Xp5F2vndIOTb4=
|
||||||
|
@ -92,6 +64,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||||
|
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||||
|
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
|
@ -106,7 +80,6 @@ github.com/RoaringBitmap/roaring v0.5.5 h1:naNqvO1mNnghk2UvcsqnzHDBn9DRbCIRy94Gm
|
||||||
github.com/RoaringBitmap/roaring v0.5.5/go.mod h1:puNo5VdzwbaIQxSiDIwfXl4Hnc+fbovcX4IW/dSTtUk=
|
github.com/RoaringBitmap/roaring v0.5.5/go.mod h1:puNo5VdzwbaIQxSiDIwfXl4Hnc+fbovcX4IW/dSTtUk=
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68=
|
|
||||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
|
@ -233,19 +206,13 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
||||||
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
|
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
|
||||||
github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 h1:uNLXQ6QO1TocD8BaN/KkRki0Xw0brCM1PKl/ZA5pgfs=
|
github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89 h1:uNLXQ6QO1TocD8BaN/KkRki0Xw0brCM1PKl/ZA5pgfs=
|
||||||
github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
|
github.com/couchbase/go-couchbase v0.0.0-20201026062457-7b3be89bbd89/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
|
||||||
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
|
|
||||||
github.com/couchbase/gomemcached v0.1.0 h1:whUde87n8CScx8ckMp2En5liqAlcuG3aKy/BQeBPu84=
|
|
||||||
github.com/couchbase/gomemcached v0.1.0/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
|
|
||||||
github.com/couchbase/gomemcached v0.1.1 h1:xCS8ZglJDhrlQg3jmK7Rn1V8f7bPjXABLC05CgLQauc=
|
github.com/couchbase/gomemcached v0.1.1 h1:xCS8ZglJDhrlQg3jmK7Rn1V8f7bPjXABLC05CgLQauc=
|
||||||
github.com/couchbase/gomemcached v0.1.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
|
github.com/couchbase/gomemcached v0.1.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
|
||||||
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
|
||||||
github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 h1:NCqJ6fwen6YP0WlV/IyibaT0kPt3JEI1rA62V/UPKT4=
|
github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67 h1:NCqJ6fwen6YP0WlV/IyibaT0kPt3JEI1rA62V/UPKT4=
|
||||||
github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
github.com/couchbase/goutils v0.0.0-20201030094643-5e82bb967e67/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
||||||
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
|
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
|
||||||
github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw=
|
github.com/couchbase/vellum v1.0.2 h1:BrbP0NKiyDdndMPec8Jjhy0U47CZ0Lgx3xUC2r9rZqw=
|
||||||
github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4=
|
github.com/couchbase/vellum v1.0.2/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4=
|
||||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 h1:1XjEY/gnjQ+AfXef2U6dxCquhiRzkEpxZuWqs+QxTL8=
|
|
||||||
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
@ -331,6 +298,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||||
github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w=
|
github.com/go-chi/chi v1.5.1 h1:kfTK3Cxd/dkMu/rKs5ZceWYp+t5CtiE7vmaTv3LjC6w=
|
||||||
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||||
|
github.com/go-chi/cors v1.1.1 h1:eHuqxsIw89iXcWnWUN8R72JMibABJTN/4IOYI5WERvw=
|
||||||
|
github.com/go-chi/cors v1.1.1/go.mod h1:K2Yje0VW/SJzxiyMYu6iPQYa7hMjQX2i/F491VChg1I=
|
||||||
github.com/go-enry/go-enry/v2 v2.6.0 h1:nbGWQBpO+D+cJuRxNgSDFnFY9QWz3QM/CeZxU7VAH20=
|
github.com/go-enry/go-enry/v2 v2.6.0 h1:nbGWQBpO+D+cJuRxNgSDFnFY9QWz3QM/CeZxU7VAH20=
|
||||||
github.com/go-enry/go-enry/v2 v2.6.0/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ=
|
github.com/go-enry/go-enry/v2 v2.6.0/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ=
|
||||||
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
|
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
|
||||||
|
@ -548,7 +517,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
|
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||||
|
@ -700,7 +668,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
|
||||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc=
|
github.com/klauspost/compress v1.11.3 h1:dB4Bn0tN3wdCzQxnS8r06kV74qN/TAfaIS0bVE8h3jc=
|
||||||
|
@ -995,7 +962,6 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
|
||||||
github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
|
github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
|
||||||
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
|
||||||
|
@ -1068,7 +1034,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
|
||||||
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||||
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
|
||||||
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
|
||||||
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
|
|
||||||
github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c h1:679/gJXwrsHC3RATr0YYjZvDMJPYN7W9FGSGNoLmKxM=
|
github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c h1:679/gJXwrsHC3RATr0YYjZvDMJPYN7W9FGSGNoLmKxM=
|
||||||
github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
|
github.com/unknwon/i18n v0.0.0-20200823051745-09abd91c7f2c/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
|
||||||
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs=
|
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs=
|
||||||
|
@ -1171,9 +1136,6 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
|
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620 h1:3wPMTskHO3+O6jqTEXyFcsnuxMQOqYSaHsDxcbUXpqA=
|
||||||
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201217014255-9d1352758620/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -1535,12 +1497,10 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||||
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
|
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
|
||||||
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/ini.v1 v1.60.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
auth "code.gitea.io/gitea/modules/forms"
|
||||||
"code.gitea.io/gitea/modules/queue"
|
"code.gitea.io/gitea/modules/queue"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
auth "code.gitea.io/gitea/modules/forms"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
|
|
|
@ -131,7 +131,7 @@ func TestAPIGetReleaseByTag(t *testing.T) {
|
||||||
|
|
||||||
tag := "v1.1"
|
tag := "v1.1"
|
||||||
|
|
||||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s/",
|
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s",
|
||||||
owner.Name, repo.Name, tag)
|
owner.Name, repo.Name, tag)
|
||||||
|
|
||||||
req := NewRequestf(t, "GET", urlStr)
|
req := NewRequestf(t, "GET", urlStr)
|
||||||
|
@ -144,7 +144,7 @@ func TestAPIGetReleaseByTag(t *testing.T) {
|
||||||
|
|
||||||
nonexistingtag := "nonexistingtag"
|
nonexistingtag := "nonexistingtag"
|
||||||
|
|
||||||
urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s/",
|
urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s",
|
||||||
owner.Name, repo.Name, nonexistingtag)
|
owner.Name, repo.Name, nonexistingtag)
|
||||||
|
|
||||||
req = NewRequestf(t, "GET", urlStr)
|
req = NewRequestf(t, "GET", urlStr)
|
||||||
|
@ -163,7 +163,7 @@ func TestAPIDeleteTagByName(t *testing.T) {
|
||||||
session := loginUser(t, owner.LowerName)
|
session := loginUser(t, owner.LowerName)
|
||||||
token := getTokenForLoggedInUser(t, session)
|
token := getTokenForLoggedInUser(t, session)
|
||||||
|
|
||||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/delete-tag/?token=%s",
|
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/delete-tag?token=%s",
|
||||||
owner.Name, repo.Name, token)
|
owner.Name, repo.Name, token)
|
||||||
|
|
||||||
req := NewRequestf(t, http.MethodDelete, urlStr)
|
req := NewRequestf(t, http.MethodDelete, urlStr)
|
||||||
|
@ -171,7 +171,7 @@ func TestAPIDeleteTagByName(t *testing.T) {
|
||||||
|
|
||||||
// Make sure that actual releases can't be deleted outright
|
// Make sure that actual releases can't be deleted outright
|
||||||
createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test")
|
createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test")
|
||||||
urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag/?token=%s",
|
urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag?token=%s",
|
||||||
owner.Name, repo.Name, token)
|
owner.Name, repo.Name, token)
|
||||||
|
|
||||||
req = NewRequestf(t, http.MethodDelete, urlStr)
|
req = NewRequestf(t, http.MethodDelete, urlStr)
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/routers/routes"
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
"gitea.com/macaron/session"
|
"gitea.com/go-chi/session"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,9 +58,7 @@ func TestSessionFileCreation(t *testing.T) {
|
||||||
oldSessionConfig := setting.SessionConfig.ProviderConfig
|
oldSessionConfig := setting.SessionConfig.ProviderConfig
|
||||||
defer func() {
|
defer func() {
|
||||||
setting.SessionConfig.ProviderConfig = oldSessionConfig
|
setting.SessionConfig.ProviderConfig = oldSessionConfig
|
||||||
c = routes.NewChi()
|
c = routes.NormalRoutes()
|
||||||
c.Mount("/", routes.NormalRoutes())
|
|
||||||
routes.DelegateToMacaron(c)
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var config session.Options
|
var config session.Options
|
||||||
|
@ -84,9 +82,7 @@ func TestSessionFileCreation(t *testing.T) {
|
||||||
|
|
||||||
setting.SessionConfig.ProviderConfig = string(newConfigBytes)
|
setting.SessionConfig.ProviderConfig = string(newConfigBytes)
|
||||||
|
|
||||||
c = routes.NewChi()
|
c = routes.NormalRoutes()
|
||||||
c.Mount("/", routes.NormalRoutes())
|
|
||||||
routes.DelegateToMacaron(c)
|
|
||||||
|
|
||||||
t.Run("NoSessionOnViewIssue", func(t *testing.T) {
|
t.Run("NoSessionOnViewIssue", func(t *testing.T) {
|
||||||
defer PrintCurrentTest(t)()
|
defer PrintCurrentTest(t)()
|
||||||
|
|
|
@ -31,15 +31,15 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
"code.gitea.io/gitea/routers/routes"
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
"github.com/go-chi/chi"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var c chi.Router
|
var c *web.Route
|
||||||
|
|
||||||
type NilResponseRecorder struct {
|
type NilResponseRecorder struct {
|
||||||
httptest.ResponseRecorder
|
httptest.ResponseRecorder
|
||||||
|
@ -66,9 +66,7 @@ func TestMain(m *testing.M) {
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
initIntegrationTest()
|
initIntegrationTest()
|
||||||
c = routes.NewChi()
|
c = routes.NormalRoutes()
|
||||||
c.Mount("/", routes.NormalRoutes())
|
|
||||||
routes.DelegateToMacaron(c)
|
|
||||||
|
|
||||||
// integration test settings...
|
// integration test settings...
|
||||||
if setting.Cfg != nil {
|
if setting.Cfg != nil {
|
||||||
|
@ -387,6 +385,9 @@ func NewRequestWithJSON(t testing.TB, method, urlStr string, v interface{}) *htt
|
||||||
|
|
||||||
func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *http.Request {
|
func NewRequestWithBody(t testing.TB, method, urlStr string, body io.Reader) *http.Request {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
if !strings.HasPrefix(urlStr, "http") && !strings.HasPrefix(urlStr, "/") {
|
||||||
|
urlStr = "/" + urlStr
|
||||||
|
}
|
||||||
request, err := http.NewRequest(method, urlStr, body)
|
request, err := http.NewRequest(method, urlStr, body)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
request.RequestURI = urlStr
|
request.RequestURI = urlStr
|
||||||
|
|
|
@ -19,8 +19,8 @@ import (
|
||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
|
"code.gitea.io/gitea/routers/routes"
|
||||||
|
|
||||||
"gitea.com/macaron/gzip"
|
|
||||||
gzipp "github.com/klauspost/compress/gzip"
|
gzipp "github.com/klauspost/compress/gzip"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -121,7 +121,7 @@ func TestGetLFSLarge(t *testing.T) {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
content := make([]byte, gzip.MinSize*10)
|
content := make([]byte, routes.GzipMinSize*10)
|
||||||
for i := range content {
|
for i := range content {
|
||||||
content[i] = byte(i % 256)
|
content[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ func TestGetLFSGzip(t *testing.T) {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b := make([]byte, gzip.MinSize*10)
|
b := make([]byte, routes.GzipMinSize*10)
|
||||||
for i := range b {
|
for i := range b {
|
||||||
b[i] = byte(i % 256)
|
b[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ func TestGetLFSZip(t *testing.T) {
|
||||||
t.Skip()
|
t.Skip()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b := make([]byte, gzip.MinSize*10)
|
b := make([]byte, routes.GzipMinSize*10)
|
||||||
for i := range b {
|
for i := range b {
|
||||||
b[i] = byte(i % 256)
|
b[i] = byte(i % 256)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ func TestLinksNoLogin(t *testing.T) {
|
||||||
"/user/login",
|
"/user/login",
|
||||||
"/user/forgot_password",
|
"/user/forgot_password",
|
||||||
"/api/swagger",
|
"/api/swagger",
|
||||||
"/api/v1/swagger",
|
|
||||||
"/user2/repo1",
|
"/user2/repo1",
|
||||||
"/user2/repo1/projects",
|
"/user2/repo1/projects",
|
||||||
"/user2/repo1/projects/1",
|
"/user2/repo1/projects/1",
|
||||||
|
@ -53,6 +52,7 @@ func TestRedirectsNoLogin(t *testing.T) {
|
||||||
"/user2/repo1/src/master/file.txt": "/user2/repo1/src/branch/master/file.txt",
|
"/user2/repo1/src/master/file.txt": "/user2/repo1/src/branch/master/file.txt",
|
||||||
"/user2/repo1/src/master/directory/file.txt": "/user2/repo1/src/branch/master/directory/file.txt",
|
"/user2/repo1/src/master/directory/file.txt": "/user2/repo1/src/branch/master/directory/file.txt",
|
||||||
"/user/avatar/Ghost/-1": "/img/avatar_default.png",
|
"/user/avatar/Ghost/-1": "/img/avatar_default.png",
|
||||||
|
"/api/v1/swagger": "/api/swagger",
|
||||||
}
|
}
|
||||||
for link, redirectLink := range redirects {
|
for link, redirectLink := range redirects {
|
||||||
req := NewRequest(t, "GET", link)
|
req := NewRequest(t, "GET", link)
|
||||||
|
@ -86,7 +86,6 @@ func testLinksAsUser(userName string, t *testing.T) {
|
||||||
"/",
|
"/",
|
||||||
"/user/forgot_password",
|
"/user/forgot_password",
|
||||||
"/api/swagger",
|
"/api/swagger",
|
||||||
"/api/v1/swagger",
|
|
||||||
"/issues",
|
"/issues",
|
||||||
"/issues?type=your_repositories&repos=[0]&sort=&state=open",
|
"/issues?type=your_repositories&repos=[0]&sort=&state=open",
|
||||||
"/issues?type=assigned&repos=[0]&sort=&state=open",
|
"/issues?type=assigned&repos=[0]&sort=&state=open",
|
||||||
|
|
|
@ -8,19 +8,15 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
|
"code.gitea.io/gitea/modules/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DataStore represents a data store
|
// DataStore represents a data store
|
||||||
type DataStore interface {
|
type DataStore middlewares.DataStore
|
||||||
GetData() map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SessionStore represents a session store
|
// SessionStore represents a session store
|
||||||
type SessionStore interface {
|
type SessionStore session.Store
|
||||||
Get(interface{}) interface{}
|
|
||||||
Set(interface{}, interface{}) error
|
|
||||||
Delete(interface{}) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// SingleSignOn represents a SSO authentication method (plugin) for HTTP requests.
|
// SingleSignOn represents a SSO authentication method (plugin) for HTTP requests.
|
||||||
type SingleSignOn interface {
|
type SingleSignOn interface {
|
||||||
|
|
|
@ -62,6 +62,8 @@ func (o *OAuth2) Free() error {
|
||||||
|
|
||||||
// userIDFromToken returns the user id corresponding to the OAuth token.
|
// userIDFromToken returns the user id corresponding to the OAuth token.
|
||||||
func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
|
func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
|
||||||
|
_ = req.ParseForm()
|
||||||
|
|
||||||
// Check access token.
|
// Check access token.
|
||||||
tokenSHA := req.Form.Get("token")
|
tokenSHA := req.Form.Get("token")
|
||||||
if len(tokenSHA) == 0 {
|
if len(tokenSHA) == 0 {
|
||||||
|
|
6
modules/cache/cache.go
vendored
6
modules/cache/cache.go
vendored
|
@ -10,9 +10,9 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
mc "gitea.com/macaron/cache"
|
mc "gitea.com/go-chi/cache"
|
||||||
|
|
||||||
_ "gitea.com/macaron/cache/memcache" // memcache plugin for cache
|
_ "gitea.com/go-chi/cache/memcache" // memcache plugin for cache
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -20,7 +20,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCache(cacheConfig setting.Cache) (mc.Cache, error) {
|
func newCache(cacheConfig setting.Cache) (mc.Cache, error) {
|
||||||
return mc.NewCacher(cacheConfig.Adapter, mc.Options{
|
return mc.NewCacher(mc.Options{
|
||||||
Adapter: cacheConfig.Adapter,
|
Adapter: cacheConfig.Adapter,
|
||||||
AdapterConfig: cacheConfig.Conn,
|
AdapterConfig: cacheConfig.Conn,
|
||||||
Interval: cacheConfig.Interval,
|
Interval: cacheConfig.Interval,
|
||||||
|
|
2
modules/cache/cache_redis.go
vendored
2
modules/cache/cache_redis.go
vendored
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/nosql"
|
"code.gitea.io/gitea/modules/nosql"
|
||||||
|
|
||||||
"gitea.com/macaron/cache"
|
"gitea.com/go-chi/cache"
|
||||||
"github.com/go-redis/redis/v7"
|
"github.com/go-redis/redis/v7"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,18 +6,21 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/auth/sso"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"gitea.com/macaron/csrf"
|
"gitea.com/go-chi/session"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIContext is a specific macaron context for API service
|
// APIContext is a specific macaron context for API service
|
||||||
|
@ -91,7 +94,7 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) {
|
||||||
if status == http.StatusInternalServerError {
|
if status == http.StatusInternalServerError {
|
||||||
log.ErrorWithSkip(1, "%s: %s", title, message)
|
log.ErrorWithSkip(1, "%s: %s", title, message)
|
||||||
|
|
||||||
if macaron.Env == macaron.PROD && !(ctx.User != nil && ctx.User.IsAdmin) {
|
if setting.IsProd() && !(ctx.User != nil && ctx.User.IsAdmin) {
|
||||||
message = ""
|
message = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,7 +111,7 @@ func (ctx *APIContext) InternalServerError(err error) {
|
||||||
log.ErrorWithSkip(1, "InternalServerError: %v", err)
|
log.ErrorWithSkip(1, "InternalServerError: %v", err)
|
||||||
|
|
||||||
var message string
|
var message string
|
||||||
if macaron.Env != macaron.PROD || (ctx.User != nil && ctx.User.IsAdmin) {
|
if !setting.IsProd() || (ctx.User != nil && ctx.User.IsAdmin) {
|
||||||
message = err.Error()
|
message = err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +121,20 @@ func (ctx *APIContext) InternalServerError(err error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
apiContextKey interface{} = "default_api_context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithAPIContext set up api context in request
|
||||||
|
func WithAPIContext(req *http.Request, ctx *APIContext) *http.Request {
|
||||||
|
return req.WithContext(context.WithValue(req.Context(), apiContextKey, ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
func genAPILinks(curURL *url.URL, total, pageSize, curPage int) []string {
|
||||||
page := NewPagination(total, pageSize, curPage, 0)
|
page := NewPagination(total, pageSize, curPage, 0)
|
||||||
paginater := page.Paginater
|
paginater := page.Paginater
|
||||||
|
@ -172,7 +189,7 @@ func (ctx *APIContext) RequireCSRF() {
|
||||||
headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName())
|
headerToken := ctx.Req.Header.Get(ctx.csrf.GetHeaderName())
|
||||||
formValueToken := ctx.Req.FormValue(ctx.csrf.GetFormName())
|
formValueToken := ctx.Req.FormValue(ctx.csrf.GetFormName())
|
||||||
if len(headerToken) > 0 || len(formValueToken) > 0 {
|
if len(headerToken) > 0 || len(formValueToken) > 0 {
|
||||||
csrf.Validate(ctx.Context.Context, ctx.csrf)
|
Validate(ctx.Context, ctx.csrf)
|
||||||
} else {
|
} else {
|
||||||
ctx.Context.Error(401, "Missing CSRF token.")
|
ctx.Context.Error(401, "Missing CSRF token.")
|
||||||
}
|
}
|
||||||
|
@ -201,42 +218,91 @@ func (ctx *APIContext) CheckForOTP() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIContexter returns apicontext as macaron middleware
|
// APIContexter returns apicontext as macaron middleware
|
||||||
func APIContexter() macaron.Handler {
|
func APIContexter() func(http.Handler) http.Handler {
|
||||||
return func(c *Context) {
|
var csrfOpts = getCsrfOpts()
|
||||||
ctx := &APIContext{
|
|
||||||
Context: c,
|
return func(next http.Handler) http.Handler {
|
||||||
}
|
|
||||||
c.Map(ctx)
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
var locale = middlewares.Locale(w, req)
|
||||||
|
var ctx = APIContext{
|
||||||
|
Context: &Context{
|
||||||
|
Resp: NewResponse(w),
|
||||||
|
Data: map[string]interface{}{},
|
||||||
|
Locale: locale,
|
||||||
|
Session: session.GetSession(req),
|
||||||
|
Repo: &Repository{
|
||||||
|
PullRequest: &PullRequest{},
|
||||||
|
},
|
||||||
|
Org: &Organization{},
|
||||||
|
},
|
||||||
|
Org: &APIOrganization{},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
|
||||||
|
ctx.csrf = Csrfer(csrfOpts, ctx.Context)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user from session if logged in.
|
||||||
|
ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req, ctx.Resp, &ctx, ctx.Session)
|
||||||
|
if ctx.User != nil {
|
||||||
|
ctx.IsSigned = true
|
||||||
|
ctx.Data["IsSigned"] = ctx.IsSigned
|
||||||
|
ctx.Data["SignedUser"] = ctx.User
|
||||||
|
ctx.Data["SignedUserID"] = ctx.User.ID
|
||||||
|
ctx.Data["SignedUserName"] = ctx.User.Name
|
||||||
|
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
|
||||||
|
} else {
|
||||||
|
ctx.Data["SignedUserID"] = int64(0)
|
||||||
|
ctx.Data["SignedUserName"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)
|
||||||
|
|
||||||
|
ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken())
|
||||||
|
|
||||||
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReferencesGitRepo injects the GitRepo into the Context
|
// ReferencesGitRepo injects the GitRepo into the Context
|
||||||
func ReferencesGitRepo(allowEmpty bool) macaron.Handler {
|
func ReferencesGitRepo(allowEmpty bool) func(http.Handler) http.Handler {
|
||||||
return func(ctx *APIContext) {
|
return func(next http.Handler) http.Handler {
|
||||||
// Empty repository does not have reference information.
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
if !allowEmpty && ctx.Repo.Repository.IsEmpty {
|
ctx := GetAPIContext(req)
|
||||||
return
|
// Empty repository does not have reference information.
|
||||||
}
|
if !allowEmpty && ctx.Repo.Repository.IsEmpty {
|
||||||
|
|
||||||
// For API calls.
|
|
||||||
if ctx.Repo.GitRepo == nil {
|
|
||||||
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
|
||||||
gitRepo, err := git.OpenRepository(repoPath)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Error(500, "RepoRef Invalid repo "+repoPath, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Repo.GitRepo = gitRepo
|
|
||||||
// We opened it, we should close it
|
|
||||||
defer func() {
|
|
||||||
// If it's been set to nil then assume someone else has closed it.
|
|
||||||
if ctx.Repo.GitRepo != nil {
|
|
||||||
ctx.Repo.GitRepo.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Next()
|
// For API calls.
|
||||||
|
if ctx.Repo.GitRepo == nil {
|
||||||
|
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||||
|
gitRepo, err := git.OpenRepository(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(500, "RepoRef Invalid repo "+repoPath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Repo.GitRepo = gitRepo
|
||||||
|
// We opened it, we should close it
|
||||||
|
defer func() {
|
||||||
|
// If it's been set to nil then assume someone else has closed it.
|
||||||
|
if ctx.Repo.GitRepo != nil {
|
||||||
|
ctx.Repo.GitRepo.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,8 +332,9 @@ func (ctx *APIContext) NotFound(objs ...interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoRefForAPI handles repository reference names when the ref name is not explicitly given
|
// RepoRefForAPI handles repository reference names when the ref name is not explicitly given
|
||||||
func RepoRefForAPI() macaron.Handler {
|
func RepoRefForAPI(next http.Handler) http.Handler {
|
||||||
return func(ctx *APIContext) {
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
ctx := GetAPIContext(req)
|
||||||
// Empty repository does not have reference information.
|
// Empty repository does not have reference information.
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
return
|
return
|
||||||
|
@ -319,6 +386,6 @@ func RepoRefForAPI() macaron.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Next()
|
next.ServeHTTP(w, req)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,8 @@ package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"gitea.com/macaron/csrf"
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToggleOptions contains required or check options
|
// ToggleOptions contains required or check options
|
||||||
|
@ -24,42 +20,23 @@ type ToggleOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle returns toggle options as middleware
|
// Toggle returns toggle options as middleware
|
||||||
func Toggle(options *ToggleOptions) macaron.Handler {
|
func Toggle(options *ToggleOptions) func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
isAPIPath := auth.IsAPIPath(ctx.Req.URL.Path)
|
|
||||||
|
|
||||||
// Check prohibit login users.
|
// Check prohibit login users.
|
||||||
if ctx.IsSigned {
|
if ctx.IsSigned {
|
||||||
if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
|
if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
|
||||||
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
|
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
|
||||||
if isAPIPath {
|
|
||||||
ctx.JSON(403, map[string]string{
|
|
||||||
"message": "This account is not activated.",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.HTML(200, "user/auth/activate")
|
ctx.HTML(200, "user/auth/activate")
|
||||||
return
|
return
|
||||||
} else if !ctx.User.IsActive || ctx.User.ProhibitLogin {
|
}
|
||||||
|
if !ctx.User.IsActive || ctx.User.ProhibitLogin {
|
||||||
log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr())
|
log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr())
|
||||||
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
|
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
|
||||||
if isAPIPath {
|
|
||||||
ctx.JSON(403, map[string]string{
|
|
||||||
"message": "This account is prohibited from signing in, please contact your site administrator.",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.HTML(200, "user/auth/prohibit_login")
|
ctx.HTML(200, "user/auth/prohibit_login")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.User.MustChangePassword {
|
if ctx.User.MustChangePassword {
|
||||||
if isAPIPath {
|
|
||||||
ctx.JSON(403, map[string]string{
|
|
||||||
"message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ctx.Req.URL.Path != "/user/settings/change_password" {
|
if ctx.Req.URL.Path != "/user/settings/change_password" {
|
||||||
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
|
||||||
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
|
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
|
||||||
|
@ -82,8 +59,8 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" && !auth.IsAPIPath(ctx.Req.URL.Path) {
|
if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" {
|
||||||
csrf.Validate(ctx.Context, ctx.csrf)
|
Validate(ctx, ctx.csrf)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -91,13 +68,6 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||||
|
|
||||||
if options.SignInRequired {
|
if options.SignInRequired {
|
||||||
if !ctx.IsSigned {
|
if !ctx.IsSigned {
|
||||||
// Restrict API calls with error message.
|
|
||||||
if isAPIPath {
|
|
||||||
ctx.JSON(403, map[string]string{
|
|
||||||
"message": "Only signed in user is allowed to call APIs.",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ctx.Req.URL.Path != "/user/events" {
|
if ctx.Req.URL.Path != "/user/events" {
|
||||||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
|
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
|
||||||
}
|
}
|
||||||
|
@ -108,32 +78,10 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||||
ctx.HTML(200, "user/auth/activate")
|
ctx.HTML(200, "user/auth/activate")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ctx.IsSigned && isAPIPath && ctx.IsBasicAuth {
|
|
||||||
twofa, err := models.GetTwoFactorByUID(ctx.User.ID)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrTwoFactorNotEnrolled(err) {
|
|
||||||
return // No 2FA enrollment for this user
|
|
||||||
}
|
|
||||||
ctx.Error(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
|
|
||||||
ok, err := twofa.ValidateTOTP(otpHeader)
|
|
||||||
if err != nil {
|
|
||||||
ctx.Error(500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
ctx.JSON(403, map[string]string{
|
|
||||||
"message": "Only signed in user is allowed to call APIs.",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
||||||
if !options.SignOutRequired && !ctx.IsSigned && !isAPIPath &&
|
if !options.SignOutRequired && !ctx.IsSigned &&
|
||||||
len(ctx.GetCookie(setting.CookieUserName)) > 0 {
|
len(ctx.GetCookie(setting.CookieUserName)) > 0 {
|
||||||
if ctx.Req.URL.Path != "/user/events" {
|
if ctx.Req.URL.Path != "/user/events" {
|
||||||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
|
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
|
||||||
|
@ -151,3 +99,86 @@ func Toggle(options *ToggleOptions) macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToggleAPI returns toggle options as middleware
|
||||||
|
func ToggleAPI(options *ToggleOptions) func(ctx *APIContext) {
|
||||||
|
return func(ctx *APIContext) {
|
||||||
|
// Check prohibit login users.
|
||||||
|
if ctx.IsSigned {
|
||||||
|
if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
|
||||||
|
ctx.JSON(403, map[string]string{
|
||||||
|
"message": "This account is not activated.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ctx.User.IsActive || ctx.User.ProhibitLogin {
|
||||||
|
log.Info("Failed authentication attempt for %s from %s", ctx.User.Name, ctx.RemoteAddr())
|
||||||
|
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
|
||||||
|
ctx.JSON(403, map[string]string{
|
||||||
|
"message": "This account is prohibited from signing in, please contact your site administrator.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.User.MustChangePassword {
|
||||||
|
ctx.JSON(403, map[string]string{
|
||||||
|
"message": "You must change your password. Change it at: " + setting.AppURL + "/user/change_password",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to dashboard if user tries to visit any non-login page.
|
||||||
|
if options.SignOutRequired && ctx.IsSigned && ctx.Req.URL.RequestURI() != "/" {
|
||||||
|
ctx.Redirect(setting.AppSubURL + "/")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.SignInRequired {
|
||||||
|
if !ctx.IsSigned {
|
||||||
|
// Restrict API calls with error message.
|
||||||
|
ctx.JSON(403, map[string]string{
|
||||||
|
"message": "Only signed in user is allowed to call APIs.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm {
|
||||||
|
ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
|
||||||
|
ctx.HTML(200, "user/auth/activate")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.IsSigned && ctx.IsBasicAuth {
|
||||||
|
twofa, err := models.GetTwoFactorByUID(ctx.User.ID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrTwoFactorNotEnrolled(err) {
|
||||||
|
return // No 2FA enrollment for this user
|
||||||
|
}
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
|
||||||
|
ok, err := twofa.ValidateTOTP(otpHeader)
|
||||||
|
if err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
ctx.JSON(403, map[string]string{
|
||||||
|
"message": "Only signed in user is allowed to call APIs.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.AdminRequired {
|
||||||
|
if !ctx.User.IsAdmin {
|
||||||
|
ctx.JSON(403, map[string]string{
|
||||||
|
"message": "You have no permission to request for this.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["PageIsAdmin"] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
26
modules/context/captcha.go
Normal file
26
modules/context/captcha.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/captcha"
|
||||||
|
)
|
||||||
|
|
||||||
|
var imageCaptchaOnce sync.Once
|
||||||
|
var cpt *captcha.Captcha
|
||||||
|
|
||||||
|
// GetImageCaptcha returns global image captcha
|
||||||
|
func GetImageCaptcha() *captcha.Captcha {
|
||||||
|
imageCaptchaOnce.Do(func() {
|
||||||
|
cpt = captcha.NewCaptcha(captcha.Options{
|
||||||
|
SubURL: setting.AppSubURL,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return cpt
|
||||||
|
}
|
|
@ -6,37 +6,55 @@
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"html"
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
|
||||||
"code.gitea.io/gitea/modules/auth/sso"
|
"code.gitea.io/gitea/modules/auth/sso"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/templates"
|
||||||
|
"code.gitea.io/gitea/modules/translation"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"gitea.com/macaron/cache"
|
"gitea.com/go-chi/cache"
|
||||||
"gitea.com/macaron/csrf"
|
"gitea.com/go-chi/session"
|
||||||
"gitea.com/macaron/i18n"
|
"github.com/go-chi/chi"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
"gitea.com/macaron/session"
|
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
|
"github.com/unknwon/i18n"
|
||||||
|
"github.com/unrolled/render"
|
||||||
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Render represents a template render
|
||||||
|
type Render interface {
|
||||||
|
TemplateLookup(tmpl string) *template.Template
|
||||||
|
HTML(w io.Writer, status int, name string, binding interface{}, htmlOpt ...render.HTMLOptions) error
|
||||||
|
}
|
||||||
|
|
||||||
// Context represents context of a request.
|
// Context represents context of a request.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
*macaron.Context
|
Resp ResponseWriter
|
||||||
|
Req *http.Request
|
||||||
|
Data map[string]interface{}
|
||||||
|
Render Render
|
||||||
|
translation.Locale
|
||||||
Cache cache.Cache
|
Cache cache.Cache
|
||||||
csrf csrf.CSRF
|
csrf CSRF
|
||||||
Flash *session.Flash
|
Flash *middlewares.Flash
|
||||||
Session session.Store
|
Session session.Store
|
||||||
|
|
||||||
Link string // current request URL
|
Link string // current request URL
|
||||||
|
@ -163,13 +181,22 @@ func (ctx *Context) RedirectToFirst(location ...string) {
|
||||||
// HTML calls Context.HTML and converts template name to string.
|
// HTML calls Context.HTML and converts template name to string.
|
||||||
func (ctx *Context) HTML(status int, name base.TplName) {
|
func (ctx *Context) HTML(status int, name base.TplName) {
|
||||||
log.Debug("Template: %s", name)
|
log.Debug("Template: %s", name)
|
||||||
ctx.Context.HTML(status, string(name))
|
if err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data); err != nil {
|
||||||
|
ctx.ServerError("Render failed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMLString render content to a string but not http.ResponseWriter
|
||||||
|
func (ctx *Context) HTMLString(name string, data interface{}) (string, error) {
|
||||||
|
var buf strings.Builder
|
||||||
|
err := ctx.Render.HTML(&buf, 200, string(name), data)
|
||||||
|
return buf.String(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderWithErr used for page has form validation but need to prompt error to users.
|
// RenderWithErr used for page has form validation but need to prompt error to users.
|
||||||
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) {
|
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) {
|
||||||
if form != nil {
|
if form != nil {
|
||||||
auth.AssignForm(form, ctx.Data)
|
middlewares.AssignForm(form, ctx.Data)
|
||||||
}
|
}
|
||||||
ctx.Flash.ErrorMsg = msg
|
ctx.Flash.ErrorMsg = msg
|
||||||
ctx.Data["Flash"] = ctx.Flash
|
ctx.Data["Flash"] = ctx.Flash
|
||||||
|
@ -184,7 +211,7 @@ func (ctx *Context) NotFound(title string, err error) {
|
||||||
func (ctx *Context) notFoundInternal(title string, err error) {
|
func (ctx *Context) notFoundInternal(title string, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorWithSkip(2, "%s: %v", title, err)
|
log.ErrorWithSkip(2, "%s: %v", title, err)
|
||||||
if macaron.Env != macaron.PROD {
|
if !setting.IsProd() {
|
||||||
ctx.Data["ErrorMsg"] = err
|
ctx.Data["ErrorMsg"] = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,7 +230,7 @@ func (ctx *Context) ServerError(title string, err error) {
|
||||||
func (ctx *Context) serverErrorInternal(title string, err error) {
|
func (ctx *Context) serverErrorInternal(title string, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ErrorWithSkip(2, "%s: %v", title, err)
|
log.ErrorWithSkip(2, "%s: %v", title, err)
|
||||||
if macaron.Env != macaron.PROD {
|
if !setting.IsProd() {
|
||||||
ctx.Data["ErrorMsg"] = err
|
ctx.Data["ErrorMsg"] = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,6 +251,44 @@ func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool,
|
||||||
ctx.serverErrorInternal(title, err)
|
ctx.serverErrorInternal(title, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header returns a header
|
||||||
|
func (ctx *Context) Header() http.Header {
|
||||||
|
return ctx.Resp.Header()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: We should differ Query and Form, currently we just use form as query
|
||||||
|
// Currently to be compatible with macaron, we keep it.
|
||||||
|
|
||||||
|
// Query returns request form as string with default
|
||||||
|
func (ctx *Context) Query(key string, defaults ...string) string {
|
||||||
|
return (*Forms)(ctx.Req).MustString(key, defaults...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryTrim returns request form as string with default and trimmed spaces
|
||||||
|
func (ctx *Context) QueryTrim(key string, defaults ...string) string {
|
||||||
|
return (*Forms)(ctx.Req).MustTrimmed(key, defaults...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryStrings returns request form as strings with default
|
||||||
|
func (ctx *Context) QueryStrings(key string, defaults ...[]string) []string {
|
||||||
|
return (*Forms)(ctx.Req).MustStrings(key, defaults...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryInt returns request form as int with default
|
||||||
|
func (ctx *Context) QueryInt(key string, defaults ...int) int {
|
||||||
|
return (*Forms)(ctx.Req).MustInt(key, defaults...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryInt64 returns request form as int64 with default
|
||||||
|
func (ctx *Context) QueryInt64(key string, defaults ...int64) int64 {
|
||||||
|
return (*Forms)(ctx.Req).MustInt64(key, defaults...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryBool returns request form as bool with default
|
||||||
|
func (ctx *Context) QueryBool(key string, defaults ...bool) bool {
|
||||||
|
return (*Forms)(ctx.Req).MustBool(key, defaults...)
|
||||||
|
}
|
||||||
|
|
||||||
// HandleText handles HTTP status code
|
// HandleText handles HTTP status code
|
||||||
func (ctx *Context) HandleText(status int, title string) {
|
func (ctx *Context) HandleText(status int, title string) {
|
||||||
if (status/100 == 4) || (status/100 == 5) {
|
if (status/100 == 4) || (status/100 == 5) {
|
||||||
|
@ -249,66 +314,324 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
|
||||||
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
||||||
ctx.Resp.Header().Set("Pragma", "public")
|
ctx.Resp.Header().Set("Pragma", "public")
|
||||||
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
|
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
|
||||||
http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
|
http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlainText render content as plain text
|
||||||
|
func (ctx *Context) PlainText(status int, bs []byte) {
|
||||||
|
ctx.Resp.WriteHeader(status)
|
||||||
|
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf8")
|
||||||
|
if _, err := ctx.Resp.Write(bs); err != nil {
|
||||||
|
ctx.ServerError("Render JSON failed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeFile serves given file to response.
|
||||||
|
func (ctx *Context) ServeFile(file string, names ...string) {
|
||||||
|
var name string
|
||||||
|
if len(names) > 0 {
|
||||||
|
name = names[0]
|
||||||
|
} else {
|
||||||
|
name = path.Base(file)
|
||||||
|
}
|
||||||
|
ctx.Resp.Header().Set("Content-Description", "File Transfer")
|
||||||
|
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
|
||||||
|
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
|
||||||
|
ctx.Resp.Header().Set("Expires", "0")
|
||||||
|
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
||||||
|
ctx.Resp.Header().Set("Pragma", "public")
|
||||||
|
http.ServeFile(ctx.Resp, ctx.Req, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returned an error to web browser
|
||||||
|
func (ctx *Context) Error(status int, contents ...string) {
|
||||||
|
var v = http.StatusText(status)
|
||||||
|
if len(contents) > 0 {
|
||||||
|
v = contents[0]
|
||||||
|
}
|
||||||
|
http.Error(ctx.Resp, v, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON render content as JSON
|
||||||
|
func (ctx *Context) JSON(status int, content interface{}) {
|
||||||
|
ctx.Resp.WriteHeader(status)
|
||||||
|
ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf8")
|
||||||
|
if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil {
|
||||||
|
ctx.ServerError("Render JSON failed", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect redirect the request
|
||||||
|
func (ctx *Context) Redirect(location string, status ...int) {
|
||||||
|
code := http.StatusFound
|
||||||
|
if len(status) == 1 {
|
||||||
|
code = status[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(ctx.Resp, ctx.Req, location, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCookie set cookies to web browser
|
||||||
|
func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
|
||||||
|
middlewares.SetCookie(ctx.Resp, name, value, others...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCookie returns given cookie value from request header.
|
||||||
|
func (ctx *Context) GetCookie(name string) string {
|
||||||
|
return middlewares.GetCookie(ctx.Req, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSuperSecureCookie returns given cookie value from request header with secret string.
|
||||||
|
func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
|
||||||
|
val := ctx.GetCookie(name)
|
||||||
|
if val == "" {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
text, err := hex.DecodeString(val)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
|
||||||
|
text, err = com.AESGCMDecrypt(key, text)
|
||||||
|
return string(text), err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSuperSecureCookie sets given cookie value to response header with secret string.
|
||||||
|
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) {
|
||||||
|
key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
|
||||||
|
text, err := com.AESGCMEncrypt(key, []byte(value))
|
||||||
|
if err != nil {
|
||||||
|
panic("error encrypting cookie: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.SetCookie(name, hex.EncodeToString(text), others...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCookieInt returns cookie result in int type.
|
||||||
|
func (ctx *Context) GetCookieInt(name string) int {
|
||||||
|
r, _ := strconv.Atoi(ctx.GetCookie(name))
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCookieInt64 returns cookie result in int64 type.
|
||||||
|
func (ctx *Context) GetCookieInt64(name string) int64 {
|
||||||
|
r, _ := strconv.ParseInt(ctx.GetCookie(name), 10, 64)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCookieFloat64 returns cookie result in float64 type.
|
||||||
|
func (ctx *Context) GetCookieFloat64(name string) float64 {
|
||||||
|
v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoteAddr returns the client machie ip address
|
||||||
|
func (ctx *Context) RemoteAddr() string {
|
||||||
|
return ctx.Req.RemoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params returns the param on route
|
||||||
|
func (ctx *Context) Params(p string) string {
|
||||||
|
s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":")))
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParamsInt64 returns the param on route as int64
|
||||||
|
func (ctx *Context) ParamsInt64(p string) int64 {
|
||||||
|
v, _ := strconv.ParseInt(ctx.Params(p), 10, 64)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetParams set params into routes
|
||||||
|
func (ctx *Context) SetParams(k, v string) {
|
||||||
|
chiCtx := chi.RouteContext(ctx.Req.Context())
|
||||||
|
chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes data to webbrowser
|
||||||
|
func (ctx *Context) Write(bs []byte) (int, error) {
|
||||||
|
return ctx.Resp.Write(bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Written returns true if there are something sent to web browser
|
||||||
|
func (ctx *Context) Written() bool {
|
||||||
|
return ctx.Resp.Status() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status writes status code
|
||||||
|
func (ctx *Context) Status(status int) {
|
||||||
|
ctx.Resp.WriteHeader(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler represents a custom handler
|
||||||
|
type Handler func(*Context)
|
||||||
|
|
||||||
|
// enumerate all content
|
||||||
|
var (
|
||||||
|
contextKey interface{} = "default_context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithContext set up install context in request
|
||||||
|
func WithContext(req *http.Request, ctx *Context) *http.Request {
|
||||||
|
return req.WithContext(context.WithValue(req.Context(), contextKey, ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContext retrieves install context from request
|
||||||
|
func GetContext(req *http.Request) *Context {
|
||||||
|
return req.Context().Value(contextKey).(*Context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCsrfOpts() CsrfOptions {
|
||||||
|
return CsrfOptions{
|
||||||
|
Secret: setting.SecretKey,
|
||||||
|
Cookie: setting.CSRFCookieName,
|
||||||
|
SetCookie: true,
|
||||||
|
Secure: setting.SessionConfig.Secure,
|
||||||
|
CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
|
||||||
|
Header: "X-Csrf-Token",
|
||||||
|
CookieDomain: setting.SessionConfig.Domain,
|
||||||
|
CookiePath: setting.SessionConfig.CookiePath,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contexter initializes a classic context for a request.
|
// Contexter initializes a classic context for a request.
|
||||||
func Contexter() macaron.Handler {
|
func Contexter() func(next http.Handler) http.Handler {
|
||||||
return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) {
|
rnd := templates.HTMLRenderer()
|
||||||
ctx := &Context{
|
|
||||||
Context: c,
|
|
||||||
Cache: cache,
|
|
||||||
csrf: x,
|
|
||||||
Flash: f,
|
|
||||||
Session: sess,
|
|
||||||
Link: setting.AppSubURL + strings.TrimSuffix(c.Req.URL.EscapedPath(), "/"),
|
|
||||||
Repo: &Repository{
|
|
||||||
PullRequest: &PullRequest{},
|
|
||||||
},
|
|
||||||
Org: &Organization{},
|
|
||||||
}
|
|
||||||
ctx.Data["Language"] = ctx.Locale.Language()
|
|
||||||
c.Data["Link"] = ctx.Link
|
|
||||||
ctx.Data["CurrentURL"] = setting.AppSubURL + c.Req.URL.RequestURI()
|
|
||||||
ctx.Data["PageStartTime"] = time.Now()
|
|
||||||
// Quick responses appropriate go-get meta with status 200
|
|
||||||
// regardless of if user have access to the repository,
|
|
||||||
// or the repository does not exist at all.
|
|
||||||
// This is particular a workaround for "go get" command which does not respect
|
|
||||||
// .netrc file.
|
|
||||||
if ctx.Query("go-get") == "1" {
|
|
||||||
ownerName := c.Params(":username")
|
|
||||||
repoName := c.Params(":reponame")
|
|
||||||
trimmedRepoName := strings.TrimSuffix(repoName, ".git")
|
|
||||||
|
|
||||||
if ownerName == "" || trimmedRepoName == "" {
|
var c cache.Cache
|
||||||
_, _ = c.Write([]byte(`<!doctype html>
|
var err error
|
||||||
|
if setting.CacheService.Enabled {
|
||||||
|
c, err = cache.NewCacher(cache.Options{
|
||||||
|
Adapter: setting.CacheService.Adapter,
|
||||||
|
AdapterConfig: setting.CacheService.Conn,
|
||||||
|
Interval: setting.CacheService.Interval,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var csrfOpts = getCsrfOpts()
|
||||||
|
//var flashEncryptionKey, _ = NewSecret()
|
||||||
|
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
var locale = middlewares.Locale(resp, req)
|
||||||
|
var startTime = time.Now()
|
||||||
|
var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/")
|
||||||
|
var ctx = Context{
|
||||||
|
Resp: NewResponse(resp),
|
||||||
|
Cache: c,
|
||||||
|
Locale: locale,
|
||||||
|
Link: link,
|
||||||
|
Render: rnd,
|
||||||
|
Session: session.GetSession(req),
|
||||||
|
Repo: &Repository{
|
||||||
|
PullRequest: &PullRequest{},
|
||||||
|
},
|
||||||
|
Org: &Organization{},
|
||||||
|
Data: map[string]interface{}{
|
||||||
|
"CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
|
||||||
|
"PageStartTime": startTime,
|
||||||
|
"TmplLoadTimes": func() string {
|
||||||
|
return time.Since(startTime).String()
|
||||||
|
},
|
||||||
|
"Link": link,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Req = WithContext(req, &ctx)
|
||||||
|
ctx.csrf = Csrfer(csrfOpts, &ctx)
|
||||||
|
|
||||||
|
// Get flash.
|
||||||
|
flashCookie := ctx.GetCookie("macaron_flash")
|
||||||
|
vals, _ := url.ParseQuery(flashCookie)
|
||||||
|
if len(vals) > 0 {
|
||||||
|
f := &middlewares.Flash{
|
||||||
|
DataStore: &ctx,
|
||||||
|
Values: vals,
|
||||||
|
ErrorMsg: vals.Get("error"),
|
||||||
|
SuccessMsg: vals.Get("success"),
|
||||||
|
InfoMsg: vals.Get("info"),
|
||||||
|
WarningMsg: vals.Get("warning"),
|
||||||
|
}
|
||||||
|
ctx.Data["Flash"] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
f := &middlewares.Flash{
|
||||||
|
DataStore: &ctx,
|
||||||
|
Values: url.Values{},
|
||||||
|
ErrorMsg: "",
|
||||||
|
WarningMsg: "",
|
||||||
|
InfoMsg: "",
|
||||||
|
SuccessMsg: "",
|
||||||
|
}
|
||||||
|
ctx.Resp.Before(func(resp ResponseWriter) {
|
||||||
|
if flash := f.Encode(); len(flash) > 0 {
|
||||||
|
if err == nil {
|
||||||
|
middlewares.SetCookie(resp, "macaron_flash", flash, 0,
|
||||||
|
setting.SessionConfig.CookiePath,
|
||||||
|
middlewares.Domain(setting.SessionConfig.Domain),
|
||||||
|
middlewares.HTTPOnly(true),
|
||||||
|
middlewares.Secure(setting.SessionConfig.Secure),
|
||||||
|
//middlewares.SameSite(opt.SameSite), FIXME: we need a samesite config
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.SetCookie("macaron_flash", "", -1,
|
||||||
|
setting.SessionConfig.CookiePath,
|
||||||
|
middlewares.Domain(setting.SessionConfig.Domain),
|
||||||
|
middlewares.HTTPOnly(true),
|
||||||
|
middlewares.Secure(setting.SessionConfig.Secure),
|
||||||
|
//middlewares.SameSite(), FIXME: we need a samesite config
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.Flash = f
|
||||||
|
|
||||||
|
// Quick responses appropriate go-get meta with status 200
|
||||||
|
// regardless of if user have access to the repository,
|
||||||
|
// or the repository does not exist at all.
|
||||||
|
// This is particular a workaround for "go get" command which does not respect
|
||||||
|
// .netrc file.
|
||||||
|
if ctx.Query("go-get") == "1" {
|
||||||
|
ownerName := ctx.Params(":username")
|
||||||
|
repoName := ctx.Params(":reponame")
|
||||||
|
trimmedRepoName := strings.TrimSuffix(repoName, ".git")
|
||||||
|
|
||||||
|
if ownerName == "" || trimmedRepoName == "" {
|
||||||
|
_, _ = ctx.Write([]byte(`<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
invalid import path
|
invalid import path
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`))
|
`))
|
||||||
c.WriteHeader(400)
|
ctx.Status(400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
branchName := "master"
|
branchName := "master"
|
||||||
|
|
||||||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
|
||||||
if err == nil && len(repo.DefaultBranch) > 0 {
|
if err == nil && len(repo.DefaultBranch) > 0 {
|
||||||
branchName = repo.DefaultBranch
|
branchName = repo.DefaultBranch
|
||||||
}
|
}
|
||||||
prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
|
prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
|
||||||
|
|
||||||
appURL, _ := url.Parse(setting.AppURL)
|
appURL, _ := url.Parse(setting.AppURL)
|
||||||
|
|
||||||
insecure := ""
|
insecure := ""
|
||||||
if appURL.Scheme == string(setting.HTTP) {
|
if appURL.Scheme == string(setting.HTTP) {
|
||||||
insecure = "--insecure "
|
insecure = "--insecure "
|
||||||
}
|
}
|
||||||
c.Header().Set("Content-Type", "text/html")
|
ctx.Header().Set("Content-Type", "text/html")
|
||||||
c.WriteHeader(http.StatusOK)
|
ctx.Status(http.StatusOK)
|
||||||
_, _ = c.Write([]byte(com.Expand(`<!doctype html>
|
_, _ = ctx.Write([]byte(com.Expand(`<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta name="go-import" content="{GoGetImport} git {CloneLink}">
|
<meta name="go-import" content="{GoGetImport} git {CloneLink}">
|
||||||
|
@ -319,60 +642,72 @@ func Contexter() macaron.Handler {
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`, map[string]string{
|
`, map[string]string{
|
||||||
"GoGetImport": ComposeGoGetImport(ownerName, trimmedRepoName),
|
"GoGetImport": ComposeGoGetImport(ownerName, trimmedRepoName),
|
||||||
"CloneLink": models.ComposeHTTPSCloneURL(ownerName, repoName),
|
"CloneLink": models.ComposeHTTPSCloneURL(ownerName, repoName),
|
||||||
"GoDocDirectory": prefix + "{/dir}",
|
"GoDocDirectory": prefix + "{/dir}",
|
||||||
"GoDocFile": prefix + "{/dir}/{file}#L{line}",
|
"GoDocFile": prefix + "{/dir}/{file}#L{line}",
|
||||||
"Insecure": insecure,
|
"Insecure": insecure,
|
||||||
})))
|
})))
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user from session if logged in.
|
|
||||||
ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req.Request, c.Resp, ctx, ctx.Session)
|
|
||||||
|
|
||||||
if ctx.User != nil {
|
|
||||||
ctx.IsSigned = true
|
|
||||||
ctx.Data["IsSigned"] = ctx.IsSigned
|
|
||||||
ctx.Data["SignedUser"] = ctx.User
|
|
||||||
ctx.Data["SignedUserID"] = ctx.User.ID
|
|
||||||
ctx.Data["SignedUserName"] = ctx.User.Name
|
|
||||||
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
|
|
||||||
} else {
|
|
||||||
ctx.Data["SignedUserID"] = int64(0)
|
|
||||||
ctx.Data["SignedUserName"] = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Data["CsrfToken"] = html.EscapeString(x.GetToken())
|
// Get user from session if logged in.
|
||||||
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
|
ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req, ctx.Resp, &ctx, ctx.Session)
|
||||||
log.Debug("Session ID: %s", sess.ID())
|
|
||||||
log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
|
|
||||||
|
|
||||||
ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome
|
if ctx.User != nil {
|
||||||
ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore
|
ctx.IsSigned = true
|
||||||
ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations
|
ctx.Data["IsSigned"] = ctx.IsSigned
|
||||||
|
ctx.Data["SignedUser"] = ctx.User
|
||||||
|
ctx.Data["SignedUserID"] = ctx.User.ID
|
||||||
|
ctx.Data["SignedUserName"] = ctx.User.Name
|
||||||
|
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
|
||||||
|
} else {
|
||||||
|
ctx.Data["SignedUserID"] = int64(0)
|
||||||
|
ctx.Data["SignedUserName"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
|
ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)
|
||||||
ctx.Data["ShowMilestonesDashboardPage"] = setting.Service.ShowMilestonesDashboardPage
|
|
||||||
ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
|
|
||||||
ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion
|
|
||||||
|
|
||||||
ctx.Data["EnableSwagger"] = setting.API.EnableSwagger
|
ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken())
|
||||||
ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
|
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
|
||||||
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
|
log.Debug("Session ID: %s", ctx.Session.ID())
|
||||||
|
log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
|
||||||
|
|
||||||
ctx.Data["ManifestData"] = setting.ManifestData
|
ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome
|
||||||
|
ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore
|
||||||
|
ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations
|
||||||
|
|
||||||
c.Map(ctx)
|
ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
|
||||||
|
ctx.Data["ShowMilestonesDashboardPage"] = setting.Service.ShowMilestonesDashboardPage
|
||||||
|
ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
|
||||||
|
ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion
|
||||||
|
|
||||||
|
ctx.Data["EnableSwagger"] = setting.API.EnableSwagger
|
||||||
|
ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
|
||||||
|
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
|
||||||
|
|
||||||
|
ctx.Data["ManifestData"] = setting.ManifestData
|
||||||
|
|
||||||
|
ctx.Data["i18n"] = locale
|
||||||
|
ctx.Data["Tr"] = i18n.Tr
|
||||||
|
ctx.Data["Lang"] = locale.Language()
|
||||||
|
ctx.Data["AllLangs"] = translation.AllLangs()
|
||||||
|
for _, lang := range translation.AllLangs() {
|
||||||
|
if lang.Lang == locale.Language() {
|
||||||
|
ctx.Data["LangName"] = lang.Name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2013 Martini Authors
|
// Copyright 2013 Martini Authors
|
||||||
// Copyright 2014 The Macaron Authors
|
// Copyright 2014 The Macaron Authors
|
||||||
|
// Copyright 2021 The Gitea Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
// 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
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
@ -13,24 +14,17 @@
|
||||||
// License for the specific language governing permissions and limitations
|
// License for the specific language governing permissions and limitations
|
||||||
// under the License.
|
// under the License.
|
||||||
|
|
||||||
// Package csrf is a middleware that generates and validates CSRF tokens for Macaron.
|
// a middleware that generates and validates CSRF tokens.
|
||||||
package csrf
|
|
||||||
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
"gitea.com/macaron/session"
|
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
)
|
)
|
||||||
|
|
||||||
const _VERSION = "0.1.1"
|
|
||||||
|
|
||||||
func Version() string {
|
|
||||||
return _VERSION
|
|
||||||
}
|
|
||||||
|
|
||||||
// CSRF represents a CSRF service and is used to get the current token and validate a suspect token.
|
// CSRF represents a CSRF service and is used to get the current token and validate a suspect token.
|
||||||
type CSRF interface {
|
type CSRF interface {
|
||||||
// Return HTTP header to search for token.
|
// Return HTTP header to search for token.
|
||||||
|
@ -42,7 +36,7 @@ type CSRF interface {
|
||||||
// Return cookie path
|
// Return cookie path
|
||||||
GetCookiePath() string
|
GetCookiePath() string
|
||||||
// Return the flag value used for the csrf token.
|
// Return the flag value used for the csrf token.
|
||||||
GetCookieHttpOnly() bool
|
GetCookieHTTPOnly() bool
|
||||||
// Return the token.
|
// Return the token.
|
||||||
GetToken() string
|
GetToken() string
|
||||||
// Validate by token.
|
// Validate by token.
|
||||||
|
@ -63,7 +57,7 @@ type csrf struct {
|
||||||
//Cookie path
|
//Cookie path
|
||||||
CookiePath string
|
CookiePath string
|
||||||
// Cookie HttpOnly flag value used for the csrf token.
|
// Cookie HttpOnly flag value used for the csrf token.
|
||||||
CookieHttpOnly bool
|
CookieHTTPOnly bool
|
||||||
// Token generated to pass via header, cookie, or hidden form value.
|
// Token generated to pass via header, cookie, or hidden form value.
|
||||||
Token string
|
Token string
|
||||||
// This value must be unique per user.
|
// This value must be unique per user.
|
||||||
|
@ -94,9 +88,9 @@ func (c *csrf) GetCookiePath() string {
|
||||||
return c.CookiePath
|
return c.CookiePath
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCookieHttpOnly returns the flag value used for the csrf token.
|
// GetCookieHTTPOnly returns the flag value used for the csrf token.
|
||||||
func (c *csrf) GetCookieHttpOnly() bool {
|
func (c *csrf) GetCookieHTTPOnly() bool {
|
||||||
return c.CookieHttpOnly
|
return c.CookieHTTPOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken returns the current token. This is typically used
|
// GetToken returns the current token. This is typically used
|
||||||
|
@ -115,8 +109,8 @@ func (c *csrf) Error(w http.ResponseWriter) {
|
||||||
c.ErrorFunc(w)
|
c.ErrorFunc(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options maintains options to manage behavior of Generate.
|
// CsrfOptions maintains options to manage behavior of Generate.
|
||||||
type Options struct {
|
type CsrfOptions struct {
|
||||||
// The global secret value used to generate Tokens.
|
// The global secret value used to generate Tokens.
|
||||||
Secret string
|
Secret string
|
||||||
// HTTP header used to set and get token.
|
// HTTP header used to set and get token.
|
||||||
|
@ -129,11 +123,13 @@ type Options struct {
|
||||||
CookieDomain string
|
CookieDomain string
|
||||||
// Cookie path.
|
// Cookie path.
|
||||||
CookiePath string
|
CookiePath string
|
||||||
CookieHttpOnly bool
|
CookieHTTPOnly bool
|
||||||
|
// SameSite set the cookie SameSite type
|
||||||
|
SameSite http.SameSite
|
||||||
// Key used for getting the unique ID per user.
|
// Key used for getting the unique ID per user.
|
||||||
SessionKey string
|
SessionKey string
|
||||||
// oldSeesionKey saves old value corresponding to SessionKey.
|
// oldSessionKey saves old value corresponding to SessionKey.
|
||||||
oldSeesionKey string
|
oldSessionKey string
|
||||||
// If true, send token via X-CSRFToken header.
|
// If true, send token via X-CSRFToken header.
|
||||||
SetHeader bool
|
SetHeader bool
|
||||||
// If true, send token via _csrf cookie.
|
// If true, send token via _csrf cookie.
|
||||||
|
@ -144,10 +140,12 @@ type Options struct {
|
||||||
Origin bool
|
Origin bool
|
||||||
// The function called when Validate fails.
|
// The function called when Validate fails.
|
||||||
ErrorFunc func(w http.ResponseWriter)
|
ErrorFunc func(w http.ResponseWriter)
|
||||||
|
// Cookie life time. Default is 0
|
||||||
|
CookieLifeTime int
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareOptions(options []Options) Options {
|
func prepareOptions(options []CsrfOptions) CsrfOptions {
|
||||||
var opt Options
|
var opt CsrfOptions
|
||||||
if len(options) > 0 {
|
if len(options) > 0 {
|
||||||
opt = options[0]
|
opt = options[0]
|
||||||
}
|
}
|
||||||
|
@ -171,7 +169,7 @@ func prepareOptions(options []Options) Options {
|
||||||
if len(opt.SessionKey) == 0 {
|
if len(opt.SessionKey) == 0 {
|
||||||
opt.SessionKey = "uid"
|
opt.SessionKey = "uid"
|
||||||
}
|
}
|
||||||
opt.oldSeesionKey = "_old_" + opt.SessionKey
|
opt.oldSessionKey = "_old_" + opt.SessionKey
|
||||||
if opt.ErrorFunc == nil {
|
if opt.ErrorFunc == nil {
|
||||||
opt.ErrorFunc = func(w http.ResponseWriter) {
|
opt.ErrorFunc = func(w http.ResponseWriter) {
|
||||||
http.Error(w, "Invalid csrf token.", http.StatusBadRequest)
|
http.Error(w, "Invalid csrf token.", http.StatusBadRequest)
|
||||||
|
@ -181,73 +179,73 @@ func prepareOptions(options []Options) Options {
|
||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate maps CSRF to each request. If this request is a Get request, it will generate a new token.
|
|
||||||
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
|
|
||||||
func Generate(options ...Options) macaron.Handler {
|
|
||||||
opt := prepareOptions(options)
|
|
||||||
return func(ctx *macaron.Context, sess session.Store) {
|
|
||||||
x := &csrf{
|
|
||||||
Secret: opt.Secret,
|
|
||||||
Header: opt.Header,
|
|
||||||
Form: opt.Form,
|
|
||||||
Cookie: opt.Cookie,
|
|
||||||
CookieDomain: opt.CookieDomain,
|
|
||||||
CookiePath: opt.CookiePath,
|
|
||||||
CookieHttpOnly: opt.CookieHttpOnly,
|
|
||||||
ErrorFunc: opt.ErrorFunc,
|
|
||||||
}
|
|
||||||
ctx.MapTo(x, (*CSRF)(nil))
|
|
||||||
|
|
||||||
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
x.ID = "0"
|
|
||||||
uid := sess.Get(opt.SessionKey)
|
|
||||||
if uid != nil {
|
|
||||||
x.ID = com.ToStr(uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
needsNew := false
|
|
||||||
oldUid := sess.Get(opt.oldSeesionKey)
|
|
||||||
if oldUid == nil || oldUid.(string) != x.ID {
|
|
||||||
needsNew = true
|
|
||||||
sess.Set(opt.oldSeesionKey, x.ID)
|
|
||||||
} else {
|
|
||||||
// If cookie present, map existing token, else generate a new one.
|
|
||||||
if val := ctx.GetCookie(opt.Cookie); len(val) > 0 {
|
|
||||||
// FIXME: test coverage.
|
|
||||||
x.Token = val
|
|
||||||
} else {
|
|
||||||
needsNew = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if needsNew {
|
|
||||||
// FIXME: actionId.
|
|
||||||
x.Token = GenerateToken(x.Secret, x.ID, "POST")
|
|
||||||
if opt.SetCookie {
|
|
||||||
ctx.SetCookie(opt.Cookie, x.Token, 0, opt.CookiePath, opt.CookieDomain, opt.Secure, opt.CookieHttpOnly, time.Now().AddDate(0, 0, 1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.SetHeader {
|
|
||||||
ctx.Resp.Header().Add(opt.Header, x.Token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Csrfer maps CSRF to each request. If this request is a Get request, it will generate a new token.
|
// Csrfer maps CSRF to each request. If this request is a Get request, it will generate a new token.
|
||||||
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
|
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
|
||||||
func Csrfer(options ...Options) macaron.Handler {
|
func Csrfer(opt CsrfOptions, ctx *Context) CSRF {
|
||||||
return Generate(options...)
|
opt = prepareOptions([]CsrfOptions{opt})
|
||||||
|
x := &csrf{
|
||||||
|
Secret: opt.Secret,
|
||||||
|
Header: opt.Header,
|
||||||
|
Form: opt.Form,
|
||||||
|
Cookie: opt.Cookie,
|
||||||
|
CookieDomain: opt.CookieDomain,
|
||||||
|
CookiePath: opt.CookiePath,
|
||||||
|
CookieHTTPOnly: opt.CookieHTTPOnly,
|
||||||
|
ErrorFunc: opt.ErrorFunc,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
x.ID = "0"
|
||||||
|
uid := ctx.Session.Get(opt.SessionKey)
|
||||||
|
if uid != nil {
|
||||||
|
x.ID = com.ToStr(uid)
|
||||||
|
}
|
||||||
|
|
||||||
|
needsNew := false
|
||||||
|
oldUID := ctx.Session.Get(opt.oldSessionKey)
|
||||||
|
if oldUID == nil || oldUID.(string) != x.ID {
|
||||||
|
needsNew = true
|
||||||
|
_ = ctx.Session.Set(opt.oldSessionKey, x.ID)
|
||||||
|
} else {
|
||||||
|
// If cookie present, map existing token, else generate a new one.
|
||||||
|
if val := ctx.GetCookie(opt.Cookie); len(val) > 0 {
|
||||||
|
// FIXME: test coverage.
|
||||||
|
x.Token = val
|
||||||
|
} else {
|
||||||
|
needsNew = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if needsNew {
|
||||||
|
// FIXME: actionId.
|
||||||
|
x.Token = GenerateToken(x.Secret, x.ID, "POST")
|
||||||
|
if opt.SetCookie {
|
||||||
|
var expires interface{}
|
||||||
|
if opt.CookieLifeTime == 0 {
|
||||||
|
expires = time.Now().AddDate(0, 0, 1)
|
||||||
|
}
|
||||||
|
ctx.SetCookie(opt.Cookie, x.Token, opt.CookieLifeTime, opt.CookiePath, opt.CookieDomain, opt.Secure, opt.CookieHTTPOnly, expires,
|
||||||
|
func(c *http.Cookie) {
|
||||||
|
c.SameSite = opt.SameSite
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.SetHeader {
|
||||||
|
ctx.Resp.Header().Add(opt.Header, x.Token)
|
||||||
|
}
|
||||||
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate should be used as a per route middleware. It attempts to get a token from a "X-CSRFToken"
|
// Validate should be used as a per route middleware. It attempts to get a token from a "X-CSRFToken"
|
||||||
// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated
|
// HTTP header and then a "_csrf" form value. If one of these is found, the token will be validated
|
||||||
// using ValidToken. If this validation fails, custom Error is sent in the reply.
|
// using ValidToken. If this validation fails, custom Error is sent in the reply.
|
||||||
// If neither a header or form value is found, http.StatusBadRequest is sent.
|
// If neither a header or form value is found, http.StatusBadRequest is sent.
|
||||||
func Validate(ctx *macaron.Context, x CSRF) {
|
func Validate(ctx *Context, x CSRF) {
|
||||||
if token := ctx.Req.Header.Get(x.GetHeaderName()); len(token) > 0 {
|
if token := ctx.Req.Header.Get(x.GetHeaderName()); len(token) > 0 {
|
||||||
if !x.ValidToken(token) {
|
if !x.ValidToken(token) {
|
||||||
ctx.SetCookie(x.GetCookieName(), "", -1, x.GetCookiePath())
|
ctx.SetCookie(x.GetCookieName(), "", -1, x.GetCookiePath())
|
227
modules/context/form.go
Normal file
227
modules/context/form.go
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Forms a new enhancement of http.Request
|
||||||
|
type Forms http.Request
|
||||||
|
|
||||||
|
// Values returns http.Request values
|
||||||
|
func (f *Forms) Values() url.Values {
|
||||||
|
return (*http.Request)(f).Form
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns request form as string
|
||||||
|
func (f *Forms) String(key string) (string, error) {
|
||||||
|
return (*http.Request)(f).FormValue(key), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trimmed returns request form as string with trimed spaces left and right
|
||||||
|
func (f *Forms) Trimmed(key string) (string, error) {
|
||||||
|
return strings.TrimSpace((*http.Request)(f).FormValue(key)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strings returns request form as strings
|
||||||
|
func (f *Forms) Strings(key string) ([]string, error) {
|
||||||
|
if (*http.Request)(f).Form == nil {
|
||||||
|
if err := (*http.Request)(f).ParseMultipartForm(32 << 20); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, ok := (*http.Request)(f).Form[key]; ok {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escape returns request form as escaped string
|
||||||
|
func (f *Forms) Escape(key string) (string, error) {
|
||||||
|
return template.HTMLEscapeString((*http.Request)(f).FormValue(key)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int returns request form as int
|
||||||
|
func (f *Forms) Int(key string) (int, error) {
|
||||||
|
return strconv.Atoi((*http.Request)(f).FormValue(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32 returns request form as int32
|
||||||
|
func (f *Forms) Int32(key string) (int32, error) {
|
||||||
|
v, err := strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 32)
|
||||||
|
return int32(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int64 returns request form as int64
|
||||||
|
func (f *Forms) Int64(key string) (int64, error) {
|
||||||
|
return strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint returns request form as uint
|
||||||
|
func (f *Forms) Uint(key string) (uint, error) {
|
||||||
|
v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64)
|
||||||
|
return uint(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32 returns request form as uint32
|
||||||
|
func (f *Forms) Uint32(key string) (uint32, error) {
|
||||||
|
v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 32)
|
||||||
|
return uint32(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64 returns request form as uint64
|
||||||
|
func (f *Forms) Uint64(key string) (uint64, error) {
|
||||||
|
return strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool returns request form as bool
|
||||||
|
func (f *Forms) Bool(key string) (bool, error) {
|
||||||
|
return strconv.ParseBool((*http.Request)(f).FormValue(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32 returns request form as float32
|
||||||
|
func (f *Forms) Float32(key string) (float32, error) {
|
||||||
|
v, err := strconv.ParseFloat((*http.Request)(f).FormValue(key), 64)
|
||||||
|
return float32(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float64 returns request form as float64
|
||||||
|
func (f *Forms) Float64(key string) (float64, error) {
|
||||||
|
return strconv.ParseFloat((*http.Request)(f).FormValue(key), 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustString returns request form as string with default
|
||||||
|
func (f *Forms) MustString(key string, defaults ...string) string {
|
||||||
|
if v := (*http.Request)(f).FormValue(key); len(v) > 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustTrimmed returns request form as string with default
|
||||||
|
func (f *Forms) MustTrimmed(key string, defaults ...string) string {
|
||||||
|
return strings.TrimSpace(f.MustString(key, defaults...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustStrings returns request form as strings with default
|
||||||
|
func (f *Forms) MustStrings(key string, defaults ...[]string) []string {
|
||||||
|
if (*http.Request)(f).Form == nil {
|
||||||
|
if err := (*http.Request)(f).ParseMultipartForm(32 << 20); err != nil {
|
||||||
|
log.Error("ParseMultipartForm: %v", err)
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := (*http.Request)(f).Form[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustEscape returns request form as escaped string with default
|
||||||
|
func (f *Forms) MustEscape(key string, defaults ...string) string {
|
||||||
|
if v := (*http.Request)(f).FormValue(key); len(v) > 0 {
|
||||||
|
return template.HTMLEscapeString(v)
|
||||||
|
}
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt returns request form as int with default
|
||||||
|
func (f *Forms) MustInt(key string, defaults ...int) int {
|
||||||
|
v, err := strconv.Atoi((*http.Request)(f).FormValue(key))
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt32 returns request form as int32 with default
|
||||||
|
func (f *Forms) MustInt32(key string, defaults ...int32) int32 {
|
||||||
|
v, err := strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 32)
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return int32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustInt64 returns request form as int64 with default
|
||||||
|
func (f *Forms) MustInt64(key string, defaults ...int64) int64 {
|
||||||
|
v, err := strconv.ParseInt((*http.Request)(f).FormValue(key), 10, 64)
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustUint returns request form as uint with default
|
||||||
|
func (f *Forms) MustUint(key string, defaults ...uint) uint {
|
||||||
|
v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64)
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return uint(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustUint32 returns request form as uint32 with default
|
||||||
|
func (f *Forms) MustUint32(key string, defaults ...uint32) uint32 {
|
||||||
|
v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 32)
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return uint32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustUint64 returns request form as uint64 with default
|
||||||
|
func (f *Forms) MustUint64(key string, defaults ...uint64) uint64 {
|
||||||
|
v, err := strconv.ParseUint((*http.Request)(f).FormValue(key), 10, 64)
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustFloat32 returns request form as float32 with default
|
||||||
|
func (f *Forms) MustFloat32(key string, defaults ...float32) float32 {
|
||||||
|
v, err := strconv.ParseFloat((*http.Request)(f).FormValue(key), 32)
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return float32(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustFloat64 returns request form as float64 with default
|
||||||
|
func (f *Forms) MustFloat64(key string, defaults ...float64) float64 {
|
||||||
|
v, err := strconv.ParseFloat((*http.Request)(f).FormValue(key), 64)
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustBool returns request form as bool with default
|
||||||
|
func (f *Forms) MustBool(key string, defaults ...bool) bool {
|
||||||
|
v, err := strconv.ParseBool((*http.Request)(f).FormValue(key))
|
||||||
|
if len(defaults) > 0 && err != nil {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
|
@ -10,8 +10,6 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Organization contains organization context
|
// Organization contains organization context
|
||||||
|
@ -173,7 +171,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrgAssignment returns a macaron middleware to handle organization assignment
|
// OrgAssignment returns a macaron middleware to handle organization assignment
|
||||||
func OrgAssignment(args ...bool) macaron.Handler {
|
func OrgAssignment(args ...bool) func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
HandleOrgAssignment(ctx, args...)
|
HandleOrgAssignment(ctx, args...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,10 @@ package context
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequireRepoAdmin returns a macaron middleware for requiring repository admin permission
|
// RequireRepoAdmin returns a macaron middleware for requiring repository admin permission
|
||||||
func RequireRepoAdmin() macaron.Handler {
|
func RequireRepoAdmin() func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
|
if !ctx.IsSigned || !ctx.Repo.IsAdmin() {
|
||||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||||
|
@ -22,7 +20,7 @@ func RequireRepoAdmin() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireRepoWriter returns a macaron middleware for requiring repository write to the specify unitType
|
// RequireRepoWriter returns a macaron middleware for requiring repository write to the specify unitType
|
||||||
func RequireRepoWriter(unitType models.UnitType) macaron.Handler {
|
func RequireRepoWriter(unitType models.UnitType) func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
if !ctx.Repo.CanWrite(unitType) {
|
if !ctx.Repo.CanWrite(unitType) {
|
||||||
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
|
||||||
|
@ -32,7 +30,7 @@ func RequireRepoWriter(unitType models.UnitType) macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireRepoWriterOr returns a macaron middleware for requiring repository write to one of the unit permission
|
// RequireRepoWriterOr returns a macaron middleware for requiring repository write to one of the unit permission
|
||||||
func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler {
|
func RequireRepoWriterOr(unitTypes ...models.UnitType) func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
for _, unitType := range unitTypes {
|
for _, unitType := range unitTypes {
|
||||||
if ctx.Repo.CanWrite(unitType) {
|
if ctx.Repo.CanWrite(unitType) {
|
||||||
|
@ -44,7 +42,7 @@ func RequireRepoWriterOr(unitTypes ...models.UnitType) macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireRepoReader returns a macaron middleware for requiring repository read to the specify unitType
|
// RequireRepoReader returns a macaron middleware for requiring repository read to the specify unitType
|
||||||
func RequireRepoReader(unitType models.UnitType) macaron.Handler {
|
func RequireRepoReader(unitType models.UnitType) func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
if !ctx.Repo.CanRead(unitType) {
|
if !ctx.Repo.CanRead(unitType) {
|
||||||
if log.IsTrace() {
|
if log.IsTrace() {
|
||||||
|
@ -70,7 +68,7 @@ func RequireRepoReader(unitType models.UnitType) macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireRepoReaderOr returns a macaron middleware for requiring repository write to one of the unit permission
|
// RequireRepoReaderOr returns a macaron middleware for requiring repository write to one of the unit permission
|
||||||
func RequireRepoReaderOr(unitTypes ...models.UnitType) macaron.Handler {
|
func RequireRepoReaderOr(unitTypes ...models.UnitType) func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
for _, unitType := range unitTypes {
|
for _, unitType := range unitTypes {
|
||||||
if ctx.Repo.CanRead(unitType) {
|
if ctx.Repo.CanRead(unitType) {
|
||||||
|
|
45
modules/context/private.go
Normal file
45
modules/context/private.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrivateContext represents a context for private routes
|
||||||
|
type PrivateContext struct {
|
||||||
|
*Context
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
privateContextKey interface{} = "default_private_context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WithPrivateContext set up private context in request
|
||||||
|
func WithPrivateContext(req *http.Request, ctx *PrivateContext) *http.Request {
|
||||||
|
return req.WithContext(context.WithValue(req.Context(), privateContextKey, ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPrivateContext returns a context for Private routes
|
||||||
|
func GetPrivateContext(req *http.Request) *PrivateContext {
|
||||||
|
return req.Context().Value(privateContextKey).(*PrivateContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateContexter returns apicontext as macaron 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) {
|
||||||
|
ctx := &PrivateContext{
|
||||||
|
Context: &Context{
|
||||||
|
Resp: NewResponse(w),
|
||||||
|
Data: map[string]interface{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx.Req = WithPrivateContext(req, ctx)
|
||||||
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ package context
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -21,7 +22,6 @@ import (
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
"github.com/editorconfig/editorconfig-core-go/v2"
|
"github.com/editorconfig/editorconfig-core-go/v2"
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
)
|
)
|
||||||
|
@ -81,7 +81,7 @@ func (r *Repository) CanCreateBranch() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoMustNotBeArchived checks if a repo is archived
|
// RepoMustNotBeArchived checks if a repo is archived
|
||||||
func RepoMustNotBeArchived() macaron.Handler {
|
func RepoMustNotBeArchived() func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
if ctx.Repo.Repository.IsArchived {
|
if ctx.Repo.Repository.IsArchived {
|
||||||
ctx.NotFound("IsArchived", fmt.Errorf(ctx.Tr("repo.archive.title")))
|
ctx.NotFound("IsArchived", fmt.Errorf(ctx.Tr("repo.archive.title")))
|
||||||
|
@ -374,7 +374,7 @@ func repoAssignment(ctx *Context, repo *models.Repository) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoIDAssignment returns a macaron handler which assigns the repo to the context.
|
// RepoIDAssignment returns a macaron handler which assigns the repo to the context.
|
||||||
func RepoIDAssignment() macaron.Handler {
|
func RepoIDAssignment() func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
repoID := ctx.ParamsInt64(":repoid")
|
repoID := ctx.ParamsInt64(":repoid")
|
||||||
|
|
||||||
|
@ -394,223 +394,220 @@ func RepoIDAssignment() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RepoAssignment returns a macaron to handle repository assignment
|
// RepoAssignment returns a macaron to handle repository assignment
|
||||||
func RepoAssignment() macaron.Handler {
|
func RepoAssignment() func(http.Handler) http.Handler {
|
||||||
return func(ctx *Context) {
|
return func(next http.Handler) http.Handler {
|
||||||
var (
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
owner *models.User
|
var (
|
||||||
err error
|
owner *models.User
|
||||||
)
|
err error
|
||||||
|
ctx = GetContext(req)
|
||||||
|
)
|
||||||
|
|
||||||
userName := ctx.Params(":username")
|
userName := ctx.Params(":username")
|
||||||
repoName := ctx.Params(":reponame")
|
repoName := ctx.Params(":reponame")
|
||||||
|
repoName = strings.TrimSuffix(repoName, ".git")
|
||||||
|
|
||||||
// Check if the user is the same as the repository owner
|
// Check if the user is the same as the repository owner
|
||||||
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
|
if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
|
||||||
owner = ctx.User
|
owner = ctx.User
|
||||||
} else {
|
} else {
|
||||||
owner, err = models.GetUserByName(userName)
|
owner, err = models.GetUserByName(userName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrUserNotExist(err) {
|
if models.IsErrUserNotExist(err) {
|
||||||
redirectUserID, err := models.LookupUserRedirect(userName)
|
|
||||||
if err == nil {
|
|
||||||
RedirectToUser(ctx, userName, redirectUserID)
|
|
||||||
} else if models.IsErrUserRedirectNotExist(err) {
|
|
||||||
if ctx.Query("go-get") == "1" {
|
if ctx.Query("go-get") == "1" {
|
||||||
EarlyResponseForGoGetMeta(ctx)
|
EarlyResponseForGoGetMeta(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.NotFound("GetUserByName", nil)
|
ctx.NotFound("GetUserByName", nil)
|
||||||
} else {
|
} else {
|
||||||
ctx.ServerError("LookupUserRedirect", err)
|
ctx.ServerError("GetUserByName", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.Repo.Owner = owner
|
||||||
|
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||||
|
|
||||||
|
// Get repository.
|
||||||
|
repo, err := models.GetRepositoryByName(owner.ID, repoName)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrRepoNotExist(err) {
|
||||||
|
redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName)
|
||||||
|
if err == nil {
|
||||||
|
RedirectToRepo(ctx, redirectRepoID)
|
||||||
|
} else if models.IsErrRepoRedirectNotExist(err) {
|
||||||
|
if ctx.Query("go-get") == "1" {
|
||||||
|
EarlyResponseForGoGetMeta(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.NotFound("GetRepositoryByName", nil)
|
||||||
|
} else {
|
||||||
|
ctx.ServerError("LookupRepoRedirect", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.ServerError("GetUserByName", err)
|
ctx.ServerError("GetRepositoryByName", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
repo.Owner = owner
|
||||||
ctx.Repo.Owner = owner
|
|
||||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
|
||||||
|
|
||||||
// Get repository.
|
repoAssignment(ctx, repo)
|
||||||
repo, err := models.GetRepositoryByName(owner.ID, repoName)
|
if ctx.Written() {
|
||||||
if err != nil {
|
return
|
||||||
if models.IsErrRepoNotExist(err) {
|
|
||||||
redirectRepoID, err := models.LookupRepoRedirect(owner.ID, repoName)
|
|
||||||
if err == nil {
|
|
||||||
RedirectToRepo(ctx, redirectRepoID)
|
|
||||||
} else if models.IsErrRepoRedirectNotExist(err) {
|
|
||||||
if ctx.Query("go-get") == "1" {
|
|
||||||
EarlyResponseForGoGetMeta(ctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.NotFound("GetRepositoryByName", nil)
|
|
||||||
} else {
|
|
||||||
ctx.ServerError("LookupRepoRedirect", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.ServerError("GetRepositoryByName", err)
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
repo.Owner = owner
|
|
||||||
|
|
||||||
repoAssignment(ctx, repo)
|
ctx.Repo.RepoLink = repo.Link()
|
||||||
if ctx.Written() {
|
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
|
||||||
return
|
ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Repo.RepoLink = repo.Link()
|
unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalTracker)
|
||||||
ctx.Data["RepoLink"] = ctx.Repo.RepoLink
|
if err == nil {
|
||||||
ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
|
ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
|
||||||
|
}
|
||||||
|
|
||||||
unit, err := ctx.Repo.Repository.GetUnit(models.UnitTypeExternalTracker)
|
ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
|
||||||
if err == nil {
|
IncludeTags: true,
|
||||||
ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
|
})
|
||||||
}
|
if err != nil {
|
||||||
|
ctx.ServerError("GetReleaseCountByRepoID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{})
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetReleaseCountByRepoID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
|
ctx.Data["Title"] = owner.Name + "/" + repo.Name
|
||||||
IncludeTags: true,
|
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(models.UnitTypeCode)
|
||||||
|
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues)
|
||||||
|
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
|
||||||
|
|
||||||
|
if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil {
|
||||||
|
ctx.ServerError("CanUserFork", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
||||||
|
ctx.Data["ExposeAnonSSH"] = setting.SSH.ExposeAnonymous
|
||||||
|
ctx.Data["DisableHTTP"] = setting.Repository.DisableHTTPGit
|
||||||
|
ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled
|
||||||
|
ctx.Data["CloneLink"] = repo.CloneLink()
|
||||||
|
ctx.Data["WikiCloneLink"] = repo.WikiCloneLink()
|
||||||
|
|
||||||
|
if ctx.IsSigned {
|
||||||
|
ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID)
|
||||||
|
ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.IsFork {
|
||||||
|
RetrieveBaseRepo(ctx, repo)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.IsGenerated() {
|
||||||
|
RetrieveTemplateRepo(ctx, repo)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable everything when the repo is being created
|
||||||
|
if ctx.Repo.Repository.IsBeingCreated() {
|
||||||
|
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Repo.GitRepo = gitRepo
|
||||||
|
|
||||||
|
// We opened it, we should close it
|
||||||
|
defer 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
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, err := ctx.Repo.GitRepo.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetTags", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Tags"] = tags
|
||||||
|
|
||||||
|
brs, err := ctx.Repo.GitRepo.GetBranches()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Branches"] = brs
|
||||||
|
ctx.Data["BranchesCount"] = len(brs)
|
||||||
|
|
||||||
|
ctx.Data["TagName"] = ctx.Repo.TagName
|
||||||
|
|
||||||
|
// If not branch selected, try default one.
|
||||||
|
// If default branch doesn't exists, fall back to some other branch.
|
||||||
|
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 if len(brs) > 0 {
|
||||||
|
ctx.Repo.BranchName = brs[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||||
|
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||||
|
|
||||||
|
// People who have push access or have forked repository can propose a new pull request.
|
||||||
|
canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(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() {
|
||||||
|
canCompare = true
|
||||||
|
ctx.Data["BaseRepo"] = repo.BaseRepo
|
||||||
|
ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
|
||||||
|
ctx.Repo.PullRequest.Allowed = canPush
|
||||||
|
ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName
|
||||||
|
} else if repo.AllowsPulls() {
|
||||||
|
// 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.HeadInfo = ctx.Repo.BranchName
|
||||||
|
}
|
||||||
|
ctx.Data["CanCompareOrPull"] = canCompare
|
||||||
|
ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
|
||||||
|
|
||||||
|
if ctx.Query("go-get") == "1" {
|
||||||
|
ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
|
||||||
|
prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName)
|
||||||
|
ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
|
||||||
|
ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, req)
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetReleaseCountByRepoID", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{})
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetReleaseCountByRepoID", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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(models.UnitTypeCode)
|
|
||||||
ctx.Data["CanWriteIssues"] = ctx.Repo.CanWrite(models.UnitTypeIssues)
|
|
||||||
ctx.Data["CanWritePulls"] = ctx.Repo.CanWrite(models.UnitTypePullRequests)
|
|
||||||
|
|
||||||
if ctx.Data["CanSignedUserFork"], err = ctx.Repo.Repository.CanUserFork(ctx.User); err != nil {
|
|
||||||
ctx.ServerError("CanUserFork", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Data["DisableSSH"] = setting.SSH.Disabled
|
|
||||||
ctx.Data["ExposeAnonSSH"] = setting.SSH.ExposeAnonymous
|
|
||||||
ctx.Data["DisableHTTP"] = setting.Repository.DisableHTTPGit
|
|
||||||
ctx.Data["RepoSearchEnabled"] = setting.Indexer.RepoIndexerEnabled
|
|
||||||
ctx.Data["CloneLink"] = repo.CloneLink()
|
|
||||||
ctx.Data["WikiCloneLink"] = repo.WikiCloneLink()
|
|
||||||
|
|
||||||
if ctx.IsSigned {
|
|
||||||
ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID)
|
|
||||||
ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.IsFork {
|
|
||||||
RetrieveBaseRepo(ctx, repo)
|
|
||||||
if ctx.Written() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if repo.IsGenerated() {
|
|
||||||
RetrieveTemplateRepo(ctx, repo)
|
|
||||||
if ctx.Written() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable everything when the repo is being created
|
|
||||||
if ctx.Repo.Repository.IsBeingCreated() {
|
|
||||||
ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Repo.GitRepo = gitRepo
|
|
||||||
|
|
||||||
// We opened it, we should close it
|
|
||||||
defer 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
|
|
||||||
ctx.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, err := ctx.Repo.GitRepo.GetTags()
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetTags", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["Tags"] = tags
|
|
||||||
|
|
||||||
brs, err := ctx.Repo.GitRepo.GetBranches()
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetBranches", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Data["Branches"] = brs
|
|
||||||
ctx.Data["BranchesCount"] = len(brs)
|
|
||||||
|
|
||||||
ctx.Data["TagName"] = ctx.Repo.TagName
|
|
||||||
|
|
||||||
// If not branch selected, try default one.
|
|
||||||
// If default branch doesn't exists, fall back to some other branch.
|
|
||||||
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 if len(brs) > 0 {
|
|
||||||
ctx.Repo.BranchName = brs[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
|
||||||
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
|
||||||
|
|
||||||
// People who have push access or have forked repository can propose a new pull request.
|
|
||||||
canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(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() {
|
|
||||||
canCompare = true
|
|
||||||
ctx.Data["BaseRepo"] = repo.BaseRepo
|
|
||||||
ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo
|
|
||||||
ctx.Repo.PullRequest.Allowed = canPush
|
|
||||||
ctx.Repo.PullRequest.HeadInfo = ctx.Repo.Owner.Name + ":" + ctx.Repo.BranchName
|
|
||||||
} else if repo.AllowsPulls() {
|
|
||||||
// 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.HeadInfo = ctx.Repo.BranchName
|
|
||||||
}
|
|
||||||
ctx.Data["CanCompareOrPull"] = canCompare
|
|
||||||
ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest
|
|
||||||
|
|
||||||
if ctx.Query("go-get") == "1" {
|
|
||||||
ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
|
|
||||||
prefix := setting.AppURL + path.Join(owner.Name, repo.Name, "src", "branch", ctx.Repo.BranchName)
|
|
||||||
ctx.Data["GoDocDirectory"] = prefix + "{/dir}"
|
|
||||||
ctx.Data["GoDocFile"] = prefix + "{/dir}/{file}#L{line}"
|
|
||||||
}
|
|
||||||
ctx.Next()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -636,7 +633,7 @@ const (
|
||||||
|
|
||||||
// RepoRef handles repository reference names when the ref name is not
|
// RepoRef handles repository reference names when the ref name is not
|
||||||
// explicitly given
|
// explicitly given
|
||||||
func RepoRef() macaron.Handler {
|
func RepoRef() func(http.Handler) http.Handler {
|
||||||
// since no ref name is explicitly specified, ok to just use branch
|
// since no ref name is explicitly specified, ok to just use branch
|
||||||
return RepoRefByType(RepoRefBranch)
|
return RepoRefByType(RepoRefBranch)
|
||||||
}
|
}
|
||||||
|
@ -715,132 +712,135 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
|
||||||
|
|
||||||
// RepoRefByType handles repository reference name for a specific type
|
// RepoRefByType handles repository reference name for a specific type
|
||||||
// of repository reference
|
// of repository reference
|
||||||
func RepoRefByType(refType RepoRefType) macaron.Handler {
|
func RepoRefByType(refType RepoRefType) func(http.Handler) http.Handler {
|
||||||
return func(ctx *Context) {
|
return func(next http.Handler) http.Handler {
|
||||||
// Empty repository does not have reference information.
|
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
ctx := GetContext(req)
|
||||||
return
|
// Empty repository does not have reference information.
|
||||||
}
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
|
||||||
var (
|
|
||||||
refName string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if ctx.Repo.GitRepo == nil {
|
|
||||||
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
|
||||||
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("RepoRef Invalid repo "+repoPath, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// We opened it, we should close it
|
|
||||||
defer 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.
|
var (
|
||||||
if len(ctx.Params("*")) == 0 {
|
refName string
|
||||||
refName = ctx.Repo.Repository.DefaultBranch
|
err error
|
||||||
ctx.Repo.BranchName = refName
|
)
|
||||||
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
|
||||||
brs, err := ctx.Repo.GitRepo.GetBranches()
|
if ctx.Repo.GitRepo == nil {
|
||||||
|
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||||
|
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranches", err)
|
ctx.ServerError("RepoRef Invalid repo "+repoPath, err)
|
||||||
return
|
|
||||||
} else if len(brs) == 0 {
|
|
||||||
err = fmt.Errorf("No branches in non-empty repository %s",
|
|
||||||
ctx.Repo.GitRepo.Path)
|
|
||||||
ctx.ServerError("GetBranches", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
refName = brs[0]
|
// We opened it, we should close it
|
||||||
|
defer func() {
|
||||||
|
// If it's been set to nil then assume someone else has closed it.
|
||||||
|
if ctx.Repo.GitRepo != nil {
|
||||||
|
ctx.Repo.GitRepo.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetBranchCommit", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
|
||||||
ctx.Repo.IsViewBranch = true
|
|
||||||
|
|
||||||
} else {
|
|
||||||
refName = getRefName(ctx, refType)
|
|
||||||
ctx.Repo.BranchName = refName
|
|
||||||
if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
|
|
||||||
ctx.Repo.IsViewBranch = true
|
|
||||||
|
|
||||||
|
// Get default branch.
|
||||||
|
if len(ctx.Params("*")) == 0 {
|
||||||
|
refName = ctx.Repo.Repository.DefaultBranch
|
||||||
|
ctx.Repo.BranchName = refName
|
||||||
|
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||||
|
brs, err := ctx.Repo.GitRepo.GetBranches()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetBranches", err)
|
||||||
|
return
|
||||||
|
} else if len(brs) == 0 {
|
||||||
|
err = fmt.Errorf("No branches in non-empty repository %s",
|
||||||
|
ctx.Repo.GitRepo.Path)
|
||||||
|
ctx.ServerError("GetBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
refName = brs[0]
|
||||||
|
}
|
||||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranchCommit", err)
|
ctx.ServerError("GetBranchCommit", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||||
|
ctx.Repo.IsViewBranch = true
|
||||||
|
|
||||||
} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
|
|
||||||
ctx.Repo.IsViewTag = true
|
|
||||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
|
|
||||||
if err != nil {
|
|
||||||
ctx.ServerError("GetTagCommit", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
|
||||||
} else if len(refName) >= 7 && len(refName) <= 40 {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
// If short commit ID add canonical link header
|
|
||||||
if len(refName) < 40 {
|
|
||||||
ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
|
|
||||||
util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), refName, ctx.Repo.Commit.ID.String(), 1))))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
|
refName = getRefName(ctx, refType)
|
||||||
return
|
ctx.Repo.BranchName = refName
|
||||||
|
if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||||
|
ctx.Repo.IsViewBranch = true
|
||||||
|
|
||||||
|
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetBranchCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||||
|
|
||||||
|
} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||||
|
ctx.Repo.IsViewTag = true
|
||||||
|
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetTagCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||||
|
} else if len(refName) >= 7 && len(refName) <= 40 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
// If short commit ID add canonical link header
|
||||||
|
if len(refName) < 40 {
|
||||||
|
ctx.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
|
||||||
|
util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), refName, ctx.Repo.Commit.ID.String(), 1))))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if refType == RepoRefLegacy {
|
||||||
|
// redirect from old URL scheme to new URL scheme
|
||||||
|
ctx.Redirect(path.Join(
|
||||||
|
setting.AppSubURL,
|
||||||
|
strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
|
||||||
|
ctx.Repo.BranchNameSubURL(),
|
||||||
|
ctx.Repo.TreePath))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if refType == RepoRefLegacy {
|
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||||
// redirect from old URL scheme to new URL scheme
|
ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
|
||||||
ctx.Redirect(path.Join(
|
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||||
setting.AppSubURL,
|
ctx.Data["TreePath"] = ctx.Repo.TreePath
|
||||||
strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
|
ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
|
||||||
ctx.Repo.BranchNameSubURL(),
|
ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag
|
||||||
ctx.Repo.TreePath))
|
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
|
return
|
||||||
}
|
}
|
||||||
}
|
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
||||||
|
|
||||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
next.ServeHTTP(w, req)
|
||||||
ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
|
})
|
||||||
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
|
|
||||||
}
|
|
||||||
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
|
|
||||||
|
|
||||||
ctx.Next()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitHookService checks if repository Git hooks service has been enabled.
|
// GitHookService checks if repository Git hooks service has been enabled.
|
||||||
func GitHookService() macaron.Handler {
|
func GitHookService() func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
if !ctx.User.CanEditGitHook() {
|
if !ctx.User.CanEditGitHook() {
|
||||||
ctx.NotFound("GitHookService", nil)
|
ctx.NotFound("GitHookService", nil)
|
||||||
|
@ -850,7 +850,7 @@ func GitHookService() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnitTypes returns a macaron middleware to set unit types to context variables.
|
// UnitTypes returns a macaron middleware to set unit types to context variables.
|
||||||
func UnitTypes() macaron.Handler {
|
func UnitTypes() func(ctx *Context) {
|
||||||
return func(ctx *Context) {
|
return func(ctx *Context) {
|
||||||
ctx.Data["UnitTypeCode"] = models.UnitTypeCode
|
ctx.Data["UnitTypeCode"] = models.UnitTypeCode
|
||||||
ctx.Data["UnitTypeIssues"] = models.UnitTypeIssues
|
ctx.Data["UnitTypeIssues"] = models.UnitTypeIssues
|
||||||
|
|
|
@ -11,6 +11,7 @@ type ResponseWriter interface {
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
Flush()
|
Flush()
|
||||||
Status() int
|
Status() int
|
||||||
|
Before(func(ResponseWriter))
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -20,11 +21,19 @@ var (
|
||||||
// Response represents a response
|
// Response represents a response
|
||||||
type Response struct {
|
type Response struct {
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
status int
|
status int
|
||||||
|
befores []func(ResponseWriter)
|
||||||
|
beforeExecuted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write writes bytes to HTTP endpoint
|
// Write writes bytes to HTTP endpoint
|
||||||
func (r *Response) Write(bs []byte) (int, error) {
|
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)
|
size, err := r.ResponseWriter.Write(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -37,6 +46,12 @@ func (r *Response) Write(bs []byte) (int, error) {
|
||||||
|
|
||||||
// WriteHeader write status code
|
// WriteHeader write status code
|
||||||
func (r *Response) WriteHeader(statusCode int) {
|
func (r *Response) WriteHeader(statusCode int) {
|
||||||
|
if !r.beforeExecuted {
|
||||||
|
for _, before := range r.befores {
|
||||||
|
before(r)
|
||||||
|
}
|
||||||
|
r.beforeExecuted = true
|
||||||
|
}
|
||||||
r.status = statusCode
|
r.status = statusCode
|
||||||
r.ResponseWriter.WriteHeader(statusCode)
|
r.ResponseWriter.WriteHeader(statusCode)
|
||||||
}
|
}
|
||||||
|
@ -53,10 +68,20 @@ func (r *Response) Status() int {
|
||||||
return r.status
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
// NewResponse creates a response
|
// NewResponse creates a response
|
||||||
func NewResponse(resp http.ResponseWriter) *Response {
|
func NewResponse(resp http.ResponseWriter) *Response {
|
||||||
if v, ok := resp.(*Response); ok {
|
if v, ok := resp.(*Response); ok {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
return &Response{resp, 0}
|
return &Response{
|
||||||
|
ResponseWriter: resp,
|
||||||
|
status: 0,
|
||||||
|
befores: make([]func(ResponseWriter), 0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
100
modules/context/secret.go
Normal file
100
modules/context/secret.go
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSecret creates a new secret
|
||||||
|
func NewSecret() (string, error) {
|
||||||
|
return NewSecretWithLength(32)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSecretWithLength creates a new secret for a given length
|
||||||
|
func NewSecretWithLength(length int64) (string, error) {
|
||||||
|
return randomString(length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomBytes(len int64) ([]byte, error) {
|
||||||
|
b := make([]byte, len)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomString(len int64) (string, error) {
|
||||||
|
b, err := randomBytes(len)
|
||||||
|
return base64.URLEncoding.EncodeToString(b), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AesEncrypt encrypts text and given key with AES.
|
||||||
|
func AesEncrypt(key, text []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b := base64.StdEncoding.EncodeToString(text)
|
||||||
|
ciphertext := make([]byte, aes.BlockSize+len(b))
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfb := cipher.NewCFBEncrypter(block, iv)
|
||||||
|
cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b))
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AesDecrypt decrypts text and given key with AES.
|
||||||
|
func AesDecrypt(key, text []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(text) < aes.BlockSize {
|
||||||
|
return nil, errors.New("ciphertext too short")
|
||||||
|
}
|
||||||
|
iv := text[:aes.BlockSize]
|
||||||
|
text = text[aes.BlockSize:]
|
||||||
|
cfb := cipher.NewCFBDecrypter(block, iv)
|
||||||
|
cfb.XORKeyStream(text, text)
|
||||||
|
data, err := base64.StdEncoding.DecodeString(string(text))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptSecret encrypts a string with given key into a hex string
|
||||||
|
func EncryptSecret(key string, str string) (string, error) {
|
||||||
|
keyHash := sha256.Sum256([]byte(key))
|
||||||
|
plaintext := []byte(str)
|
||||||
|
ciphertext, err := AesEncrypt(keyHash[:], plaintext)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptSecret decrypts a previously encrypted hex string
|
||||||
|
func DecryptSecret(key string, cipherhex string) (string, error) {
|
||||||
|
keyHash := sha256.Sum256([]byte(key))
|
||||||
|
ciphertext, err := base64.StdEncoding.DecodeString(cipherhex)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
plaintext, err := AesDecrypt(keyHash[:], ciphertext)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(plaintext), nil
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||||
// Copyright 2014 The Macaron Authors
|
// Copyright 2014 The Macaron Authors
|
||||||
|
// Copyright 2020 The Gitea Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -13,7 +14,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package csrf
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -27,13 +28,13 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The duration that XSRF tokens are valid.
|
// Timeout represents the duration that XSRF tokens are valid.
|
||||||
// It is exported so clients may set cookie timeouts that match generated tokens.
|
// It is exported so clients may set cookie timeouts that match generated tokens.
|
||||||
const TIMEOUT = 24 * time.Hour
|
const Timeout = 24 * time.Hour
|
||||||
|
|
||||||
// clean sanitizes a string for inclusion in a token by replacing all ":"s.
|
// clean sanitizes a string for inclusion in a token by replacing all ":"s.
|
||||||
func clean(s string) string {
|
func clean(s string) string {
|
||||||
return strings.Replace(s, ":", "_", -1)
|
return strings.ReplaceAll(s, ":", "_")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateToken returns a URL-safe secure XSRF token that expires in 24 hours.
|
// GenerateToken returns a URL-safe secure XSRF token that expires in 24 hours.
|
||||||
|
@ -53,7 +54,7 @@ func generateTokenAtTime(key, userID, actionID string, now time.Time) string {
|
||||||
return base64.RawURLEncoding.EncodeToString([]byte(tok))
|
return base64.RawURLEncoding.EncodeToString([]byte(tok))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid returns true if token is a valid, unexpired token returned by Generate.
|
// ValidToken returns true if token is a valid, unexpired token returned by Generate.
|
||||||
func ValidToken(token, key, userID, actionID string) bool {
|
func ValidToken(token, key, userID, actionID string) bool {
|
||||||
return validTokenAtTime(token, key, userID, actionID, time.Now())
|
return validTokenAtTime(token, key, userID, actionID, time.Now())
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,7 @@ func validTokenAtTime(token, key, userID, actionID string, now time.Time) bool {
|
||||||
issueTime := time.Unix(0, nanos)
|
issueTime := time.Unix(0, nanos)
|
||||||
|
|
||||||
// Check that the token is not expired.
|
// Check that the token is not expired.
|
||||||
if now.Sub(issueTime) >= TIMEOUT {
|
if now.Sub(issueTime) >= Timeout {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
90
modules/context/xsrf_test.go
Normal file
90
modules/context/xsrf_test.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
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 := generateTokenAtTime(key, userID, actionID, now)
|
||||||
|
assert.True(t, validTokenAtTime(tok, key, userID, actionID, oneMinuteFromNow))
|
||||||
|
assert.True(t, validTokenAtTime(tok, key, userID, actionID, now.Add(Timeout-1*time.Nanosecond)))
|
||||||
|
assert.True(t, validTokenAtTime(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, generateTokenAtTime("foo:bar", "baz", "wah", now),
|
||||||
|
generateTokenAtTime("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(Timeout)},
|
||||||
|
{"More than 1 minute from the future", key, userID, actionID, now.Add(-1*time.Nanosecond - 1*time.Minute)},
|
||||||
|
}
|
||||||
|
|
||||||
|
tok := generateTokenAtTime(key, userID, actionID, now)
|
||||||
|
for _, itt := range invalidTokenTests {
|
||||||
|
assert.False(t, validTokenAtTime(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, validTokenAtTime(bdt.tok, key, userID, actionID, oneMinuteFromNow))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -2,11 +2,15 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.com/macaron/binding"
|
"net/http"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AdminCreateUserForm form for admin to create user
|
// AdminCreateUserForm form for admin to create user
|
||||||
|
@ -21,8 +25,9 @@ type AdminCreateUserForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates form fields
|
// Validate validates form fields
|
||||||
func (f *AdminCreateUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdminEditUserForm form for admin to create user
|
// AdminEditUserForm form for admin to create user
|
||||||
|
@ -47,8 +52,9 @@ type AdminEditUserForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates form fields
|
// Validate validates form fields
|
||||||
func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AdminEditUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdminDashboardForm form for admin dashboard operations
|
// AdminDashboardForm form for admin dashboard operations
|
||||||
|
@ -58,6 +64,7 @@ type AdminDashboardForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates form fields
|
// Validate validates form fields
|
||||||
func (f *AdminDashboardForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
|
@ -2,11 +2,15 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.com/macaron/binding"
|
"net/http"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthenticationForm form for authentication
|
// AuthenticationForm form for authentication
|
||||||
|
@ -65,6 +69,7 @@ type AuthenticationForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates fields
|
// Validate validates fields
|
||||||
func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AuthenticationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
|
@ -3,14 +3,17 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ________ .__ __ .__
|
// ________ .__ __ .__
|
||||||
|
@ -28,8 +31,9 @@ type CreateOrgForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOrgSettingForm form for updating organization settings
|
// UpdateOrgSettingForm form for updating organization settings
|
||||||
|
@ -45,8 +49,9 @@ type UpdateOrgSettingForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___________
|
// ___________
|
||||||
|
@ -67,6 +72,7 @@ type CreateTeamForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CreateTeamForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
|
@ -2,11 +2,15 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.com/macaron/binding"
|
"net/http"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewBranchForm form for creating a new branch
|
// NewBranchForm form for creating a new branch
|
||||||
|
@ -15,6 +19,7 @@ type NewBranchForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
|
@ -3,21 +3,23 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/structs"
|
"code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/routers/utils"
|
"code.gitea.io/gitea/routers/utils"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// _______________________________________ _________.______________________ _______________.___.
|
// _______________________________________ _________.______________________ _______________.___.
|
||||||
|
@ -52,8 +54,9 @@ type CreateRepoForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CreateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MigrateRepoForm form for migrating repository
|
// MigrateRepoForm form for migrating repository
|
||||||
|
@ -82,8 +85,9 @@ type MigrateRepoForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseRemoteAddr checks if given remote address is valid,
|
// ParseRemoteAddr checks if given remote address is valid,
|
||||||
|
@ -166,8 +170,9 @@ type RepoSettingForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// __________ .__
|
// __________ .__
|
||||||
|
@ -202,8 +207,9 @@ type ProtectBranchForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *ProtectBranchForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// __ __ ___. .__ .__ __
|
// __ __ ___. .__ .__ __
|
||||||
|
@ -263,8 +269,9 @@ type NewWebhookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGogshookForm form for creating gogs hook
|
// NewGogshookForm form for creating gogs hook
|
||||||
|
@ -276,8 +283,9 @@ type NewGogshookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewGogshookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSlackHookForm form for creating slack hook
|
// NewSlackHookForm form for creating slack hook
|
||||||
|
@ -291,8 +299,9 @@ type NewSlackHookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewSlackHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasInvalidChannel validates the channel name is in the right format
|
// HasInvalidChannel validates the channel name is in the right format
|
||||||
|
@ -309,8 +318,9 @@ type NewDiscordHookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewDiscordHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDingtalkHookForm form for creating dingtalk hook
|
// NewDingtalkHookForm form for creating dingtalk hook
|
||||||
|
@ -320,8 +330,9 @@ type NewDingtalkHookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewDingtalkHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTelegramHookForm form for creating telegram hook
|
// NewTelegramHookForm form for creating telegram hook
|
||||||
|
@ -332,8 +343,9 @@ type NewTelegramHookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewTelegramHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMatrixHookForm form for creating Matrix hook
|
// NewMatrixHookForm form for creating Matrix hook
|
||||||
|
@ -346,8 +358,9 @@ type NewMatrixHookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewMatrixHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMSTeamsHookForm form for creating MS Teams hook
|
// NewMSTeamsHookForm form for creating MS Teams hook
|
||||||
|
@ -357,8 +370,9 @@ type NewMSTeamsHookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewMSTeamsHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFeishuHookForm form for creating feishu hook
|
// NewFeishuHookForm form for creating feishu hook
|
||||||
|
@ -368,8 +382,9 @@ type NewFeishuHookForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewFeishuHookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// .___
|
// .___
|
||||||
|
@ -393,8 +408,9 @@ type CreateIssueForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCommentForm form for creating comment
|
// CreateCommentForm form for creating comment
|
||||||
|
@ -405,8 +421,9 @@ type CreateCommentForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CreateCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CreateCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReactionForm form for adding and removing reaction
|
// ReactionForm form for adding and removing reaction
|
||||||
|
@ -415,8 +432,9 @@ type ReactionForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *ReactionForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *ReactionForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssueLockForm form for locking an issue
|
// IssueLockForm form for locking an issue
|
||||||
|
@ -425,8 +443,9 @@ type IssueLockForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (i *IssueLockForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, i, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, i, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasValidReason checks to make sure that the reason submitted in
|
// HasValidReason checks to make sure that the reason submitted in
|
||||||
|
@ -489,8 +508,9 @@ type CreateMilestoneForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// .____ ___. .__
|
// .____ ___. .__
|
||||||
|
@ -509,8 +529,9 @@ type CreateLabelForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CreateLabelForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitializeLabelsForm form for initializing labels
|
// InitializeLabelsForm form for initializing labels
|
||||||
|
@ -519,8 +540,9 @@ type InitializeLabelsForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *InitializeLabelsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// __________ .__ .__ __________ __
|
// __________ .__ .__ __________ __
|
||||||
|
@ -542,8 +564,9 @@ type MergePullRequestForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *MergePullRequestForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeCommentForm form for adding code comments for PRs
|
// CodeCommentForm form for adding code comments for PRs
|
||||||
|
@ -559,8 +582,9 @@ type CodeCommentForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *CodeCommentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitReviewForm for submitting a finished code review
|
// SubmitReviewForm for submitting a finished code review
|
||||||
|
@ -571,8 +595,9 @@ type SubmitReviewForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *SubmitReviewForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReviewType will return the corresponding reviewtype for type
|
// ReviewType will return the corresponding reviewtype for type
|
||||||
|
@ -616,8 +641,9 @@ type NewReleaseForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditReleaseForm form for changing release
|
// EditReleaseForm form for changing release
|
||||||
|
@ -630,8 +656,9 @@ type EditReleaseForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *EditReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// __ __.__ __ .__
|
// __ __.__ __ .__
|
||||||
|
@ -650,8 +677,9 @@ type NewWikiForm struct {
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
// FIXME: use code generation to generate this method.
|
// FIXME: use code generation to generate this method.
|
||||||
func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___________ .___.__ __
|
// ___________ .___.__ __
|
||||||
|
@ -673,8 +701,9 @@ type EditRepoFileForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *EditRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditPreviewDiffForm form for changing preview diff
|
// EditPreviewDiffForm form for changing preview diff
|
||||||
|
@ -683,8 +712,9 @@ type EditPreviewDiffForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *EditPreviewDiffForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ____ ___ .__ .___
|
// ____ ___ .__ .___
|
||||||
|
@ -706,8 +736,9 @@ type UploadRepoFileForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *UploadRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveUploadFileForm form for removing uploaded file
|
// RemoveUploadFileForm form for removing uploaded file
|
||||||
|
@ -716,8 +747,9 @@ type RemoveUploadFileForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *RemoveUploadFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ________ .__ __
|
// ________ .__ __
|
||||||
|
@ -737,8 +769,9 @@ type DeleteRepoFileForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *DeleteRepoFileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ___________.__ ___________ __
|
// ___________.__ ___________ __
|
||||||
|
@ -755,8 +788,9 @@ type AddTimeManuallyForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *AddTimeManuallyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveTopicForm form for save topics for repository
|
// SaveTopicForm form for save topics for repository
|
||||||
|
@ -770,6 +804,7 @@ type DeadlineForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
|
@ -3,16 +3,18 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// InstallForm form for installation page
|
// InstallForm form for installation page
|
||||||
|
@ -65,8 +67,9 @@ type InstallForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// _____ ____ _________________ ___
|
// _____ ____ _________________ ___
|
||||||
|
@ -87,8 +90,9 @@ type RegisterForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsEmailDomainWhitelisted validates that the email address
|
// IsEmailDomainWhitelisted validates that the email address
|
||||||
|
@ -124,8 +128,9 @@ type MustChangePasswordForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *MustChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *MustChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignInForm form for signing in with user/password
|
// SignInForm form for signing in with user/password
|
||||||
|
@ -137,8 +142,9 @@ type SignInForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *SignInForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizationForm form for authorizing oauth2 clients
|
// AuthorizationForm form for authorizing oauth2 clients
|
||||||
|
@ -156,8 +162,9 @@ type AuthorizationForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *AuthorizationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GrantApplicationForm form for authorizing oauth2 clients
|
// GrantApplicationForm form for authorizing oauth2 clients
|
||||||
|
@ -170,8 +177,9 @@ type GrantApplicationForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *GrantApplicationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *GrantApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AccessTokenForm for issuing access tokens from authorization codes or refresh tokens
|
// AccessTokenForm for issuing access tokens from authorization codes or refresh tokens
|
||||||
|
@ -188,8 +196,9 @@ type AccessTokenForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *AccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// __________________________________________.___ _______ ________ _________
|
// __________________________________________.___ _______ ________ _________
|
||||||
|
@ -212,8 +221,9 @@ type UpdateProfileForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *UpdateProfileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avatar types
|
// Avatar types
|
||||||
|
@ -231,8 +241,9 @@ type AvatarForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *AvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AvatarForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddEmailForm form for adding new email
|
// AddEmailForm form for adding new email
|
||||||
|
@ -241,8 +252,9 @@ type AddEmailForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateThemeForm form for updating a users' theme
|
// UpdateThemeForm form for updating a users' theme
|
||||||
|
@ -251,8 +263,9 @@ type UpdateThemeForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the field
|
// Validate validates the field
|
||||||
func (f *UpdateThemeForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsThemeExists checks if the theme is a theme available in the config.
|
// IsThemeExists checks if the theme is a theme available in the config.
|
||||||
|
@ -277,8 +290,9 @@ type ChangePasswordForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *ChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOpenIDForm is for changing openid uri
|
// AddOpenIDForm is for changing openid uri
|
||||||
|
@ -287,8 +301,9 @@ type AddOpenIDForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *AddOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddKeyForm form for adding SSH/GPG key
|
// AddKeyForm form for adding SSH/GPG key
|
||||||
|
@ -300,8 +315,9 @@ type AddKeyForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *AddKeyForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAccessTokenForm form for creating access token
|
// NewAccessTokenForm form for creating access token
|
||||||
|
@ -310,8 +326,9 @@ type NewAccessTokenForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *NewAccessTokenForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditOAuth2ApplicationForm form for editing oauth2 applications
|
// EditOAuth2ApplicationForm form for editing oauth2 applications
|
||||||
|
@ -321,8 +338,9 @@ type EditOAuth2ApplicationForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *EditOAuth2ApplicationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *EditOAuth2ApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorAuthForm for logging in with 2FA token.
|
// TwoFactorAuthForm for logging in with 2FA token.
|
||||||
|
@ -331,8 +349,9 @@ type TwoFactorAuthForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *TwoFactorAuthForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *TwoFactorAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TwoFactorScratchAuthForm for logging in with 2FA scratch token.
|
// TwoFactorScratchAuthForm for logging in with 2FA scratch token.
|
||||||
|
@ -341,8 +360,9 @@ type TwoFactorScratchAuthForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *TwoFactorScratchAuthForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *TwoFactorScratchAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// U2FRegistrationForm for reserving an U2F name
|
// U2FRegistrationForm for reserving an U2F name
|
||||||
|
@ -351,8 +371,9 @@ type U2FRegistrationForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *U2FRegistrationForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *U2FRegistrationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// U2FDeleteForm for deleting U2F keys
|
// U2FDeleteForm for deleting U2F keys
|
||||||
|
@ -361,6 +382,7 @@ type U2FDeleteForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *U2FDeleteForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *U2FDeleteForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
|
@ -2,11 +2,14 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.com/macaron/binding"
|
"net/http"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignInOpenIDForm form for signing in with OpenID
|
// SignInOpenIDForm form for signing in with OpenID
|
||||||
|
@ -16,8 +19,9 @@ type SignInOpenIDForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *SignInOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignUpOpenIDForm form for signin up with OpenID
|
// SignUpOpenIDForm form for signin up with OpenID
|
||||||
|
@ -29,8 +33,9 @@ type SignUpOpenIDForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *SignUpOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *SignUpOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectOpenIDForm form for connecting an existing account to an OpenID URI
|
// ConnectOpenIDForm form for connecting an existing account to an OpenID URI
|
||||||
|
@ -40,6 +45,7 @@ type ConnectOpenIDForm struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the fields
|
// Validate validates the fields
|
||||||
func (f *ConnectOpenIDForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
|
func (f *ConnectOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
|
||||||
return validate(errs, ctx.Data, f, ctx.Locale)
|
ctx := context.GetContext(req)
|
||||||
|
return middlewares.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package forms
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
|
@ -182,7 +182,7 @@ func PostLockHandler(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var req api.LFSLockRequest
|
var req api.LFSLockRequest
|
||||||
bodyReader := ctx.Req.Body().ReadCloser()
|
bodyReader := ctx.Req.Body
|
||||||
defer bodyReader.Close()
|
defer bodyReader.Close()
|
||||||
dec := json.NewDecoder(bodyReader)
|
dec := json.NewDecoder(bodyReader)
|
||||||
if err := dec.Decode(&req); err != nil {
|
if err := dec.Decode(&req); err != nil {
|
||||||
|
@ -317,7 +317,7 @@ func UnLockHandler(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var req api.LFSLockDeleteRequest
|
var req api.LFSLockDeleteRequest
|
||||||
bodyReader := ctx.Req.Body().ReadCloser()
|
bodyReader := ctx.Req.Body
|
||||||
defer bodyReader.Close()
|
defer bodyReader.Close()
|
||||||
dec := json.NewDecoder(bodyReader)
|
dec := json.NewDecoder(bodyReader)
|
||||||
if err := dec.Decode(&req); err != nil {
|
if err := dec.Decode(&req); err != nil {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -413,8 +412,8 @@ func PutHandler(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
contentStore := &ContentStore{ObjectStorage: storage.LFS}
|
contentStore := &ContentStore{ObjectStorage: storage.LFS}
|
||||||
defer ctx.Req.Request.Body.Close()
|
defer ctx.Req.Body.Close()
|
||||||
if err := contentStore.Put(meta, ctx.Req.Request.Body); err != nil {
|
if err := contentStore.Put(meta, ctx.Req.Body); err != nil {
|
||||||
// Put will log the error itself
|
// Put will log the error itself
|
||||||
ctx.Resp.WriteHeader(500)
|
ctx.Resp.WriteHeader(500)
|
||||||
if err == errSizeMismatch || err == errHashMismatch {
|
if err == errSizeMismatch || err == errHashMismatch {
|
||||||
|
@ -513,7 +512,7 @@ func Represent(rv *RequestVars, meta *models.LFSMetaObject, download, upload boo
|
||||||
|
|
||||||
// MetaMatcher provides a mux.MatcherFunc that only allows requests that contain
|
// MetaMatcher provides a mux.MatcherFunc that only allows requests that contain
|
||||||
// an Accept header with the metaMediaType
|
// an Accept header with the metaMediaType
|
||||||
func MetaMatcher(r macaron.Request) bool {
|
func MetaMatcher(r *http.Request) bool {
|
||||||
mediaParts := strings.Split(r.Header.Get("Accept"), ";")
|
mediaParts := strings.Split(r.Header.Get("Accept"), ";")
|
||||||
mt := mediaParts[0]
|
mt := mediaParts[0]
|
||||||
return mt == metaMediaType
|
return mt == metaMediaType
|
||||||
|
@ -530,7 +529,7 @@ func unpack(ctx *context.Context) *RequestVars {
|
||||||
|
|
||||||
if r.Method == "POST" { // Maybe also check if +json
|
if r.Method == "POST" { // Maybe also check if +json
|
||||||
var p RequestVars
|
var p RequestVars
|
||||||
bodyReader := r.Body().ReadCloser()
|
bodyReader := r.Body
|
||||||
defer bodyReader.Close()
|
defer bodyReader.Close()
|
||||||
dec := json.NewDecoder(bodyReader)
|
dec := json.NewDecoder(bodyReader)
|
||||||
err := dec.Decode(&p)
|
err := dec.Decode(&p)
|
||||||
|
@ -553,7 +552,7 @@ func unpackbatch(ctx *context.Context) *BatchVars {
|
||||||
r := ctx.Req
|
r := ctx.Req
|
||||||
var bv BatchVars
|
var bv BatchVars
|
||||||
|
|
||||||
bodyReader := r.Body().ReadCloser()
|
bodyReader := r.Body
|
||||||
defer bodyReader.Close()
|
defer bodyReader.Close()
|
||||||
dec := json.NewDecoder(bodyReader)
|
dec := json.NewDecoder(bodyReader)
|
||||||
err := dec.Decode(&bv)
|
err := dec.Decode(&bv)
|
||||||
|
@ -586,7 +585,7 @@ func writeStatus(ctx *context.Context, status int) {
|
||||||
logRequest(ctx.Req, status)
|
logRequest(ctx.Req, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logRequest(r macaron.Request, status int) {
|
func logRequest(r *http.Request, status int) {
|
||||||
log.Debug("LFS request - Method: %s, URL: %s, Status %d", r.Method, r.URL, status)
|
log.Debug("LFS request - Method: %s, URL: %s, Status %d", r.Method, r.URL, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,24 +3,19 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package auth
|
package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/translation"
|
||||||
"code.gitea.io/gitea/modules/validation"
|
"code.gitea.io/gitea/modules/validation"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
"github.com/unknwon/com"
|
"github.com/unknwon/com"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsAPIPath if URL is an api path
|
|
||||||
func IsAPIPath(url string) bool {
|
|
||||||
return strings.HasPrefix(url, "/api/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Form form binding interface
|
// Form form binding interface
|
||||||
type Form interface {
|
type Form interface {
|
||||||
binding.Validator
|
binding.Validator
|
||||||
|
@ -35,7 +30,7 @@ func AssignForm(form interface{}, data map[string]interface{}) {
|
||||||
typ := reflect.TypeOf(form)
|
typ := reflect.TypeOf(form)
|
||||||
val := reflect.ValueOf(form)
|
val := reflect.ValueOf(form)
|
||||||
|
|
||||||
if typ.Kind() == reflect.Ptr {
|
for typ.Kind() == reflect.Ptr {
|
||||||
typ = typ.Elem()
|
typ = typ.Elem()
|
||||||
val = val.Elem()
|
val = val.Elem()
|
||||||
}
|
}
|
||||||
|
@ -84,7 +79,8 @@ func GetInclude(field reflect.StructField) string {
|
||||||
return getRuleBody(field, "Include(")
|
return getRuleBody(field, "Include(")
|
||||||
}
|
}
|
||||||
|
|
||||||
func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaron.Locale) binding.Errors {
|
// Validate validate TODO:
|
||||||
|
func Validate(errs binding.Errors, data map[string]interface{}, f Form, l translation.Locale) binding.Errors {
|
||||||
if errs.Len() == 0 {
|
if errs.Len() == 0 {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Copyright 2020 The Macaron Authors
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
@ -12,6 +13,56 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MaxAge sets the maximum age for a provided cookie
|
||||||
|
func MaxAge(maxAge int) func(*http.Cookie) {
|
||||||
|
return func(c *http.Cookie) {
|
||||||
|
c.MaxAge = maxAge
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path sets the path for a provided cookie
|
||||||
|
func Path(path string) func(*http.Cookie) {
|
||||||
|
return func(c *http.Cookie) {
|
||||||
|
c.Path = path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Domain sets the domain for a provided cookie
|
||||||
|
func Domain(domain string) func(*http.Cookie) {
|
||||||
|
return func(c *http.Cookie) {
|
||||||
|
c.Domain = domain
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secure sets the secure setting for a provided cookie
|
||||||
|
func Secure(secure bool) func(*http.Cookie) {
|
||||||
|
return func(c *http.Cookie) {
|
||||||
|
c.Secure = secure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPOnly sets the HttpOnly setting for a provided cookie
|
||||||
|
func HTTPOnly(httpOnly bool) func(*http.Cookie) {
|
||||||
|
return func(c *http.Cookie) {
|
||||||
|
c.HttpOnly = httpOnly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expires sets the expires and rawexpires for a provided cookie
|
||||||
|
func Expires(expires time.Time) func(*http.Cookie) {
|
||||||
|
return func(c *http.Cookie) {
|
||||||
|
c.Expires = expires
|
||||||
|
c.RawExpires = expires.Format(time.UnixDate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SameSite sets the SameSite for a provided cookie
|
||||||
|
func SameSite(sameSite http.SameSite) func(*http.Cookie) {
|
||||||
|
return func(c *http.Cookie) {
|
||||||
|
c.SameSite = sameSite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewCookie creates a cookie
|
// NewCookie creates a cookie
|
||||||
func NewCookie(name, value string, maxAge int) *http.Cookie {
|
func NewCookie(name, value string, maxAge int) *http.Cookie {
|
||||||
return &http.Cookie{
|
return &http.Cookie{
|
||||||
|
@ -102,3 +153,13 @@ func SetCookie(resp http.ResponseWriter, name string, value string, others ...in
|
||||||
|
|
||||||
resp.Header().Add("Set-Cookie", cookie.String())
|
resp.Header().Add("Set-Cookie", cookie.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCookie returns given cookie value from request header.
|
||||||
|
func GetCookie(req *http.Request, name string) string {
|
||||||
|
cookie, err := req.Cookie(name)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
val, _ := url.QueryUnescape(cookie.Value)
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
10
modules/middlewares/data.go
Normal file
10
modules/middlewares/data.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
// DataStore represents a data store
|
||||||
|
type DataStore interface {
|
||||||
|
GetData() map[string]interface{}
|
||||||
|
}
|
65
modules/middlewares/flash.go
Normal file
65
modules/middlewares/flash.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import "net/url"
|
||||||
|
|
||||||
|
// flashes enumerates all the flash types
|
||||||
|
const (
|
||||||
|
SuccessFlash = "SuccessMsg"
|
||||||
|
ErrorFlash = "ErrorMsg"
|
||||||
|
WarnFlash = "WarningMsg"
|
||||||
|
InfoFlash = "InfoMsg"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// FlashNow FIXME:
|
||||||
|
FlashNow bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// Flash represents a one time data transfer between two requests.
|
||||||
|
type Flash struct {
|
||||||
|
DataStore
|
||||||
|
url.Values
|
||||||
|
ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Flash) set(name, msg string, current ...bool) {
|
||||||
|
isShow := false
|
||||||
|
if (len(current) == 0 && FlashNow) ||
|
||||||
|
(len(current) > 0 && current[0]) {
|
||||||
|
isShow = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isShow {
|
||||||
|
f.GetData()["Flash"] = f
|
||||||
|
} else {
|
||||||
|
f.Set(name, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error sets error message
|
||||||
|
func (f *Flash) Error(msg string, current ...bool) {
|
||||||
|
f.ErrorMsg = msg
|
||||||
|
f.set("error", msg, current...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning sets warning message
|
||||||
|
func (f *Flash) Warning(msg string, current ...bool) {
|
||||||
|
f.WarningMsg = msg
|
||||||
|
f.set("warning", msg, current...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info sets info message
|
||||||
|
func (f *Flash) Info(msg string, current ...bool) {
|
||||||
|
f.InfoMsg = msg
|
||||||
|
f.set("info", msg, current...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success sets success message
|
||||||
|
func (f *Flash) Success(msg string, current ...bool) {
|
||||||
|
f.SuccessMsg = msg
|
||||||
|
f.set("success", msg, current...)
|
||||||
|
}
|
|
@ -23,12 +23,14 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale {
|
||||||
// 2. Get language information from cookies.
|
// 2. Get language information from cookies.
|
||||||
if len(lang) == 0 {
|
if len(lang) == 0 {
|
||||||
ck, _ := req.Cookie("lang")
|
ck, _ := req.Cookie("lang")
|
||||||
lang = ck.Value
|
if ck != nil {
|
||||||
hasCookie = true
|
lang = ck.Value
|
||||||
|
hasCookie = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check again in case someone modify by purpose.
|
// Check again in case someone modify by purpose.
|
||||||
if !i18n.IsExist(lang) {
|
if lang != "" && !i18n.IsExist(lang) {
|
||||||
lang = ""
|
lang = ""
|
||||||
hasCookie = false
|
hasCookie = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,217 +0,0 @@
|
||||||
// Copyright 2013 Beego Authors
|
|
||||||
// Copyright 2014 The Macaron Authors
|
|
||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/nosql"
|
|
||||||
|
|
||||||
"gitea.com/go-chi/session"
|
|
||||||
"github.com/go-redis/redis/v7"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RedisStore represents a redis session store implementation.
|
|
||||||
// TODO: copied from modules/session/redis.go and should remove that one until macaron removed.
|
|
||||||
type RedisStore struct {
|
|
||||||
c redis.UniversalClient
|
|
||||||
prefix, sid string
|
|
||||||
duration time.Duration
|
|
||||||
lock sync.RWMutex
|
|
||||||
data map[interface{}]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRedisStore creates and returns a redis session store.
|
|
||||||
func NewRedisStore(c redis.UniversalClient, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore {
|
|
||||||
return &RedisStore{
|
|
||||||
c: c,
|
|
||||||
prefix: prefix,
|
|
||||||
sid: sid,
|
|
||||||
duration: dur,
|
|
||||||
data: kv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets value to given key in session.
|
|
||||||
func (s *RedisStore) Set(key, val interface{}) error {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
s.data[key] = val
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets value by given key in session.
|
|
||||||
func (s *RedisStore) Get(key interface{}) interface{} {
|
|
||||||
s.lock.RLock()
|
|
||||||
defer s.lock.RUnlock()
|
|
||||||
|
|
||||||
return s.data[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete delete a key from session.
|
|
||||||
func (s *RedisStore) Delete(key interface{}) error {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
delete(s.data, key)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns current session ID.
|
|
||||||
func (s *RedisStore) ID() string {
|
|
||||||
return s.sid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release releases resource and save data to provider.
|
|
||||||
func (s *RedisStore) Release() error {
|
|
||||||
// Skip encoding if the data is empty
|
|
||||||
if len(s.data) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := session.EncodeGob(s.data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.c.Set(s.prefix+s.sid, string(data), s.duration).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush deletes all session data.
|
|
||||||
func (s *RedisStore) Flush() error {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
s.data = make(map[interface{}]interface{})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RedisProvider represents a redis session provider implementation.
|
|
||||||
type RedisProvider struct {
|
|
||||||
c redis.UniversalClient
|
|
||||||
duration time.Duration
|
|
||||||
prefix string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initializes redis session provider.
|
|
||||||
// configs: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,prefix=session;
|
|
||||||
func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) {
|
|
||||||
p.duration, err = time.ParseDuration(fmt.Sprintf("%ds", maxlifetime))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
uri := nosql.ToRedisURI(configs)
|
|
||||||
|
|
||||||
for k, v := range uri.Query() {
|
|
||||||
switch k {
|
|
||||||
case "prefix":
|
|
||||||
p.prefix = v[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.c = nosql.GetManager().GetRedisClient(uri.String())
|
|
||||||
return p.c.Ping().Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read returns raw session store by session ID.
|
|
||||||
func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
|
|
||||||
psid := p.prefix + sid
|
|
||||||
if !p.Exist(sid) {
|
|
||||||
if err := p.c.Set(psid, "", p.duration).Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var kv map[interface{}]interface{}
|
|
||||||
kvs, err := p.c.Get(psid).Result()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(kvs) == 0 {
|
|
||||||
kv = make(map[interface{}]interface{})
|
|
||||||
} else {
|
|
||||||
kv, err = session.DecodeGob([]byte(kvs))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exist returns true if session with given ID exists.
|
|
||||||
func (p *RedisProvider) Exist(sid string) bool {
|
|
||||||
v, err := p.c.Exists(p.prefix + sid).Result()
|
|
||||||
return err == nil && v == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy deletes a session by session ID.
|
|
||||||
func (p *RedisProvider) Destroy(sid string) error {
|
|
||||||
return p.c.Del(p.prefix + sid).Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regenerate regenerates a session store from old session ID to new one.
|
|
||||||
func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) {
|
|
||||||
poldsid := p.prefix + oldsid
|
|
||||||
psid := p.prefix + sid
|
|
||||||
|
|
||||||
if p.Exist(sid) {
|
|
||||||
return nil, fmt.Errorf("new sid '%s' already exists", sid)
|
|
||||||
} else if !p.Exist(oldsid) {
|
|
||||||
// Make a fake old session.
|
|
||||||
if err = p.c.Set(poldsid, "", p.duration).Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = p.c.Rename(poldsid, psid).Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var kv map[interface{}]interface{}
|
|
||||||
kvs, err := p.c.Get(psid).Result()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(kvs) == 0 {
|
|
||||||
kv = make(map[interface{}]interface{})
|
|
||||||
} else {
|
|
||||||
kv, err = session.DecodeGob([]byte(kvs))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count counts and returns number of sessions.
|
|
||||||
func (p *RedisProvider) Count() int {
|
|
||||||
return int(p.c.DBSize().Val())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GC calls GC to clean expired sessions.
|
|
||||||
func (*RedisProvider) GC() {}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
session.Register("redis", &RedisProvider{})
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"gitea.com/go-chi/session"
|
|
||||||
couchbase "gitea.com/go-chi/session/couchbase"
|
|
||||||
memcache "gitea.com/go-chi/session/memcache"
|
|
||||||
mysql "gitea.com/go-chi/session/mysql"
|
|
||||||
postgres "gitea.com/go-chi/session/postgres"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VirtualSessionProvider represents a shadowed session provider implementation.
|
|
||||||
// TODO: copied from modules/session/redis.go and should remove that one until macaron removed.
|
|
||||||
type VirtualSessionProvider struct {
|
|
||||||
lock sync.RWMutex
|
|
||||||
provider session.Provider
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init initializes the cookie session provider with given root path.
|
|
||||||
func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
|
|
||||||
var opts session.Options
|
|
||||||
if err := json.Unmarshal([]byte(config), &opts); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Note that these options are unprepared so we can't just use NewManager here.
|
|
||||||
// Nor can we access the provider map in session.
|
|
||||||
// So we will just have to do this by hand.
|
|
||||||
// This is only slightly more wrong than modules/setting/session.go:23
|
|
||||||
switch opts.Provider {
|
|
||||||
case "memory":
|
|
||||||
o.provider = &session.MemProvider{}
|
|
||||||
case "file":
|
|
||||||
o.provider = &session.FileProvider{}
|
|
||||||
case "redis":
|
|
||||||
o.provider = &RedisProvider{}
|
|
||||||
case "mysql":
|
|
||||||
o.provider = &mysql.MysqlProvider{}
|
|
||||||
case "postgres":
|
|
||||||
o.provider = &postgres.PostgresProvider{}
|
|
||||||
case "couchbase":
|
|
||||||
o.provider = &couchbase.CouchbaseProvider{}
|
|
||||||
case "memcache":
|
|
||||||
o.provider = &memcache.MemcacheProvider{}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider)
|
|
||||||
}
|
|
||||||
return o.provider.Init(gclifetime, opts.ProviderConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read returns raw session store by session ID.
|
|
||||||
func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
|
|
||||||
o.lock.RLock()
|
|
||||||
defer o.lock.RUnlock()
|
|
||||||
if o.provider.Exist(sid) {
|
|
||||||
return o.provider.Read(sid)
|
|
||||||
}
|
|
||||||
kv := make(map[interface{}]interface{})
|
|
||||||
kv["_old_uid"] = "0"
|
|
||||||
return NewVirtualStore(o, sid, kv), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exist returns true if session with given ID exists.
|
|
||||||
func (o *VirtualSessionProvider) Exist(sid string) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy deletes a session by session ID.
|
|
||||||
func (o *VirtualSessionProvider) Destroy(sid string) error {
|
|
||||||
o.lock.Lock()
|
|
||||||
defer o.lock.Unlock()
|
|
||||||
return o.provider.Destroy(sid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regenerate regenerates a session store from old session ID to new one.
|
|
||||||
func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
|
|
||||||
o.lock.Lock()
|
|
||||||
defer o.lock.Unlock()
|
|
||||||
return o.provider.Regenerate(oldsid, sid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count counts and returns number of sessions.
|
|
||||||
func (o *VirtualSessionProvider) Count() int {
|
|
||||||
o.lock.RLock()
|
|
||||||
defer o.lock.RUnlock()
|
|
||||||
return o.provider.Count()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GC calls GC to clean expired sessions.
|
|
||||||
func (o *VirtualSessionProvider) GC() {
|
|
||||||
o.provider.GC()
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
session.Register("VirtualSession", &VirtualSessionProvider{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// VirtualStore represents a virtual session store implementation.
|
|
||||||
type VirtualStore struct {
|
|
||||||
p *VirtualSessionProvider
|
|
||||||
sid string
|
|
||||||
lock sync.RWMutex
|
|
||||||
data map[interface{}]interface{}
|
|
||||||
released bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewVirtualStore creates and returns a virtual session store.
|
|
||||||
func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore {
|
|
||||||
return &VirtualStore{
|
|
||||||
p: p,
|
|
||||||
sid: sid,
|
|
||||||
data: kv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets value to given key in session.
|
|
||||||
func (s *VirtualStore) Set(key, val interface{}) error {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
s.data[key] = val
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets value by given key in session.
|
|
||||||
func (s *VirtualStore) Get(key interface{}) interface{} {
|
|
||||||
s.lock.RLock()
|
|
||||||
defer s.lock.RUnlock()
|
|
||||||
|
|
||||||
return s.data[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete delete a key from session.
|
|
||||||
func (s *VirtualStore) Delete(key interface{}) error {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
delete(s.data, key)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns current session ID.
|
|
||||||
func (s *VirtualStore) ID() string {
|
|
||||||
return s.sid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release releases resource and save data to provider.
|
|
||||||
func (s *VirtualStore) Release() error {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
// Now need to lock the provider
|
|
||||||
s.p.lock.Lock()
|
|
||||||
defer s.p.lock.Unlock()
|
|
||||||
if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) {
|
|
||||||
// Now ensure that we don't exist!
|
|
||||||
realProvider := s.p.provider
|
|
||||||
|
|
||||||
if !s.released && realProvider.Exist(s.sid) {
|
|
||||||
// This is an error!
|
|
||||||
return fmt.Errorf("new sid '%s' already exists", s.sid)
|
|
||||||
}
|
|
||||||
realStore, err := realProvider.Read(s.sid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := realStore.Flush(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for key, value := range s.data {
|
|
||||||
if err := realStore.Set(key, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = realStore.Release()
|
|
||||||
if err == nil {
|
|
||||||
s.released = true
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flush deletes all session data.
|
|
||||||
func (s *VirtualStore) Flush() error {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
|
|
||||||
s.data = make(map[interface{}]interface{})
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/nosql"
|
"code.gitea.io/gitea/modules/nosql"
|
||||||
|
|
||||||
"gitea.com/macaron/session"
|
"gitea.com/go-chi/session"
|
||||||
"github.com/go-redis/redis/v7"
|
"github.com/go-redis/redis/v7"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
12
modules/session/store.go
Normal file
12
modules/session/store.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package session
|
||||||
|
|
||||||
|
// Store represents a session store
|
||||||
|
type Store interface {
|
||||||
|
Get(interface{}) interface{}
|
||||||
|
Set(interface{}, interface{}) error
|
||||||
|
Delete(interface{}) error
|
||||||
|
}
|
|
@ -9,12 +9,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gitea.com/macaron/session"
|
"gitea.com/go-chi/session"
|
||||||
couchbase "gitea.com/macaron/session/couchbase"
|
couchbase "gitea.com/go-chi/session/couchbase"
|
||||||
memcache "gitea.com/macaron/session/memcache"
|
memcache "gitea.com/go-chi/session/memcache"
|
||||||
mysql "gitea.com/macaron/session/mysql"
|
mysql "gitea.com/go-chi/session/mysql"
|
||||||
nodb "gitea.com/macaron/session/nodb"
|
postgres "gitea.com/go-chi/session/postgres"
|
||||||
postgres "gitea.com/macaron/session/postgres"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// VirtualSessionProvider represents a shadowed session provider implementation.
|
// VirtualSessionProvider represents a shadowed session provider implementation.
|
||||||
|
@ -48,8 +47,6 @@ func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
|
||||||
o.provider = &couchbase.CouchbaseProvider{}
|
o.provider = &couchbase.CouchbaseProvider{}
|
||||||
case "memcache":
|
case "memcache":
|
||||||
o.provider = &memcache.MemcacheProvider{}
|
o.provider = &memcache.MemcacheProvider{}
|
||||||
case "nodb":
|
|
||||||
o.provider = &nodb.NodbProvider{}
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider)
|
return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider)
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,17 +254,6 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
|
||||||
return &description
|
return &description
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMacaronLogService() {
|
|
||||||
options := newDefaultLogOptions()
|
|
||||||
options.filename = filepath.Join(LogRootPath, "macaron.log")
|
|
||||||
options.bufferLength = Cfg.Section("log").Key("BUFFER_LEN").MustInt64(10000)
|
|
||||||
|
|
||||||
Cfg.Section("log").Key("MACARON").MustString("file")
|
|
||||||
if RedirectMacaronLog {
|
|
||||||
generateNamedLogger("macaron", options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAccessLogService() {
|
func newAccessLogService() {
|
||||||
EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false)
|
EnableAccessLog = Cfg.Section("log").Key("ENABLE_ACCESS_LOG").MustBool(false)
|
||||||
AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString(
|
AccessLogTemplate = Cfg.Section("log").Key("ACCESS_LOG_TEMPLATE").MustString(
|
||||||
|
@ -360,7 +349,6 @@ func RestartLogsWithPIDSuffix() {
|
||||||
// NewLogServices creates all the log services
|
// NewLogServices creates all the log services
|
||||||
func NewLogServices(disableConsole bool) {
|
func NewLogServices(disableConsole bool) {
|
||||||
newLogService()
|
newLogService()
|
||||||
newMacaronLogService()
|
|
||||||
newRouterLogService()
|
newRouterLogService()
|
||||||
newAccessLogService()
|
newAccessLogService()
|
||||||
NewXORMLogService(disableConsole)
|
NewXORMLogService(disableConsole)
|
||||||
|
|
|
@ -41,7 +41,7 @@ var (
|
||||||
func newSessionService() {
|
func newSessionService() {
|
||||||
sec := Cfg.Section("session")
|
sec := Cfg.Section("session")
|
||||||
SessionConfig.Provider = sec.Key("PROVIDER").In("memory",
|
SessionConfig.Provider = sec.Key("PROVIDER").In("memory",
|
||||||
[]string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache", "nodb"})
|
[]string{"memory", "file", "redis", "mysql", "postgres", "couchbase", "memcache"})
|
||||||
SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
|
SessionConfig.ProviderConfig = strings.Trim(sec.Key("PROVIDER_CONFIG").MustString(path.Join(AppDataPath, "sessions")), "\" ")
|
||||||
if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) {
|
if SessionConfig.Provider == "file" && !filepath.IsAbs(SessionConfig.ProviderConfig) {
|
||||||
SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
|
SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
|
"github.com/unrolled/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Vars represents variables to be render in golang templates
|
// Vars represents variables to be render in golang templates
|
||||||
|
@ -80,3 +82,15 @@ func getDirAssetNames(dir string) []string {
|
||||||
}
|
}
|
||||||
return tmpls
|
return tmpls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HTMLRenderer returns a render.
|
||||||
|
func HTMLRenderer() *render.Render {
|
||||||
|
return render.New(render.Options{
|
||||||
|
Extensions: []string{".tmpl"},
|
||||||
|
Directory: "templates",
|
||||||
|
Funcs: NewFuncMap(),
|
||||||
|
Asset: GetAsset,
|
||||||
|
AssetNames: GetAssetNames,
|
||||||
|
IsDevelopment: !setting.IsProd(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -46,29 +44,6 @@ func GetAssetNames() []string {
|
||||||
return append(tmpls, tmpls2...)
|
return append(tmpls, tmpls2...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTMLRenderer implements the macaron handler for serving HTML templates.
|
|
||||||
func HTMLRenderer() macaron.Handler {
|
|
||||||
return macaron.Renderer(macaron.RenderOptions{
|
|
||||||
Funcs: NewFuncMap(),
|
|
||||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
|
||||||
AppendDirectories: []string{
|
|
||||||
path.Join(setting.CustomPath, "templates"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONRenderer implements the macaron handler for serving JSON templates.
|
|
||||||
func JSONRenderer() macaron.Handler {
|
|
||||||
return macaron.Renderer(macaron.RenderOptions{
|
|
||||||
Funcs: NewFuncMap(),
|
|
||||||
Directory: path.Join(setting.StaticRootPath, "templates"),
|
|
||||||
AppendDirectories: []string{
|
|
||||||
path.Join(setting.CustomPath, "templates"),
|
|
||||||
},
|
|
||||||
HTMLContentType: "application/json",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mailer provides the templates required for sending notification mails.
|
// Mailer provides the templates required for sending notification mails.
|
||||||
func Mailer() (*texttmpl.Template, *template.Template) {
|
func Mailer() (*texttmpl.Template, *template.Template) {
|
||||||
for _, funcs := range NewTextFuncMap() {
|
for _, funcs := range NewTextFuncMap() {
|
||||||
|
|
|
@ -7,10 +7,7 @@
|
||||||
package templates
|
package templates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
@ -21,8 +18,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -30,24 +25,6 @@ var (
|
||||||
bodyTemplates = template.New("")
|
bodyTemplates = template.New("")
|
||||||
)
|
)
|
||||||
|
|
||||||
type templateFileSystem struct {
|
|
||||||
files []macaron.TemplateFile
|
|
||||||
}
|
|
||||||
|
|
||||||
func (templates templateFileSystem) ListFiles() []macaron.TemplateFile {
|
|
||||||
return templates.files
|
|
||||||
}
|
|
||||||
|
|
||||||
func (templates templateFileSystem) Get(name string) (io.Reader, error) {
|
|
||||||
for i := range templates.files {
|
|
||||||
if templates.files[i].Name()+templates.files[i].Ext() == name {
|
|
||||||
return bytes.NewReader(templates.files[i].Data()), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("file '%s' not found", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAsset get a special asset, only for chi
|
// GetAsset get a special asset, only for chi
|
||||||
func GetAsset(name string) ([]byte, error) {
|
func GetAsset(name string) ([]byte, error) {
|
||||||
bs, err := ioutil.ReadFile(filepath.Join(setting.CustomPath, name))
|
bs, err := ioutil.ReadFile(filepath.Join(setting.CustomPath, name))
|
||||||
|
@ -72,95 +49,6 @@ func GetAssetNames() []string {
|
||||||
return append(tmpls, customTmpls...)
|
return append(tmpls, customTmpls...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTemplateFileSystem() templateFileSystem {
|
|
||||||
fs := templateFileSystem{}
|
|
||||||
fs.files = make([]macaron.TemplateFile, 0, 10)
|
|
||||||
|
|
||||||
for _, assetPath := range AssetNames() {
|
|
||||||
if strings.HasPrefix(assetPath, "mail/") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasSuffix(assetPath, ".tmpl") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := Asset(assetPath)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to read embedded %s template. %v", assetPath, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.files = append(fs.files, macaron.NewTplFile(
|
|
||||||
strings.TrimSuffix(
|
|
||||||
assetPath,
|
|
||||||
".tmpl",
|
|
||||||
),
|
|
||||||
content,
|
|
||||||
".tmpl",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
customDir := path.Join(setting.CustomPath, "templates")
|
|
||||||
isDir, err := util.IsDir(customDir)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Unable to check if templates dir %s is a directory. Error: %v", customDir, err)
|
|
||||||
}
|
|
||||||
if isDir {
|
|
||||||
files, err := util.StatDir(customDir)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to read %s templates dir. %v", customDir, err)
|
|
||||||
} else {
|
|
||||||
for _, filePath := range files {
|
|
||||||
if strings.HasPrefix(filePath, "mail/") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasSuffix(filePath, ".tmpl") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(path.Join(customDir, filePath))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to read custom %s template. %v", filePath, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.files = append(fs.files, macaron.NewTplFile(
|
|
||||||
strings.TrimSuffix(
|
|
||||||
filePath,
|
|
||||||
".tmpl",
|
|
||||||
),
|
|
||||||
content,
|
|
||||||
".tmpl",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTMLRenderer implements the macaron handler for serving HTML templates.
|
|
||||||
func HTMLRenderer() macaron.Handler {
|
|
||||||
return macaron.Renderer(macaron.RenderOptions{
|
|
||||||
Funcs: NewFuncMap(),
|
|
||||||
TemplateFileSystem: NewTemplateFileSystem(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONRenderer implements the macaron handler for serving JSON templates.
|
|
||||||
func JSONRenderer() macaron.Handler {
|
|
||||||
return macaron.Renderer(macaron.RenderOptions{
|
|
||||||
Funcs: NewFuncMap(),
|
|
||||||
TemplateFileSystem: NewTemplateFileSystem(),
|
|
||||||
HTMLContentType: "application/json",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mailer provides the templates required for sending notification mails.
|
// Mailer provides the templates required for sending notification mails.
|
||||||
func Mailer() (*texttmpl.Template, *template.Template) {
|
func Mailer() (*texttmpl.Template, *template.Template) {
|
||||||
for _, funcs := range NewTextFuncMap() {
|
for _, funcs := range NewTextFuncMap() {
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
scontext "context"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -13,32 +16,37 @@ import (
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
"github.com/go-chi/chi"
|
||||||
"gitea.com/macaron/session"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/unrolled/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockContext mock context for unit tests
|
// MockContext mock context for unit tests
|
||||||
func MockContext(t *testing.T, path string) *context.Context {
|
func MockContext(t *testing.T, path string) *context.Context {
|
||||||
var macaronContext macaron.Context
|
var resp = &mockResponseWriter{}
|
||||||
macaronContext.ReplaceAllParams(macaron.Params{})
|
var ctx = context.Context{
|
||||||
macaronContext.Locale = &mockLocale{}
|
Render: &mockRender{},
|
||||||
requestURL, err := url.Parse(path)
|
Data: make(map[string]interface{}),
|
||||||
assert.NoError(t, err)
|
Flash: &middlewares.Flash{
|
||||||
macaronContext.Req = macaron.Request{Request: &http.Request{
|
|
||||||
URL: requestURL,
|
|
||||||
Form: url.Values{},
|
|
||||||
}}
|
|
||||||
macaronContext.Resp = &mockResponseWriter{}
|
|
||||||
macaronContext.Render = &mockRender{ResponseWriter: macaronContext.Resp}
|
|
||||||
macaronContext.Data = map[string]interface{}{}
|
|
||||||
return &context.Context{
|
|
||||||
Context: &macaronContext,
|
|
||||||
Flash: &session.Flash{
|
|
||||||
Values: make(url.Values),
|
Values: make(url.Values),
|
||||||
},
|
},
|
||||||
|
Resp: context.NewResponse(resp),
|
||||||
|
Locale: &mockLocale{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestURL, err := url.Parse(path)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
var req = &http.Request{
|
||||||
|
URL: requestURL,
|
||||||
|
Form: url.Values{},
|
||||||
|
}
|
||||||
|
|
||||||
|
chiCtx := chi.NewRouteContext()
|
||||||
|
req = req.WithContext(scontext.WithValue(req.Context(), chi.RouteCtxKey, chiCtx))
|
||||||
|
ctx.Req = context.WithContext(req, &ctx)
|
||||||
|
return &ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadRepo load a repo into a test context.
|
// LoadRepo load a repo into a test context.
|
||||||
|
@ -113,77 +121,20 @@ func (rw *mockResponseWriter) Size() int {
|
||||||
return rw.size
|
return rw.size
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rw *mockResponseWriter) Before(b macaron.BeforeFunc) {
|
|
||||||
b(rw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rw *mockResponseWriter) Push(target string, opts *http.PushOptions) error {
|
func (rw *mockResponseWriter) Push(target string, opts *http.PushOptions) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockRender struct {
|
type mockRender struct {
|
||||||
http.ResponseWriter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *mockRender) SetResponseWriter(rw http.ResponseWriter) {
|
func (tr *mockRender) TemplateLookup(tmpl string) *template.Template {
|
||||||
tr.ResponseWriter = rw
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *mockRender) JSON(status int, _ interface{}) {
|
func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ interface{}, _ ...render.HTMLOptions) error {
|
||||||
tr.Status(status)
|
if resp, ok := w.(http.ResponseWriter); ok {
|
||||||
}
|
resp.WriteHeader(status)
|
||||||
|
}
|
||||||
func (tr *mockRender) JSONString(interface{}) (string, error) {
|
return nil
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) RawData(status int, _ []byte) {
|
|
||||||
tr.Status(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) PlainText(status int, _ []byte) {
|
|
||||||
tr.Status(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) HTML(status int, _ string, _ interface{}, _ ...macaron.HTMLOptions) {
|
|
||||||
tr.Status(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) HTMLSet(status int, _ string, _ string, _ interface{}, _ ...macaron.HTMLOptions) {
|
|
||||||
tr.Status(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) HTMLSetString(string, string, interface{}, ...macaron.HTMLOptions) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error) {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) HTMLSetBytes(string, string, interface{}, ...macaron.HTMLOptions) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) HTMLBytes(string, interface{}, ...macaron.HTMLOptions) ([]byte, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) XML(status int, _ interface{}) {
|
|
||||||
tr.Status(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) Error(status int, _ ...string) {
|
|
||||||
tr.Status(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) Status(status int) {
|
|
||||||
tr.ResponseWriter.WriteHeader(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) SetTemplatePath(string, string) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *mockRender) HasTemplateSet(string) bool {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/translation"
|
||||||
|
|
||||||
macaroni18n "gitea.com/macaron/i18n"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/unknwon/i18n"
|
"github.com/unknwon/i18n"
|
||||||
)
|
)
|
||||||
|
@ -27,13 +27,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
|
setting.StaticRootPath = "../../"
|
||||||
|
setting.Names = []string{"english"}
|
||||||
|
setting.Langs = []string{"en-US"}
|
||||||
// setup
|
// setup
|
||||||
macaroni18n.I18n(macaroni18n.Options{
|
translation.InitLocales()
|
||||||
Directory: "../../options/locale/",
|
|
||||||
DefaultLang: "en-US",
|
|
||||||
Langs: []string{"en-US"},
|
|
||||||
Names: []string{"english"},
|
|
||||||
})
|
|
||||||
BaseDate = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
|
BaseDate = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
// run the tests
|
// run the tests
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"code.gitea.io/gitea/modules/options"
|
"code.gitea.io/gitea/modules/options"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
macaron_i18n "gitea.com/macaron/i18n"
|
|
||||||
"github.com/unknwon/i18n"
|
"github.com/unknwon/i18n"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
)
|
)
|
||||||
|
@ -20,49 +19,57 @@ type Locale interface {
|
||||||
Tr(string, ...interface{}) string
|
Tr(string, ...interface{}) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LangType represents a lang type
|
||||||
|
type LangType struct {
|
||||||
|
Lang, Name string
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
matcher language.Matcher
|
matcher language.Matcher
|
||||||
|
allLangs []LangType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AllLangs returns all supported langauages
|
||||||
|
func AllLangs() []LangType {
|
||||||
|
return allLangs
|
||||||
|
}
|
||||||
|
|
||||||
// InitLocales loads the locales
|
// InitLocales loads the locales
|
||||||
func InitLocales() {
|
func InitLocales() {
|
||||||
localeNames, err := options.Dir("locale")
|
localeNames, err := options.Dir("locale")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to list locale files: %v", err)
|
log.Fatal("Failed to list locale files: %v", err)
|
||||||
}
|
}
|
||||||
localFiles := make(map[string][]byte)
|
|
||||||
|
|
||||||
|
localFiles := make(map[string][]byte)
|
||||||
for _, name := range localeNames {
|
for _, name := range localeNames {
|
||||||
localFiles[name], err = options.Locale(name)
|
localFiles[name], err = options.Locale(name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to load %s locale file. %v", name, err)
|
log.Fatal("Failed to load %s locale file. %v", name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// These codes will be used once macaron removed
|
// These codes will be used once macaron removed
|
||||||
/*tags := make([]language.Tag, len(setting.Langs))
|
tags := make([]language.Tag, len(setting.Langs))
|
||||||
for i, lang := range setting.Langs {
|
for i, lang := range setting.Langs {
|
||||||
tags[i] = language.Raw.Make(lang)
|
tags[i] = language.Raw.Make(lang)
|
||||||
}
|
}
|
||||||
matcher = language.NewMatcher(tags)
|
|
||||||
for i, name := range setting.Names {
|
|
||||||
i18n.SetMessage(setting.Langs[i], localFiles[name])
|
|
||||||
}
|
|
||||||
i18n.SetDefaultLang("en-US")*/
|
|
||||||
|
|
||||||
// To be compatible with macaron, we now have to use macaron i18n, once macaron
|
matcher = language.NewMatcher(tags)
|
||||||
// removed, we can use i18n directly
|
for i := range setting.Names {
|
||||||
macaron_i18n.I18n(macaron_i18n.Options{
|
key := "locale_" + setting.Langs[i] + ".ini"
|
||||||
SubURL: setting.AppSubURL,
|
if err := i18n.SetMessageWithDesc(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil {
|
||||||
Files: localFiles,
|
log.Fatal("Failed to set messages to %s: %v", setting.Langs[i], err)
|
||||||
Langs: setting.Langs,
|
}
|
||||||
Names: setting.Names,
|
}
|
||||||
DefaultLang: "en-US",
|
i18n.SetDefaultLang("en-US")
|
||||||
Redirect: false,
|
|
||||||
CookieDomain: setting.SessionConfig.Domain,
|
allLangs = make([]LangType, 0, i18n.Count()-1)
|
||||||
})
|
langs := i18n.ListLangs()
|
||||||
|
names := i18n.ListLangDescs()
|
||||||
|
for i, v := range langs {
|
||||||
|
allLangs = append(allLangs, LangType{v, names[i]})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match matches accept languages
|
// Match matches accept languages
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"gitea.com/macaron/macaron"
|
"github.com/go-chi/chi"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,9 +34,10 @@ type (
|
||||||
|
|
||||||
func performValidationTest(t *testing.T, testCase validationTestCase) {
|
func performValidationTest(t *testing.T, testCase validationTestCase) {
|
||||||
httpRecorder := httptest.NewRecorder()
|
httpRecorder := httptest.NewRecorder()
|
||||||
m := macaron.Classic()
|
m := chi.NewRouter()
|
||||||
|
|
||||||
m.Post(testRoute, binding.Validate(testCase.data), func(actual binding.Errors) {
|
m.Post(testRoute, func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
actual := binding.Validate(req, testCase.data)
|
||||||
// see https://github.com/stretchr/testify/issues/435
|
// see https://github.com/stretchr/testify/issues/435
|
||||||
if actual == nil {
|
if actual == nil {
|
||||||
actual = binding.Errors{}
|
actual = binding.Errors{}
|
||||||
|
@ -49,7 +50,7 @@ func performValidationTest(t *testing.T, testCase validationTestCase) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
req.Header.Add("Content-Type", "x-www-form-urlencoded")
|
||||||
m.ServeHTTP(httpRecorder, req)
|
m.ServeHTTP(httpRecorder, req)
|
||||||
|
|
||||||
switch httpRecorder.Code {
|
switch httpRecorder.Code {
|
||||||
|
|
|
@ -7,7 +7,7 @@ package validation
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ package validation
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
var gitRefNameValidationTestCases = []validationTestCase{
|
var gitRefNameValidationTestCases = []validationTestCase{
|
||||||
|
|
|
@ -7,7 +7,7 @@ package validation
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
var urlValidationTestCases = []validationTestCase{
|
var urlValidationTestCases = []validationTestCase{
|
||||||
|
|
322
modules/web/route.go
Normal file
322
modules/web/route.go
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/middlewares"
|
||||||
|
|
||||||
|
"gitea.com/go-chi/binding"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Wrap converts all kinds of routes to standard library one
|
||||||
|
func Wrap(handlers ...interface{}) http.HandlerFunc {
|
||||||
|
if len(handlers) == 0 {
|
||||||
|
panic("No handlers found")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, handler := range handlers {
|
||||||
|
switch t := handler.(type) {
|
||||||
|
case http.HandlerFunc, func(http.ResponseWriter, *http.Request),
|
||||||
|
func(ctx *context.Context),
|
||||||
|
func(*context.APIContext),
|
||||||
|
func(*context.PrivateContext),
|
||||||
|
func(http.Handler) http.Handler:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported handler type: %#v", t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
for i := 0; i < len(handlers); i++ {
|
||||||
|
handler := handlers[i]
|
||||||
|
switch t := handler.(type) {
|
||||||
|
case http.HandlerFunc:
|
||||||
|
t(resp, req)
|
||||||
|
if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case func(http.ResponseWriter, *http.Request):
|
||||||
|
t(resp, req)
|
||||||
|
if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case func(ctx *context.Context):
|
||||||
|
ctx := context.GetContext(req)
|
||||||
|
t(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case func(*context.APIContext):
|
||||||
|
ctx := context.GetAPIContext(req)
|
||||||
|
t(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case func(*context.PrivateContext):
|
||||||
|
ctx := context.GetPrivateContext(req)
|
||||||
|
t(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case func(http.Handler) http.Handler:
|
||||||
|
var next = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})
|
||||||
|
t(next).ServeHTTP(resp, req)
|
||||||
|
if r, ok := resp.(context.ResponseWriter); ok && r.Status() > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported handler type: %#v", t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middle wrap a context function as a chi middleware
|
||||||
|
func Middle(f func(ctx *context.Context)) func(netx http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
ctx := context.GetContext(req)
|
||||||
|
f(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MiddleAPI wrap a context function as a chi middleware
|
||||||
|
func MiddleAPI(f func(ctx *context.APIContext)) func(netx http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
ctx := context.GetAPIContext(req)
|
||||||
|
f(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind binding an obj to a handler
|
||||||
|
func Bind(obj interface{}) http.HandlerFunc {
|
||||||
|
var tp = reflect.TypeOf(obj)
|
||||||
|
if tp.Kind() == reflect.Ptr {
|
||||||
|
tp = tp.Elem()
|
||||||
|
}
|
||||||
|
if tp.Kind() != reflect.Struct {
|
||||||
|
panic("Only structs are allowed to bind")
|
||||||
|
}
|
||||||
|
return Wrap(func(ctx *context.Context) {
|
||||||
|
var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly
|
||||||
|
binding.Bind(ctx.Req, theObj)
|
||||||
|
SetForm(ctx, theObj)
|
||||||
|
middlewares.AssignForm(theObj, ctx.Data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForm set the form object
|
||||||
|
func SetForm(data middlewares.DataStore, obj interface{}) {
|
||||||
|
data.GetData()["__form"] = obj
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetForm returns the validate form information
|
||||||
|
func GetForm(data middlewares.DataStore) interface{} {
|
||||||
|
return data.GetData()["__form"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route defines a route based on chi's router
|
||||||
|
type Route struct {
|
||||||
|
R chi.Router
|
||||||
|
curGroupPrefix string
|
||||||
|
curMiddlewares []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRoute creates a new route
|
||||||
|
func NewRoute() *Route {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
return &Route{
|
||||||
|
R: r,
|
||||||
|
curGroupPrefix: "",
|
||||||
|
curMiddlewares: []interface{}{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use supports two middlewares
|
||||||
|
func (r *Route) Use(middlewares ...interface{}) {
|
||||||
|
if r.curGroupPrefix != "" {
|
||||||
|
r.curMiddlewares = append(r.curMiddlewares, middlewares...)
|
||||||
|
} else {
|
||||||
|
for _, middle := range middlewares {
|
||||||
|
switch t := middle.(type) {
|
||||||
|
case func(http.Handler) http.Handler:
|
||||||
|
r.R.Use(t)
|
||||||
|
case func(*context.Context):
|
||||||
|
r.R.Use(Middle(t))
|
||||||
|
case func(*context.APIContext):
|
||||||
|
r.R.Use(MiddleAPI(t))
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported middleware type: %#v", t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group mounts a sub-Router along a `pattern` string.
|
||||||
|
func (r *Route) Group(pattern string, fn func(), middlewares ...interface{}) {
|
||||||
|
var previousGroupPrefix = r.curGroupPrefix
|
||||||
|
var previousMiddlewares = r.curMiddlewares
|
||||||
|
r.curGroupPrefix += pattern
|
||||||
|
r.curMiddlewares = append(r.curMiddlewares, middlewares...)
|
||||||
|
|
||||||
|
fn()
|
||||||
|
|
||||||
|
r.curGroupPrefix = previousGroupPrefix
|
||||||
|
r.curMiddlewares = previousMiddlewares
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) getPattern(pattern string) string {
|
||||||
|
newPattern := r.curGroupPrefix + pattern
|
||||||
|
if !strings.HasPrefix(newPattern, "/") {
|
||||||
|
newPattern = "/" + newPattern
|
||||||
|
}
|
||||||
|
if newPattern == "/" {
|
||||||
|
return newPattern
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(newPattern, "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount attaches another Route along ./pattern/*
|
||||||
|
func (r *Route) Mount(pattern string, subR *Route) {
|
||||||
|
var middlewares = make([]interface{}, len(r.curMiddlewares))
|
||||||
|
copy(middlewares, r.curMiddlewares)
|
||||||
|
subR.Use(middlewares...)
|
||||||
|
r.R.Mount(r.getPattern(pattern), subR.R)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any delegate requests for all methods
|
||||||
|
func (r *Route) Any(pattern string, h ...interface{}) {
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
r.R.HandleFunc(r.getPattern(pattern), Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route delegate special methods
|
||||||
|
func (r *Route) Route(pattern string, methods string, h ...interface{}) {
|
||||||
|
p := r.getPattern(pattern)
|
||||||
|
ms := strings.Split(methods, ",")
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
for _, method := range ms {
|
||||||
|
r.R.MethodFunc(strings.TrimSpace(method), p, Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete delegate delete method
|
||||||
|
func (r *Route) Delete(pattern string, h ...interface{}) {
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
r.R.Delete(r.getPattern(pattern), Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Route) getMiddlewares(h []interface{}) []interface{} {
|
||||||
|
var middlewares = make([]interface{}, len(r.curMiddlewares), len(r.curMiddlewares)+len(h))
|
||||||
|
copy(middlewares, r.curMiddlewares)
|
||||||
|
middlewares = append(middlewares, h...)
|
||||||
|
return middlewares
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get delegate get method
|
||||||
|
func (r *Route) Get(pattern string, h ...interface{}) {
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
r.R.Get(r.getPattern(pattern), Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head delegate head method
|
||||||
|
func (r *Route) Head(pattern string, h ...interface{}) {
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
r.R.Head(r.getPattern(pattern), Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post delegate post method
|
||||||
|
func (r *Route) Post(pattern string, h ...interface{}) {
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
r.R.Post(r.getPattern(pattern), Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put delegate put method
|
||||||
|
func (r *Route) Put(pattern string, h ...interface{}) {
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
r.R.Put(r.getPattern(pattern), Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch delegate patch method
|
||||||
|
func (r *Route) Patch(pattern string, h ...interface{}) {
|
||||||
|
var middlewares = r.getMiddlewares(h)
|
||||||
|
r.R.Patch(r.getPattern(pattern), Wrap(middlewares...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP implements http.Handler
|
||||||
|
func (r *Route) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
r.R.ServeHTTP(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFound defines a handler to respond whenever a route could
|
||||||
|
// not be found.
|
||||||
|
func (r *Route) NotFound(h http.HandlerFunc) {
|
||||||
|
r.R.NotFound(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MethodNotAllowed defines a handler to respond whenever a method is
|
||||||
|
// not allowed.
|
||||||
|
func (r *Route) MethodNotAllowed(h http.HandlerFunc) {
|
||||||
|
r.R.MethodNotAllowed(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combo deletegate requests to Combo
|
||||||
|
func (r *Route) Combo(pattern string, h ...interface{}) *Combo {
|
||||||
|
return &Combo{r, pattern, h}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combo represents a tiny group routes with same pattern
|
||||||
|
type Combo struct {
|
||||||
|
r *Route
|
||||||
|
pattern string
|
||||||
|
h []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get deletegate Get method
|
||||||
|
func (c *Combo) Get(h ...interface{}) *Combo {
|
||||||
|
c.r.Get(c.pattern, append(c.h, h...)...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post deletegate Post method
|
||||||
|
func (c *Combo) Post(h ...interface{}) *Combo {
|
||||||
|
c.r.Post(c.pattern, append(c.h, h...)...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletegate Delete method
|
||||||
|
func (c *Combo) Delete(h ...interface{}) *Combo {
|
||||||
|
c.r.Delete(c.pattern, append(c.h, h...)...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put deletegate Put method
|
||||||
|
func (c *Combo) Put(h ...interface{}) *Combo {
|
||||||
|
c.r.Put(c.pattern, append(c.h, h...)...)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch deletegate Patch method
|
||||||
|
func (c *Combo) Patch(h ...interface{}) *Combo {
|
||||||
|
c.r.Patch(c.pattern, append(c.h, h...)...)
|
||||||
|
return c
|
||||||
|
}
|
169
modules/web/route_test.go
Normal file
169
modules/web/route_test.go
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package web
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRoute1(t *testing.T) {
|
||||||
|
buff := bytes.NewBufferString("")
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
recorder.Body = buff
|
||||||
|
|
||||||
|
r := NewRoute()
|
||||||
|
r.Get("/{username}/{reponame}/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
username := chi.URLParam(req, "username")
|
||||||
|
assert.EqualValues(t, "gitea", username)
|
||||||
|
reponame := chi.URLParam(req, "reponame")
|
||||||
|
assert.EqualValues(t, "gitea", reponame)
|
||||||
|
tp := chi.URLParam(req, "type")
|
||||||
|
assert.EqualValues(t, "issues", tp)
|
||||||
|
})
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoute2(t *testing.T) {
|
||||||
|
buff := bytes.NewBufferString("")
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
recorder.Body = buff
|
||||||
|
|
||||||
|
var route int
|
||||||
|
|
||||||
|
r := NewRoute()
|
||||||
|
r.Group("/{username}/{reponame}", func() {
|
||||||
|
r.Group("", func() {
|
||||||
|
r.Get("/{type:issues|pulls}", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
username := chi.URLParam(req, "username")
|
||||||
|
assert.EqualValues(t, "gitea", username)
|
||||||
|
reponame := chi.URLParam(req, "reponame")
|
||||||
|
assert.EqualValues(t, "gitea", reponame)
|
||||||
|
tp := chi.URLParam(req, "type")
|
||||||
|
assert.EqualValues(t, "issues", tp)
|
||||||
|
route = 0
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/{type:issues|pulls}/{index}", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
username := chi.URLParam(req, "username")
|
||||||
|
assert.EqualValues(t, "gitea", username)
|
||||||
|
reponame := chi.URLParam(req, "reponame")
|
||||||
|
assert.EqualValues(t, "gitea", reponame)
|
||||||
|
tp := chi.URLParam(req, "type")
|
||||||
|
assert.EqualValues(t, "issues", tp)
|
||||||
|
index := chi.URLParam(req, "index")
|
||||||
|
assert.EqualValues(t, "1", index)
|
||||||
|
route = 1
|
||||||
|
})
|
||||||
|
}, func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Group("/issues/{index}", func() {
|
||||||
|
r.Get("/view", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
username := chi.URLParam(req, "username")
|
||||||
|
assert.EqualValues(t, "gitea", username)
|
||||||
|
reponame := chi.URLParam(req, "reponame")
|
||||||
|
assert.EqualValues(t, "gitea", reponame)
|
||||||
|
index := chi.URLParam(req, "index")
|
||||||
|
assert.EqualValues(t, "1", index)
|
||||||
|
route = 2
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
assert.EqualValues(t, 0, route)
|
||||||
|
|
||||||
|
req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
assert.EqualValues(t, 1, route)
|
||||||
|
|
||||||
|
req, err = http.NewRequest("GET", "http://localhost:8000/gitea/gitea/issues/1/view", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
assert.EqualValues(t, 2, route)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRoute3(t *testing.T) {
|
||||||
|
buff := bytes.NewBufferString("")
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
recorder.Body = buff
|
||||||
|
|
||||||
|
var route int
|
||||||
|
|
||||||
|
m := NewRoute()
|
||||||
|
r := NewRoute()
|
||||||
|
r.Mount("/api/v1", m)
|
||||||
|
|
||||||
|
m.Group("/repos", func() {
|
||||||
|
m.Group("/{username}/{reponame}", func() {
|
||||||
|
m.Group("/branch_protections", func() {
|
||||||
|
m.Get("", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
route = 0
|
||||||
|
})
|
||||||
|
m.Post("", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
route = 1
|
||||||
|
})
|
||||||
|
m.Group("/{name}", func() {
|
||||||
|
m.Get("", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
route = 2
|
||||||
|
})
|
||||||
|
m.Patch("", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
route = 3
|
||||||
|
})
|
||||||
|
m.Delete("", func(resp http.ResponseWriter, req *http.Request) {
|
||||||
|
route = 4
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
assert.EqualValues(t, 0, route)
|
||||||
|
|
||||||
|
req, err = http.NewRequest("POST", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code, http.StatusOK)
|
||||||
|
assert.EqualValues(t, 1, route)
|
||||||
|
|
||||||
|
req, err = http.NewRequest("GET", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
assert.EqualValues(t, 2, route)
|
||||||
|
|
||||||
|
req, err = http.NewRequest("PATCH", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
assert.EqualValues(t, 3, route)
|
||||||
|
|
||||||
|
req, err = http.NewRequest("DELETE", "http://localhost:8000/api/v1/repos/gitea/gitea/branch_protections/master", nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
r.ServeHTTP(recorder, req)
|
||||||
|
assert.EqualValues(t, http.StatusOK, recorder.Code)
|
||||||
|
assert.EqualValues(t, 4, route)
|
||||||
|
}
|
|
@ -1538,8 +1538,7 @@ settings.trust_model.collaborator.long=Mitarbeiter: Vertraue Signaturen von Mita
|
||||||
settings.trust_model.collaborator.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als "vertrauenswürdig" markiert - ( egal ob sie mit dem Committer übereinstimmen oder nicht). Andernfalls werden gültige Signaturen als "nicht vertrauenswürdig" markiert, unabhängig ob die Signatur mit dem Committer übereinstimmt oder nicht.
|
settings.trust_model.collaborator.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als "vertrauenswürdig" markiert - ( egal ob sie mit dem Committer übereinstimmen oder nicht). Andernfalls werden gültige Signaturen als "nicht vertrauenswürdig" markiert, unabhängig ob die Signatur mit dem Committer übereinstimmt oder nicht.
|
||||||
settings.trust_model.committer=Committer
|
settings.trust_model.committer=Committer
|
||||||
settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (Dies stimmt mit GitHub überein und zwingt signierte Commits von Gitea dazu, Gitea als Committer zu haben)
|
settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (Dies stimmt mit GitHub überein und zwingt signierte Commits von Gitea dazu, Gitea als Committer zu haben)
|
||||||
settings.trust_model.committer.desc=Gültige Signaturen von Mitwirkenden werden als "vertrauenswürdig" gekennzeichnet, wenn sie mit ihrem Committer übereinstimmen. Ansonsten werden sie als "nicht übereinstimmend" markiert. Das führt dazu, dass Gitea auf signierten Commits, bei denen der echte Committer als Co-authored-by: oder Co-committed-by in der Beschreibung eingetragen wurde, als Committer gilt.
|
settings.trust_model.committer.desc=Gültige Signaturen von Mitwirkenden werden als "vertrauenswürdig" gekennzeichnet, wenn sie mit ihrem Committer übereinstimmen. Ansonsten werden sie als "nicht übereinstimmend" markiert. Das führt dazu, dass Gitea auf signierten Commits, bei denen der echte Committer als Co-authored-by: oder Co-committed-by in der Beschreibung eingetragen wurde, als Committer gilt. Der Standard Gitea Schlüssel muss auf einen User in der Datenbank zeigen.
|
||||||
Der Standard Gitea Schlüssel muss auf einen User in der Datenbank zeigen.
|
|
||||||
settings.trust_model.collaboratorcommitter=Mitarbeiter+Committer
|
settings.trust_model.collaboratorcommitter=Mitarbeiter+Committer
|
||||||
settings.trust_model.collaboratorcommitter.long=Mitarbeiter+Committer: Signaturen der Mitarbeiter vertrauen die mit dem Committer übereinstimmen
|
settings.trust_model.collaboratorcommitter.long=Mitarbeiter+Committer: Signaturen der Mitarbeiter vertrauen die mit dem Committer übereinstimmen
|
||||||
settings.trust_model.collaboratorcommitter.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als "vertrauenswürdig" markiert, wenn sie mit dem Committer übereinstimmen. Andernfalls werden gültige Signaturen als "nicht vertrauenswürdig" markiert, wenn die Signatur mit dem Committer übereinstimmt als "nicht übereinstimmend". Dies zwingt Gitea als Committer bei signierten Commits mit dem tatsächlichen Committer als Co-Authored-By: und Co-Committed-By: Trailer im Commit. Der Standard-Gitea-Schlüssel muss mit einem Benutzer in der Datenbank übereinstimmen.
|
settings.trust_model.collaboratorcommitter.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als "vertrauenswürdig" markiert, wenn sie mit dem Committer übereinstimmen. Andernfalls werden gültige Signaturen als "nicht vertrauenswürdig" markiert, wenn die Signatur mit dem Committer übereinstimmt als "nicht übereinstimmend". Dies zwingt Gitea als Committer bei signierten Commits mit dem tatsächlichen Committer als Co-Authored-By: und Co-Committed-By: Trailer im Commit. Der Standard-Gitea-Schlüssel muss mit einem Benutzer in der Datenbank übereinstimmen.
|
||||||
|
|
|
@ -16,20 +16,20 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/cron"
|
"code.gitea.io/gitea/modules/cron"
|
||||||
|
auth "code.gitea.io/gitea/modules/forms"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/process"
|
"code.gitea.io/gitea/modules/process"
|
||||||
"code.gitea.io/gitea/modules/queue"
|
"code.gitea.io/gitea/modules/queue"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/services/mailer"
|
"code.gitea.io/gitea/services/mailer"
|
||||||
|
|
||||||
"gitea.com/macaron/macaron"
|
"gitea.com/go-chi/session"
|
||||||
"gitea.com/macaron/session"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -132,7 +132,8 @@ func Dashboard(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DashboardPost run an admin operation
|
// DashboardPost run an admin operation
|
||||||
func DashboardPost(ctx *context.Context, form auth.AdminDashboardForm) {
|
func DashboardPost(ctx *context.Context) {
|
||||||
|
form := web.GetForm(ctx).(*auth.AdminDashboardForm)
|
||||||
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
|
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
|
||||||
ctx.Data["PageIsAdmin"] = true
|
ctx.Data["PageIsAdmin"] = true
|
||||||
ctx.Data["PageIsAdminDashboard"] = true
|
ctx.Data["PageIsAdminDashboard"] = true
|
||||||
|
@ -239,7 +240,7 @@ func Config(ctx *context.Context) {
|
||||||
ctx.Data["OfflineMode"] = setting.OfflineMode
|
ctx.Data["OfflineMode"] = setting.OfflineMode
|
||||||
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
|
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
|
||||||
ctx.Data["RunUser"] = setting.RunUser
|
ctx.Data["RunUser"] = setting.RunUser
|
||||||
ctx.Data["RunMode"] = strings.Title(macaron.Env)
|
ctx.Data["RunMode"] = strings.Title(setting.RunMode)
|
||||||
if version, err := git.LocalVersion(); err == nil {
|
if version, err := git.LocalVersion(); err == nil {
|
||||||
ctx.Data["GitVersion"] = version.Original()
|
ctx.Data["GitVersion"] = version.Original()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,16 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
|
||||||
"code.gitea.io/gitea/modules/auth/ldap"
|
"code.gitea.io/gitea/modules/auth/ldap"
|
||||||
"code.gitea.io/gitea/modules/auth/oauth2"
|
"code.gitea.io/gitea/modules/auth/oauth2"
|
||||||
"code.gitea.io/gitea/modules/auth/pam"
|
"code.gitea.io/gitea/modules/auth/pam"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
auth "code.gitea.io/gitea/modules/forms"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
|
||||||
"xorm.io/xorm/convert"
|
"xorm.io/xorm/convert"
|
||||||
)
|
)
|
||||||
|
@ -206,7 +207,8 @@ func parseSSPIConfig(ctx *context.Context, form auth.AuthenticationForm) (*model
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuthSourcePost response for adding an auth source
|
// NewAuthSourcePost response for adding an auth source
|
||||||
func NewAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
|
func NewAuthSourcePost(ctx *context.Context) {
|
||||||
|
form := *web.GetForm(ctx).(*auth.AuthenticationForm)
|
||||||
ctx.Data["Title"] = ctx.Tr("admin.auths.new")
|
ctx.Data["Title"] = ctx.Tr("admin.auths.new")
|
||||||
ctx.Data["PageIsAdmin"] = true
|
ctx.Data["PageIsAdmin"] = true
|
||||||
ctx.Data["PageIsAdminAuthentications"] = true
|
ctx.Data["PageIsAdminAuthentications"] = true
|
||||||
|
@ -312,7 +314,8 @@ func EditAuthSource(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditAuthSourcePost response for editing auth source
|
// EditAuthSourcePost response for editing auth source
|
||||||
func EditAuthSourcePost(ctx *context.Context, form auth.AuthenticationForm) {
|
func EditAuthSourcePost(ctx *context.Context) {
|
||||||
|
form := *web.GetForm(ctx).(*auth.AuthenticationForm)
|
||||||
ctx.Data["Title"] = ctx.Tr("admin.auths.edit")
|
ctx.Data["Title"] = ctx.Tr("admin.auths.edit")
|
||||||
ctx.Data["PageIsAdmin"] = true
|
ctx.Data["PageIsAdmin"] = true
|
||||||
ctx.Data["PageIsAdminAuthentications"] = true
|
ctx.Data["PageIsAdminAuthentications"] = true
|
||||||
|
|
|
@ -11,12 +11,13 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
auth "code.gitea.io/gitea/modules/forms"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/password"
|
"code.gitea.io/gitea/modules/password"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
router_user_setting "code.gitea.io/gitea/routers/user/setting"
|
router_user_setting "code.gitea.io/gitea/routers/user/setting"
|
||||||
"code.gitea.io/gitea/services/mailer"
|
"code.gitea.io/gitea/services/mailer"
|
||||||
|
@ -63,7 +64,8 @@ func NewUser(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewUserPost response for adding a new user
|
// NewUserPost response for adding a new user
|
||||||
func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
|
func NewUserPost(ctx *context.Context) {
|
||||||
|
form := web.GetForm(ctx).(*auth.AdminCreateUserForm)
|
||||||
ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
|
ctx.Data["Title"] = ctx.Tr("admin.users.new_account")
|
||||||
ctx.Data["PageIsAdmin"] = true
|
ctx.Data["PageIsAdmin"] = true
|
||||||
ctx.Data["PageIsAdminUsers"] = true
|
ctx.Data["PageIsAdminUsers"] = true
|
||||||
|
@ -214,7 +216,8 @@ func EditUser(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditUserPost response for editting user
|
// EditUserPost response for editting user
|
||||||
func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
|
func EditUserPost(ctx *context.Context) {
|
||||||
|
form := web.GetForm(ctx).(*auth.AdminEditUserForm)
|
||||||
ctx.Data["Title"] = ctx.Tr("admin.users.edit_account")
|
ctx.Data["Title"] = ctx.Tr("admin.users.edit_account")
|
||||||
ctx.Data["PageIsAdmin"] = true
|
ctx.Data["PageIsAdmin"] = true
|
||||||
ctx.Data["PageIsAdminUsers"] = true
|
ctx.Data["PageIsAdminUsers"] = true
|
||||||
|
|
|
@ -8,8 +8,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
auth "code.gitea.io/gitea/modules/forms"
|
||||||
"code.gitea.io/gitea/modules/test"
|
"code.gitea.io/gitea/modules/test"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -39,7 +40,8 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
|
||||||
MustChangePassword: true,
|
MustChangePassword: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
NewUserPost(ctx, form)
|
web.SetForm(ctx, &form)
|
||||||
|
NewUserPost(ctx)
|
||||||
|
|
||||||
assert.NotEmpty(t, ctx.Flash.SuccessMsg)
|
assert.NotEmpty(t, ctx.Flash.SuccessMsg)
|
||||||
|
|
||||||
|
@ -76,7 +78,8 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
|
||||||
MustChangePassword: false,
|
MustChangePassword: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
NewUserPost(ctx, form)
|
web.SetForm(ctx, &form)
|
||||||
|
NewUserPost(ctx)
|
||||||
|
|
||||||
assert.NotEmpty(t, ctx.Flash.SuccessMsg)
|
assert.NotEmpty(t, ctx.Flash.SuccessMsg)
|
||||||
|
|
||||||
|
@ -113,7 +116,8 @@ func TestNewUserPost_InvalidEmail(t *testing.T) {
|
||||||
MustChangePassword: false,
|
MustChangePassword: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
NewUserPost(ctx, form)
|
web.SetForm(ctx, &form)
|
||||||
|
NewUserPost(ctx)
|
||||||
|
|
||||||
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
|
assert.NotEmpty(t, ctx.Flash.ErrorMsg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,13 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/user"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateOrg api for create organization
|
// CreateOrg api for create organization
|
||||||
func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
|
func CreateOrg(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /admin/users/{username}/orgs admin adminCreateOrg
|
// swagger:operation POST /admin/users/{username}/orgs admin adminCreateOrg
|
||||||
// ---
|
// ---
|
||||||
// summary: Create an organization
|
// summary: Create an organization
|
||||||
|
@ -43,7 +44,7 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateOrgOption)
|
||||||
u := user.GetUserByParams(ctx)
|
u := user.GetUserByParams(ctx)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
|
|
|
@ -7,12 +7,13 @@ package admin
|
||||||
import (
|
import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/repo"
|
||||||
"code.gitea.io/gitea/routers/api/v1/user"
|
"code.gitea.io/gitea/routers/api/v1/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateRepo api for creating a repository
|
// CreateRepo api for creating a repository
|
||||||
func CreateRepo(ctx *context.APIContext, form api.CreateRepoOption) {
|
func CreateRepo(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /admin/users/{username}/repos admin adminCreateRepo
|
// swagger:operation POST /admin/users/{username}/repos admin adminCreateRepo
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a repository on behalf of a user
|
// summary: Create a repository on behalf of a user
|
||||||
|
@ -41,11 +42,11 @@ func CreateRepo(ctx *context.APIContext, form api.CreateRepoOption) {
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateRepoOption)
|
||||||
owner := user.GetUserByParams(ctx)
|
owner := user.GetUserByParams(ctx)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.CreateUserRepo(ctx, owner, form)
|
repo.CreateUserRepo(ctx, owner, *form)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/password"
|
"code.gitea.io/gitea/modules/password"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/user"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/mailer"
|
"code.gitea.io/gitea/services/mailer"
|
||||||
|
@ -42,7 +43,7 @@ func parseLoginSource(ctx *context.APIContext, u *models.User, sourceID int64, l
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUser create a user
|
// CreateUser create a user
|
||||||
func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
|
func CreateUser(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /admin/users admin adminCreateUser
|
// swagger:operation POST /admin/users admin adminCreateUser
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a user
|
// summary: Create a user
|
||||||
|
@ -64,7 +65,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateUserOption)
|
||||||
u := &models.User{
|
u := &models.User{
|
||||||
Name: form.Username,
|
Name: form.Username,
|
||||||
FullName: form.FullName,
|
FullName: form.FullName,
|
||||||
|
@ -119,7 +120,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditUser api for modifying a user's information
|
// EditUser api for modifying a user's information
|
||||||
func EditUser(ctx *context.APIContext, form api.EditUserOption) {
|
func EditUser(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /admin/users/{username} admin adminEditUser
|
// swagger:operation PATCH /admin/users/{username} admin adminEditUser
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit an existing user
|
// summary: Edit an existing user
|
||||||
|
@ -144,7 +145,7 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) {
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.EditUserOption)
|
||||||
u := user.GetUserByParams(ctx)
|
u := user.GetUserByParams(ctx)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
|
@ -283,7 +284,7 @@ func DeleteUser(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreatePublicKey api for creating a public key to a user
|
// CreatePublicKey api for creating a public key to a user
|
||||||
func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) {
|
func CreatePublicKey(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey
|
// swagger:operation POST /admin/users/{username}/keys admin adminCreatePublicKey
|
||||||
// ---
|
// ---
|
||||||
// summary: Add a public key on behalf of a user
|
// summary: Add a public key on behalf of a user
|
||||||
|
@ -308,12 +309,12 @@ func CreatePublicKey(ctx *context.APIContext, form api.CreateKeyOption) {
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateKeyOption)
|
||||||
u := user.GetUserByParams(ctx)
|
u := user.GetUserByParams(ctx)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user.CreateUserPublicKey(ctx, form, u.ID)
|
user.CreateUserPublicKey(ctx, *form, u.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteUserPublicKey api for deleting a user's public key
|
// DeleteUserPublicKey api for deleting a user's public key
|
||||||
|
|
|
@ -66,14 +66,16 @@ package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/auth"
|
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
auth "code.gitea.io/gitea/modules/forms"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/admin"
|
"code.gitea.io/gitea/routers/api/v1/admin"
|
||||||
"code.gitea.io/gitea/routers/api/v1/misc"
|
"code.gitea.io/gitea/routers/api/v1/misc"
|
||||||
"code.gitea.io/gitea/routers/api/v1/notify"
|
"code.gitea.io/gitea/routers/api/v1/notify"
|
||||||
|
@ -83,11 +85,12 @@ import (
|
||||||
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
|
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
|
||||||
"code.gitea.io/gitea/routers/api/v1/user"
|
"code.gitea.io/gitea/routers/api/v1/user"
|
||||||
|
|
||||||
"gitea.com/macaron/binding"
|
"gitea.com/go-chi/binding"
|
||||||
"gitea.com/macaron/macaron"
|
"gitea.com/go-chi/session"
|
||||||
|
"github.com/go-chi/cors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sudo() macaron.Handler {
|
func sudo() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
sudo := ctx.Query("sudo")
|
sudo := ctx.Query("sudo")
|
||||||
if len(sudo) == 0 {
|
if len(sudo) == 0 {
|
||||||
|
@ -117,10 +120,10 @@ func sudo() macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func repoAssignment() macaron.Handler {
|
func repoAssignment() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
userName := ctx.Params(":username")
|
userName := ctx.Params("username")
|
||||||
repoName := ctx.Params(":reponame")
|
repoName := ctx.Params("reponame")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
owner *models.User
|
owner *models.User
|
||||||
|
@ -184,7 +187,7 @@ func repoAssignment() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contexter middleware already checks token for user sign in process.
|
// Contexter middleware already checks token for user sign in process.
|
||||||
func reqToken() macaron.Handler {
|
func reqToken() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if true == ctx.Data["IsApiToken"] {
|
if true == ctx.Data["IsApiToken"] {
|
||||||
return
|
return
|
||||||
|
@ -201,7 +204,7 @@ func reqToken() macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reqBasicAuth() macaron.Handler {
|
func reqBasicAuth() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.Context.IsBasicAuth {
|
if !ctx.Context.IsBasicAuth {
|
||||||
ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "basic auth required")
|
ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "basic auth required")
|
||||||
|
@ -212,7 +215,7 @@ func reqBasicAuth() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqSiteAdmin user should be the site admin
|
// reqSiteAdmin user should be the site admin
|
||||||
func reqSiteAdmin() macaron.Handler {
|
func reqSiteAdmin() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.IsUserSiteAdmin() {
|
if !ctx.IsUserSiteAdmin() {
|
||||||
ctx.Error(http.StatusForbidden, "reqSiteAdmin", "user should be the site admin")
|
ctx.Error(http.StatusForbidden, "reqSiteAdmin", "user should be the site admin")
|
||||||
|
@ -222,7 +225,7 @@ func reqSiteAdmin() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqOwner user should be the owner of the repo or site admin.
|
// reqOwner user should be the owner of the repo or site admin.
|
||||||
func reqOwner() macaron.Handler {
|
func reqOwner() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.IsUserRepoOwner() && !ctx.IsUserSiteAdmin() {
|
if !ctx.IsUserRepoOwner() && !ctx.IsUserSiteAdmin() {
|
||||||
ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo")
|
ctx.Error(http.StatusForbidden, "reqOwner", "user should be the owner of the repo")
|
||||||
|
@ -232,7 +235,7 @@ func reqOwner() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin
|
// reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin
|
||||||
func reqAdmin() macaron.Handler {
|
func reqAdmin() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
||||||
ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository")
|
ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository")
|
||||||
|
@ -242,7 +245,7 @@ func reqAdmin() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqRepoWriter user should have a permission to write to a repo, or be a site admin
|
// reqRepoWriter user should have a permission to write to a repo, or be a site admin
|
||||||
func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler {
|
func reqRepoWriter(unitTypes ...models.UnitType) func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
||||||
ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo")
|
ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo")
|
||||||
|
@ -252,7 +255,7 @@ func reqRepoWriter(unitTypes ...models.UnitType) macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqRepoReader user should have specific read permission or be a repo admin or a site admin
|
// reqRepoReader user should have specific read permission or be a repo admin or a site admin
|
||||||
func reqRepoReader(unitType models.UnitType) macaron.Handler {
|
func reqRepoReader(unitType models.UnitType) func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.IsUserRepoReaderSpecific(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
if !ctx.IsUserRepoReaderSpecific(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() {
|
||||||
ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin")
|
ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin")
|
||||||
|
@ -262,7 +265,7 @@ func reqRepoReader(unitType models.UnitType) macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqAnyRepoReader user should have any permission to read repository or permissions of site admin
|
// reqAnyRepoReader user should have any permission to read repository or permissions of site admin
|
||||||
func reqAnyRepoReader() macaron.Handler {
|
func reqAnyRepoReader() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.IsUserRepoReaderAny() && !ctx.IsUserSiteAdmin() {
|
if !ctx.IsUserRepoReaderAny() && !ctx.IsUserSiteAdmin() {
|
||||||
ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin")
|
ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin")
|
||||||
|
@ -272,7 +275,7 @@ func reqAnyRepoReader() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqOrgOwnership user should be an organization owner, or a site admin
|
// reqOrgOwnership user should be an organization owner, or a site admin
|
||||||
func reqOrgOwnership() macaron.Handler {
|
func reqOrgOwnership() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if ctx.Context.IsUserSiteAdmin() {
|
if ctx.Context.IsUserSiteAdmin() {
|
||||||
return
|
return
|
||||||
|
@ -304,7 +307,7 @@ func reqOrgOwnership() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqTeamMembership user should be an team member, or a site admin
|
// reqTeamMembership user should be an team member, or a site admin
|
||||||
func reqTeamMembership() macaron.Handler {
|
func reqTeamMembership() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if ctx.Context.IsUserSiteAdmin() {
|
if ctx.Context.IsUserSiteAdmin() {
|
||||||
return
|
return
|
||||||
|
@ -341,7 +344,7 @@ func reqTeamMembership() macaron.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reqOrgMembership user should be an organization member, or a site admin
|
// reqOrgMembership user should be an organization member, or a site admin
|
||||||
func reqOrgMembership() macaron.Handler {
|
func reqOrgMembership() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if ctx.Context.IsUserSiteAdmin() {
|
if ctx.Context.IsUserSiteAdmin() {
|
||||||
return
|
return
|
||||||
|
@ -371,7 +374,7 @@ func reqOrgMembership() macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reqGitHook() macaron.Handler {
|
func reqGitHook() func(ctx *context.APIContext) {
|
||||||
return func(ctx *context.APIContext) {
|
return func(ctx *context.APIContext) {
|
||||||
if !ctx.User.CanEditGitHook() {
|
if !ctx.User.CanEditGitHook() {
|
||||||
ctx.Error(http.StatusForbidden, "", "must be allowed to edit Git hooks")
|
ctx.Error(http.StatusForbidden, "", "must be allowed to edit Git hooks")
|
||||||
|
@ -380,7 +383,7 @@ func reqGitHook() macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func orgAssignment(args ...bool) macaron.Handler {
|
func orgAssignment(args ...bool) func(ctx *context.APIContext) {
|
||||||
var (
|
var (
|
||||||
assignOrg bool
|
assignOrg bool
|
||||||
assignTeam bool
|
assignTeam bool
|
||||||
|
@ -500,13 +503,6 @@ func mustEnableIssuesOrPulls(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustEnableUserHeatmap(ctx *context.APIContext) {
|
|
||||||
if !setting.Service.EnableUserHeatmap {
|
|
||||||
ctx.NotFound()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustNotBeArchived(ctx *context.APIContext) {
|
func mustNotBeArchived(ctx *context.APIContext) {
|
||||||
if ctx.Repo.Repository.IsArchived {
|
if ctx.Repo.Repository.IsArchived {
|
||||||
ctx.NotFound()
|
ctx.NotFound()
|
||||||
|
@ -514,18 +510,59 @@ func mustNotBeArchived(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterRoutes registers all v1 APIs routes to web application.
|
// bind binding an obj to a func(ctx *context.APIContext)
|
||||||
func RegisterRoutes(m *macaron.Macaron) {
|
func bind(obj interface{}) http.HandlerFunc {
|
||||||
bind := binding.Bind
|
var tp = reflect.TypeOf(obj)
|
||||||
|
for tp.Kind() == reflect.Ptr {
|
||||||
if setting.API.EnableSwagger {
|
tp = tp.Elem()
|
||||||
m.Get("/swagger", misc.Swagger) // Render V1 by default
|
|
||||||
}
|
}
|
||||||
|
return web.Wrap(func(ctx *context.APIContext) {
|
||||||
|
var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly
|
||||||
|
errs := binding.Bind(ctx.Req, theObj)
|
||||||
|
if len(errs) > 0 {
|
||||||
|
ctx.Error(422, "validationError", errs[0].Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
web.SetForm(ctx, theObj)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
m.Group("/v1", func() {
|
// Routes registers all v1 APIs routes to web application.
|
||||||
|
func Routes() *web.Route {
|
||||||
|
var m = web.NewRoute()
|
||||||
|
|
||||||
|
m.Use(session.Sessioner(session.Options{
|
||||||
|
Provider: setting.SessionConfig.Provider,
|
||||||
|
ProviderConfig: setting.SessionConfig.ProviderConfig,
|
||||||
|
CookieName: setting.SessionConfig.CookieName,
|
||||||
|
CookiePath: setting.SessionConfig.CookiePath,
|
||||||
|
Gclifetime: setting.SessionConfig.Gclifetime,
|
||||||
|
Maxlifetime: setting.SessionConfig.Maxlifetime,
|
||||||
|
Secure: setting.SessionConfig.Secure,
|
||||||
|
Domain: setting.SessionConfig.Domain,
|
||||||
|
}))
|
||||||
|
m.Use(securityHeaders())
|
||||||
|
if setting.CORSConfig.Enabled {
|
||||||
|
m.Use(cors.Handler(cors.Options{
|
||||||
|
//Scheme: setting.CORSConfig.Scheme, // FIXME: the cors middleware needs scheme option
|
||||||
|
AllowedOrigins: setting.CORSConfig.AllowDomain,
|
||||||
|
//setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option
|
||||||
|
AllowedMethods: setting.CORSConfig.Methods,
|
||||||
|
AllowCredentials: setting.CORSConfig.AllowCredentials,
|
||||||
|
MaxAge: int(setting.CORSConfig.MaxAge.Seconds()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
m.Use(context.APIContexter())
|
||||||
|
m.Use(context.ToggleAPI(&context.ToggleOptions{
|
||||||
|
SignInRequired: setting.Service.RequireSignInView,
|
||||||
|
}))
|
||||||
|
|
||||||
|
m.Group("", func() {
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
if setting.API.EnableSwagger {
|
if setting.API.EnableSwagger {
|
||||||
m.Get("/swagger", misc.Swagger)
|
m.Get("/swagger", func(ctx *context.APIContext) {
|
||||||
|
ctx.Redirect("/api/swagger")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
m.Get("/version", misc.Version)
|
m.Get("/version", misc.Version)
|
||||||
m.Get("/signing-key.gpg", misc.SigningKey)
|
m.Get("/signing-key.gpg", misc.SigningKey)
|
||||||
|
@ -544,7 +581,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
Get(notify.ListNotifications).
|
Get(notify.ListNotifications).
|
||||||
Put(notify.ReadNotifications)
|
Put(notify.ReadNotifications)
|
||||||
m.Get("/new", notify.NewAvailable)
|
m.Get("/new", notify.NewAvailable)
|
||||||
m.Combo("/threads/:id").
|
m.Combo("/threads/{id}").
|
||||||
Get(notify.GetThread).
|
Get(notify.GetThread).
|
||||||
Patch(notify.ReadThread)
|
Patch(notify.ReadThread)
|
||||||
}, reqToken())
|
}, reqToken())
|
||||||
|
@ -553,28 +590,31 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/users", func() {
|
m.Group("/users", func() {
|
||||||
m.Get("/search", user.Search)
|
m.Get("/search", user.Search)
|
||||||
|
|
||||||
m.Group("/:username", func() {
|
m.Group("/{username}", func() {
|
||||||
m.Get("", user.GetInfo)
|
m.Get("", user.GetInfo)
|
||||||
m.Get("/heatmap", mustEnableUserHeatmap, user.GetUserHeatmapData)
|
|
||||||
|
if setting.Service.EnableUserHeatmap {
|
||||||
|
m.Get("/heatmap", user.GetUserHeatmapData)
|
||||||
|
}
|
||||||
|
|
||||||
m.Get("/repos", user.ListUserRepos)
|
m.Get("/repos", user.ListUserRepos)
|
||||||
m.Group("/tokens", func() {
|
m.Group("/tokens", func() {
|
||||||
m.Combo("").Get(user.ListAccessTokens).
|
m.Combo("").Get(user.ListAccessTokens).
|
||||||
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
|
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
|
||||||
m.Combo("/:id").Delete(user.DeleteAccessToken)
|
m.Combo("/{id}").Delete(user.DeleteAccessToken)
|
||||||
}, reqBasicAuth())
|
}, reqBasicAuth())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Group("/users", func() {
|
m.Group("/users", func() {
|
||||||
m.Group("/:username", func() {
|
m.Group("/{username}", func() {
|
||||||
m.Get("/keys", user.ListPublicKeys)
|
m.Get("/keys", user.ListPublicKeys)
|
||||||
m.Get("/gpg_keys", user.ListGPGKeys)
|
m.Get("/gpg_keys", user.ListGPGKeys)
|
||||||
|
|
||||||
m.Get("/followers", user.ListFollowers)
|
m.Get("/followers", user.ListFollowers)
|
||||||
m.Group("/following", func() {
|
m.Group("/following", func() {
|
||||||
m.Get("", user.ListFollowing)
|
m.Get("", user.ListFollowing)
|
||||||
m.Get("/:target", user.CheckFollowing)
|
m.Get("/{target}", user.CheckFollowing)
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Get("/starred", user.GetStarredRepos)
|
m.Get("/starred", user.GetStarredRepos)
|
||||||
|
@ -592,20 +632,20 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Get("/followers", user.ListMyFollowers)
|
m.Get("/followers", user.ListMyFollowers)
|
||||||
m.Group("/following", func() {
|
m.Group("/following", func() {
|
||||||
m.Get("", user.ListMyFollowing)
|
m.Get("", user.ListMyFollowing)
|
||||||
m.Combo("/:username").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow)
|
m.Combo("/{username}").Get(user.CheckMyFollowing).Put(user.Follow).Delete(user.Unfollow)
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Group("/keys", func() {
|
m.Group("/keys", func() {
|
||||||
m.Combo("").Get(user.ListMyPublicKeys).
|
m.Combo("").Get(user.ListMyPublicKeys).
|
||||||
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
|
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
|
||||||
m.Combo("/:id").Get(user.GetPublicKey).
|
m.Combo("/{id}").Get(user.GetPublicKey).
|
||||||
Delete(user.DeletePublicKey)
|
Delete(user.DeletePublicKey)
|
||||||
})
|
})
|
||||||
m.Group("/applications", func() {
|
m.Group("/applications", func() {
|
||||||
m.Combo("/oauth2").
|
m.Combo("/oauth2").
|
||||||
Get(user.ListOauth2Applications).
|
Get(user.ListOauth2Applications).
|
||||||
Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application)
|
Post(bind(api.CreateOAuth2ApplicationOptions{}), user.CreateOauth2Application)
|
||||||
m.Combo("/oauth2/:id").
|
m.Combo("/oauth2/{id}").
|
||||||
Delete(user.DeleteOauth2Application).
|
Delete(user.DeleteOauth2Application).
|
||||||
Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application).
|
Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application).
|
||||||
Get(user.GetOauth2Application)
|
Get(user.GetOauth2Application)
|
||||||
|
@ -614,7 +654,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/gpg_keys", func() {
|
m.Group("/gpg_keys", func() {
|
||||||
m.Combo("").Get(user.ListMyGPGKeys).
|
m.Combo("").Get(user.ListMyGPGKeys).
|
||||||
Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey)
|
Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey)
|
||||||
m.Combo("/:id").Get(user.GetGPGKey).
|
m.Combo("/{id}").Get(user.GetGPGKey).
|
||||||
Delete(user.DeleteGPGKey)
|
Delete(user.DeleteGPGKey)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -623,7 +663,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
|
|
||||||
m.Group("/starred", func() {
|
m.Group("/starred", func() {
|
||||||
m.Get("", user.GetMyStarredRepos)
|
m.Get("", user.GetMyStarredRepos)
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/{username}/{reponame}", func() {
|
||||||
m.Get("", user.IsStarring)
|
m.Get("", user.IsStarring)
|
||||||
m.Put("", user.Star)
|
m.Put("", user.Star)
|
||||||
m.Delete("", user.Unstar)
|
m.Delete("", user.Unstar)
|
||||||
|
@ -639,9 +679,9 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
}, reqToken())
|
}, reqToken())
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
m.Post("/org/:org/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated)
|
m.Post("/org/{org}/repos", reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated)
|
||||||
|
|
||||||
m.Combo("/repositories/:id", reqToken()).Get(repo.GetByID)
|
m.Combo("/repositories/{id}", reqToken()).Get(repo.GetByID)
|
||||||
|
|
||||||
m.Group("/repos", func() {
|
m.Group("/repos", func() {
|
||||||
m.Get("/search", repo.Search)
|
m.Get("/search", repo.Search)
|
||||||
|
@ -650,10 +690,10 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
|
|
||||||
m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate)
|
m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate)
|
||||||
|
|
||||||
m.Group("/:username/:reponame", func() {
|
m.Group("/{username}/{reponame}", func() {
|
||||||
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
||||||
Delete(reqToken(), reqOwner(), repo.Delete).
|
Delete(reqToken(), reqOwner(), repo.Delete).
|
||||||
Patch(reqToken(), reqAdmin(), context.RepoRefForAPI(), bind(api.EditRepoOption{}), repo.Edit)
|
Patch(reqToken(), reqAdmin(), context.RepoRefForAPI, bind(api.EditRepoOption{}), repo.Edit)
|
||||||
m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer)
|
m.Post("/transfer", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer)
|
||||||
m.Combo("/notifications").
|
m.Combo("/notifications").
|
||||||
Get(reqToken(), notify.ListRepoNotifications).
|
Get(reqToken(), notify.ListRepoNotifications).
|
||||||
|
@ -661,15 +701,15 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Combo("").Get(repo.ListHooks).
|
m.Combo("").Get(repo.ListHooks).
|
||||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||||
m.Group("/:id", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Combo("").Get(repo.GetHook).
|
m.Combo("").Get(repo.GetHook).
|
||||||
Patch(bind(api.EditHookOption{}), repo.EditHook).
|
Patch(bind(api.EditHookOption{}), repo.EditHook).
|
||||||
Delete(repo.DeleteHook)
|
Delete(repo.DeleteHook)
|
||||||
m.Post("/tests", context.RepoRefForAPI(), repo.TestHook)
|
m.Post("/tests", context.RepoRefForAPI, repo.TestHook)
|
||||||
})
|
})
|
||||||
m.Group("/git", func() {
|
m.Group("/git", func() {
|
||||||
m.Combo("").Get(repo.ListGitHooks)
|
m.Combo("").Get(repo.ListGitHooks)
|
||||||
m.Group("/:id", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Combo("").Get(repo.GetGitHook).
|
m.Combo("").Get(repo.GetGitHook).
|
||||||
Patch(bind(api.EditGitHookOption{}), repo.EditGitHook).
|
Patch(bind(api.EditGitHookOption{}), repo.EditGitHook).
|
||||||
Delete(repo.DeleteGitHook)
|
Delete(repo.DeleteGitHook)
|
||||||
|
@ -678,11 +718,11 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
}, reqToken(), reqAdmin())
|
}, reqToken(), reqAdmin())
|
||||||
m.Group("/collaborators", func() {
|
m.Group("/collaborators", func() {
|
||||||
m.Get("", reqAnyRepoReader(), repo.ListCollaborators)
|
m.Get("", reqAnyRepoReader(), repo.ListCollaborators)
|
||||||
m.Combo("/:collaborator").Get(reqAnyRepoReader(), repo.IsCollaborator).
|
m.Combo("/{collaborator}").Get(reqAnyRepoReader(), repo.IsCollaborator).
|
||||||
Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
|
Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
|
||||||
Delete(reqAdmin(), repo.DeleteCollaborator)
|
Delete(reqAdmin(), repo.DeleteCollaborator)
|
||||||
}, reqToken())
|
}, reqToken())
|
||||||
m.Get("/raw/*", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetRawFile)
|
m.Get("/raw/*", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetRawFile)
|
||||||
m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive)
|
m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive)
|
||||||
m.Combo("/forks").Get(repo.ListForks).
|
m.Combo("/forks").Get(repo.ListForks).
|
||||||
Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
|
Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
|
||||||
|
@ -695,7 +735,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/branch_protections", func() {
|
m.Group("/branch_protections", func() {
|
||||||
m.Get("", repo.ListBranchProtections)
|
m.Get("", repo.ListBranchProtections)
|
||||||
m.Post("", bind(api.CreateBranchProtectionOption{}), repo.CreateBranchProtection)
|
m.Post("", bind(api.CreateBranchProtectionOption{}), repo.CreateBranchProtection)
|
||||||
m.Group("/:name", func() {
|
m.Group("/{name}", func() {
|
||||||
m.Get("", repo.GetBranchProtection)
|
m.Get("", repo.GetBranchProtection)
|
||||||
m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection)
|
m.Patch("", bind(api.EditBranchProtectionOption{}), repo.EditBranchProtection)
|
||||||
m.Delete("", repo.DeleteBranchProtection)
|
m.Delete("", repo.DeleteBranchProtection)
|
||||||
|
@ -707,19 +747,19 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/keys", func() {
|
m.Group("/keys", func() {
|
||||||
m.Combo("").Get(repo.ListDeployKeys).
|
m.Combo("").Get(repo.ListDeployKeys).
|
||||||
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
|
Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
|
||||||
m.Combo("/:id").Get(repo.GetDeployKey).
|
m.Combo("/{id}").Get(repo.GetDeployKey).
|
||||||
Delete(repo.DeleteDeploykey)
|
Delete(repo.DeleteDeploykey)
|
||||||
}, reqToken(), reqAdmin())
|
}, reqToken(), reqAdmin())
|
||||||
m.Group("/times", func() {
|
m.Group("/times", func() {
|
||||||
m.Combo("").Get(repo.ListTrackedTimesByRepository)
|
m.Combo("").Get(repo.ListTrackedTimesByRepository)
|
||||||
m.Combo("/:timetrackingusername").Get(repo.ListTrackedTimesByUser)
|
m.Combo("/{timetrackingusername}").Get(repo.ListTrackedTimesByUser)
|
||||||
}, mustEnableIssues, reqToken())
|
}, mustEnableIssues, reqToken())
|
||||||
m.Group("/issues", func() {
|
m.Group("/issues", func() {
|
||||||
m.Combo("").Get(repo.ListIssues).
|
m.Combo("").Get(repo.ListIssues).
|
||||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
|
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueOption{}), repo.CreateIssue)
|
||||||
m.Group("/comments", func() {
|
m.Group("/comments", func() {
|
||||||
m.Get("", repo.ListRepoIssueComments)
|
m.Get("", repo.ListRepoIssueComments)
|
||||||
m.Group("/:id", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Combo("").
|
m.Combo("").
|
||||||
Get(repo.GetIssueComment).
|
Get(repo.GetIssueComment).
|
||||||
Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
|
Patch(mustNotBeArchived, reqToken(), bind(api.EditIssueCommentOption{}), repo.EditIssueComment).
|
||||||
|
@ -730,13 +770,13 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction)
|
Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
m.Group("/:index", func() {
|
m.Group("/{index}", func() {
|
||||||
m.Combo("").Get(repo.GetIssue).
|
m.Combo("").Get(repo.GetIssue).
|
||||||
Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue)
|
Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue)
|
||||||
m.Group("/comments", func() {
|
m.Group("/comments", func() {
|
||||||
m.Combo("").Get(repo.ListIssueComments).
|
m.Combo("").Get(repo.ListIssueComments).
|
||||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
|
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
|
||||||
m.Combo("/:id", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated).
|
m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated).
|
||||||
Delete(repo.DeleteIssueCommentDeprecated)
|
Delete(repo.DeleteIssueCommentDeprecated)
|
||||||
})
|
})
|
||||||
m.Group("/labels", func() {
|
m.Group("/labels", func() {
|
||||||
|
@ -744,14 +784,14 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels).
|
Post(reqToken(), bind(api.IssueLabelsOption{}), repo.AddIssueLabels).
|
||||||
Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels).
|
Put(reqToken(), bind(api.IssueLabelsOption{}), repo.ReplaceIssueLabels).
|
||||||
Delete(reqToken(), repo.ClearIssueLabels)
|
Delete(reqToken(), repo.ClearIssueLabels)
|
||||||
m.Delete("/:id", reqToken(), repo.DeleteIssueLabel)
|
m.Delete("/{id}", reqToken(), repo.DeleteIssueLabel)
|
||||||
})
|
})
|
||||||
m.Group("/times", func() {
|
m.Group("/times", func() {
|
||||||
m.Combo("").
|
m.Combo("").
|
||||||
Get(repo.ListTrackedTimes).
|
Get(repo.ListTrackedTimes).
|
||||||
Post(bind(api.AddTimeOption{}), repo.AddTime).
|
Post(bind(api.AddTimeOption{}), repo.AddTime).
|
||||||
Delete(repo.ResetIssueTime)
|
Delete(repo.ResetIssueTime)
|
||||||
m.Delete("/:id", repo.DeleteTime)
|
m.Delete("/{id}", repo.DeleteTime)
|
||||||
}, reqToken())
|
}, reqToken())
|
||||||
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
|
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
|
||||||
m.Group("/stopwatch", func() {
|
m.Group("/stopwatch", func() {
|
||||||
|
@ -762,8 +802,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/subscriptions", func() {
|
m.Group("/subscriptions", func() {
|
||||||
m.Get("", repo.GetIssueSubscribers)
|
m.Get("", repo.GetIssueSubscribers)
|
||||||
m.Get("/check", reqToken(), repo.CheckIssueSubscription)
|
m.Get("/check", reqToken(), repo.CheckIssueSubscription)
|
||||||
m.Put("/:user", reqToken(), repo.AddIssueSubscription)
|
m.Put("/{user}", reqToken(), repo.AddIssueSubscription)
|
||||||
m.Delete("/:user", reqToken(), repo.DelIssueSubscription)
|
m.Delete("/{user}", reqToken(), repo.DelIssueSubscription)
|
||||||
})
|
})
|
||||||
m.Combo("/reactions").
|
m.Combo("/reactions").
|
||||||
Get(repo.GetIssueReactions).
|
Get(repo.GetIssueReactions).
|
||||||
|
@ -774,7 +814,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/labels", func() {
|
m.Group("/labels", func() {
|
||||||
m.Combo("").Get(repo.ListLabels).
|
m.Combo("").Get(repo.ListLabels).
|
||||||
Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel)
|
Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateLabelOption{}), repo.CreateLabel)
|
||||||
m.Combo("/:id").Get(repo.GetLabel).
|
m.Combo("/{id}").Get(repo.GetLabel).
|
||||||
Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel).
|
Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel).
|
||||||
Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel)
|
Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel)
|
||||||
})
|
})
|
||||||
|
@ -783,7 +823,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/milestones", func() {
|
m.Group("/milestones", func() {
|
||||||
m.Combo("").Get(repo.ListMilestones).
|
m.Combo("").Get(repo.ListMilestones).
|
||||||
Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
|
Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone)
|
||||||
m.Combo("/:id").Get(repo.GetMilestone).
|
m.Combo("/{id}").Get(repo.GetMilestone).
|
||||||
Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone).
|
Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone).
|
||||||
Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteMilestone)
|
Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteMilestone)
|
||||||
})
|
})
|
||||||
|
@ -797,30 +837,30 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/releases", func() {
|
m.Group("/releases", func() {
|
||||||
m.Combo("").Get(repo.ListReleases).
|
m.Combo("").Get(repo.ListReleases).
|
||||||
Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.CreateReleaseOption{}), repo.CreateRelease)
|
Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.CreateReleaseOption{}), repo.CreateRelease)
|
||||||
m.Group("/:id", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Combo("").Get(repo.GetRelease).
|
m.Combo("").Get(repo.GetRelease).
|
||||||
Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.EditReleaseOption{}), repo.EditRelease).
|
Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), context.ReferencesGitRepo(false), bind(api.EditReleaseOption{}), repo.EditRelease).
|
||||||
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteRelease)
|
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteRelease)
|
||||||
m.Group("/assets", func() {
|
m.Group("/assets", func() {
|
||||||
m.Combo("").Get(repo.ListReleaseAttachments).
|
m.Combo("").Get(repo.ListReleaseAttachments).
|
||||||
Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.CreateReleaseAttachment)
|
Post(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.CreateReleaseAttachment)
|
||||||
m.Combo("/:asset").Get(repo.GetReleaseAttachment).
|
m.Combo("/{asset}").Get(repo.GetReleaseAttachment).
|
||||||
Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment).
|
Patch(reqToken(), reqRepoWriter(models.UnitTypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment).
|
||||||
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseAttachment)
|
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseAttachment)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
m.Group("/tags", func() {
|
m.Group("/tags", func() {
|
||||||
m.Combo("/:tag").
|
m.Combo("/{tag}").
|
||||||
Get(repo.GetReleaseTag).
|
Get(repo.GetReleaseTag).
|
||||||
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseTag)
|
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseTag)
|
||||||
})
|
})
|
||||||
}, reqRepoReader(models.UnitTypeReleases))
|
}, reqRepoReader(models.UnitTypeReleases))
|
||||||
m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync)
|
m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync)
|
||||||
m.Get("/editorconfig/:filename", context.RepoRefForAPI(), reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig)
|
m.Get("/editorconfig/{filename}", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetEditorconfig)
|
||||||
m.Group("/pulls", func() {
|
m.Group("/pulls", func() {
|
||||||
m.Combo("").Get(bind(api.ListPullRequestsOptions{}), repo.ListPullRequests).
|
m.Combo("").Get(repo.ListPullRequests).
|
||||||
Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)
|
Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest)
|
||||||
m.Group("/:index", func() {
|
m.Group("/{index}", func() {
|
||||||
m.Combo("").Get(repo.GetPullRequest).
|
m.Combo("").Get(repo.GetPullRequest).
|
||||||
Patch(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(api.EditPullRequestOption{}), repo.EditPullRequest)
|
Patch(reqToken(), reqRepoWriter(models.UnitTypePullRequests), bind(api.EditPullRequestOption{}), repo.EditPullRequest)
|
||||||
m.Get(".diff", repo.DownloadPullDiff)
|
m.Get(".diff", repo.DownloadPullDiff)
|
||||||
|
@ -832,7 +872,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Combo("").
|
m.Combo("").
|
||||||
Get(repo.ListPullReviews).
|
Get(repo.ListPullReviews).
|
||||||
Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview)
|
Post(reqToken(), bind(api.CreatePullReviewOptions{}), repo.CreatePullReview)
|
||||||
m.Group("/:id", func() {
|
m.Group("/{id}", func() {
|
||||||
m.Combo("").
|
m.Combo("").
|
||||||
Get(repo.GetPullReview).
|
Get(repo.GetPullReview).
|
||||||
Delete(reqToken(), repo.DeletePullReview).
|
Delete(reqToken(), repo.DeletePullReview).
|
||||||
|
@ -847,25 +887,25 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
})
|
})
|
||||||
}, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(false))
|
}, mustAllowPulls, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(false))
|
||||||
m.Group("/statuses", func() {
|
m.Group("/statuses", func() {
|
||||||
m.Combo("/:sha").Get(repo.GetCommitStatuses).
|
m.Combo("/{sha}").Get(repo.GetCommitStatuses).
|
||||||
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
|
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
|
||||||
}, reqRepoReader(models.UnitTypeCode))
|
}, reqRepoReader(models.UnitTypeCode))
|
||||||
m.Group("/commits", func() {
|
m.Group("/commits", func() {
|
||||||
m.Get("", repo.GetAllCommits)
|
m.Get("", repo.GetAllCommits)
|
||||||
m.Group("/:ref", func() {
|
m.Group("/{ref}", func() {
|
||||||
m.Get("/status", repo.GetCombinedCommitStatusByRef)
|
m.Get("/status", repo.GetCombinedCommitStatusByRef)
|
||||||
m.Get("/statuses", repo.GetCommitStatusesByRef)
|
m.Get("/statuses", repo.GetCommitStatusesByRef)
|
||||||
})
|
})
|
||||||
}, reqRepoReader(models.UnitTypeCode))
|
}, reqRepoReader(models.UnitTypeCode))
|
||||||
m.Group("/git", func() {
|
m.Group("/git", func() {
|
||||||
m.Group("/commits", func() {
|
m.Group("/commits", func() {
|
||||||
m.Get("/:sha", repo.GetSingleCommit)
|
m.Get("/{sha}", repo.GetSingleCommit)
|
||||||
})
|
})
|
||||||
m.Get("/refs", repo.GetGitAllRefs)
|
m.Get("/refs", repo.GetGitAllRefs)
|
||||||
m.Get("/refs/*", repo.GetGitRefs)
|
m.Get("/refs/*", repo.GetGitRefs)
|
||||||
m.Get("/trees/:sha", context.RepoRefForAPI(), repo.GetTree)
|
m.Get("/trees/{sha}", context.RepoRefForAPI, repo.GetTree)
|
||||||
m.Get("/blobs/:sha", context.RepoRefForAPI(), repo.GetBlob)
|
m.Get("/blobs/{sha}", context.RepoRefForAPI, repo.GetBlob)
|
||||||
m.Get("/tags/:sha", context.RepoRefForAPI(), repo.GetTag)
|
m.Get("/tags/{sha}", context.RepoRefForAPI, repo.GetTag)
|
||||||
}, reqRepoReader(models.UnitTypeCode))
|
}, reqRepoReader(models.UnitTypeCode))
|
||||||
m.Group("/contents", func() {
|
m.Group("/contents", func() {
|
||||||
m.Get("", repo.GetContentsList)
|
m.Get("", repo.GetContentsList)
|
||||||
|
@ -880,7 +920,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/topics", func() {
|
m.Group("/topics", func() {
|
||||||
m.Combo("").Get(repo.ListTopics).
|
m.Combo("").Get(repo.ListTopics).
|
||||||
Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics)
|
Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics)
|
||||||
m.Group("/:topic", func() {
|
m.Group("/{topic}", func() {
|
||||||
m.Combo("").Put(reqToken(), repo.AddTopic).
|
m.Combo("").Put(reqToken(), repo.AddTopic).
|
||||||
Delete(reqToken(), repo.DeleteTopic)
|
Delete(reqToken(), repo.DeleteTopic)
|
||||||
}, reqAdmin())
|
}, reqAdmin())
|
||||||
|
@ -892,10 +932,10 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
|
|
||||||
// Organizations
|
// Organizations
|
||||||
m.Get("/user/orgs", reqToken(), org.ListMyOrgs)
|
m.Get("/user/orgs", reqToken(), org.ListMyOrgs)
|
||||||
m.Get("/users/:username/orgs", org.ListUserOrgs)
|
m.Get("/users/{username}/orgs", org.ListUserOrgs)
|
||||||
m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create)
|
m.Post("/orgs", reqToken(), bind(api.CreateOrgOption{}), org.Create)
|
||||||
m.Get("/orgs", org.GetAll)
|
m.Get("/orgs", org.GetAll)
|
||||||
m.Group("/orgs/:org", func() {
|
m.Group("/orgs/{org}", func() {
|
||||||
m.Combo("").Get(org.Get).
|
m.Combo("").Get(org.Get).
|
||||||
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit).
|
Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit).
|
||||||
Delete(reqToken(), reqOrgOwnership(), org.Delete)
|
Delete(reqToken(), reqOrgOwnership(), org.Delete)
|
||||||
|
@ -903,12 +943,12 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
|
Post(reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
|
||||||
m.Group("/members", func() {
|
m.Group("/members", func() {
|
||||||
m.Get("", org.ListMembers)
|
m.Get("", org.ListMembers)
|
||||||
m.Combo("/:username").Get(org.IsMember).
|
m.Combo("/{username}").Get(org.IsMember).
|
||||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteMember)
|
Delete(reqToken(), reqOrgOwnership(), org.DeleteMember)
|
||||||
})
|
})
|
||||||
m.Group("/public_members", func() {
|
m.Group("/public_members", func() {
|
||||||
m.Get("", org.ListPublicMembers)
|
m.Get("", org.ListPublicMembers)
|
||||||
m.Combo("/:username").Get(org.IsPublicMember).
|
m.Combo("/{username}").Get(org.IsPublicMember).
|
||||||
Put(reqToken(), reqOrgMembership(), org.PublicizeMember).
|
Put(reqToken(), reqOrgMembership(), org.PublicizeMember).
|
||||||
Delete(reqToken(), reqOrgMembership(), org.ConcealMember)
|
Delete(reqToken(), reqOrgMembership(), org.ConcealMember)
|
||||||
})
|
})
|
||||||
|
@ -920,56 +960,52 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Group("/labels", func() {
|
m.Group("/labels", func() {
|
||||||
m.Get("", org.ListLabels)
|
m.Get("", org.ListLabels)
|
||||||
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel)
|
m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel)
|
||||||
m.Combo("/:id").Get(org.GetLabel).
|
m.Combo("/{id}").Get(org.GetLabel).
|
||||||
Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel).
|
Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel).
|
||||||
Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel)
|
Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel)
|
||||||
})
|
})
|
||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Combo("").Get(org.ListHooks).
|
m.Combo("").Get(org.ListHooks).
|
||||||
Post(bind(api.CreateHookOption{}), org.CreateHook)
|
Post(bind(api.CreateHookOption{}), org.CreateHook)
|
||||||
m.Combo("/:id").Get(org.GetHook).
|
m.Combo("/{id}").Get(org.GetHook).
|
||||||
Patch(bind(api.EditHookOption{}), org.EditHook).
|
Patch(bind(api.EditHookOption{}), org.EditHook).
|
||||||
Delete(org.DeleteHook)
|
Delete(org.DeleteHook)
|
||||||
}, reqToken(), reqOrgOwnership())
|
}, reqToken(), reqOrgOwnership())
|
||||||
}, orgAssignment(true))
|
}, orgAssignment(true))
|
||||||
m.Group("/teams/:teamid", func() {
|
m.Group("/teams/{teamid}", func() {
|
||||||
m.Combo("").Get(org.GetTeam).
|
m.Combo("").Get(org.GetTeam).
|
||||||
Patch(reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam).
|
Patch(reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam).
|
||||||
Delete(reqOrgOwnership(), org.DeleteTeam)
|
Delete(reqOrgOwnership(), org.DeleteTeam)
|
||||||
m.Group("/members", func() {
|
m.Group("/members", func() {
|
||||||
m.Get("", org.GetTeamMembers)
|
m.Get("", org.GetTeamMembers)
|
||||||
m.Combo("/:username").
|
m.Combo("/{username}").
|
||||||
Get(org.GetTeamMember).
|
Get(org.GetTeamMember).
|
||||||
Put(reqOrgOwnership(), org.AddTeamMember).
|
Put(reqOrgOwnership(), org.AddTeamMember).
|
||||||
Delete(reqOrgOwnership(), org.RemoveTeamMember)
|
Delete(reqOrgOwnership(), org.RemoveTeamMember)
|
||||||
})
|
})
|
||||||
m.Group("/repos", func() {
|
m.Group("/repos", func() {
|
||||||
m.Get("", org.GetTeamRepos)
|
m.Get("", org.GetTeamRepos)
|
||||||
m.Combo("/:org/:reponame").
|
m.Combo("/{org}/{reponame}").
|
||||||
Put(org.AddTeamRepository).
|
Put(org.AddTeamRepository).
|
||||||
Delete(org.RemoveTeamRepository)
|
Delete(org.RemoveTeamRepository)
|
||||||
})
|
})
|
||||||
}, orgAssignment(false, true), reqToken(), reqTeamMembership())
|
}, orgAssignment(false, true), reqToken(), reqTeamMembership())
|
||||||
|
|
||||||
m.Any("/*", func(ctx *context.APIContext) {
|
|
||||||
ctx.NotFound()
|
|
||||||
})
|
|
||||||
|
|
||||||
m.Group("/admin", func() {
|
m.Group("/admin", func() {
|
||||||
m.Group("/cron", func() {
|
m.Group("/cron", func() {
|
||||||
m.Get("", admin.ListCronTasks)
|
m.Get("", admin.ListCronTasks)
|
||||||
m.Post("/:task", admin.PostCronTask)
|
m.Post("/{task}", admin.PostCronTask)
|
||||||
})
|
})
|
||||||
m.Get("/orgs", admin.GetAllOrgs)
|
m.Get("/orgs", admin.GetAllOrgs)
|
||||||
m.Group("/users", func() {
|
m.Group("/users", func() {
|
||||||
m.Get("", admin.GetAllUsers)
|
m.Get("", admin.GetAllUsers)
|
||||||
m.Post("", bind(api.CreateUserOption{}), admin.CreateUser)
|
m.Post("", bind(api.CreateUserOption{}), admin.CreateUser)
|
||||||
m.Group("/:username", func() {
|
m.Group("/{username}", func() {
|
||||||
m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser).
|
m.Combo("").Patch(bind(api.EditUserOption{}), admin.EditUser).
|
||||||
Delete(admin.DeleteUser)
|
Delete(admin.DeleteUser)
|
||||||
m.Group("/keys", func() {
|
m.Group("/keys", func() {
|
||||||
m.Post("", bind(api.CreateKeyOption{}), admin.CreatePublicKey)
|
m.Post("", bind(api.CreateKeyOption{}), admin.CreatePublicKey)
|
||||||
m.Delete("/:id", admin.DeleteUserPublicKey)
|
m.Delete("/{id}", admin.DeleteUserPublicKey)
|
||||||
})
|
})
|
||||||
m.Get("/orgs", org.ListUserOrgs)
|
m.Get("/orgs", org.ListUserOrgs)
|
||||||
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
|
m.Post("/orgs", bind(api.CreateOrgOption{}), admin.CreateOrg)
|
||||||
|
@ -978,23 +1014,26 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
})
|
})
|
||||||
m.Group("/unadopted", func() {
|
m.Group("/unadopted", func() {
|
||||||
m.Get("", admin.ListUnadoptedRepositories)
|
m.Get("", admin.ListUnadoptedRepositories)
|
||||||
m.Post("/:username/:reponame", admin.AdoptRepository)
|
m.Post("/{username}/{reponame}", admin.AdoptRepository)
|
||||||
m.Delete("/:username/:reponame", admin.DeleteUnadoptedRepository)
|
m.Delete("/{username}/{reponame}", admin.DeleteUnadoptedRepository)
|
||||||
})
|
})
|
||||||
}, reqToken(), reqSiteAdmin())
|
}, reqToken(), reqSiteAdmin())
|
||||||
|
|
||||||
m.Group("/topics", func() {
|
m.Group("/topics", func() {
|
||||||
m.Get("/search", repo.TopicSearch)
|
m.Get("/search", repo.TopicSearch)
|
||||||
})
|
})
|
||||||
}, securityHeaders(), context.APIContexter(), sudo())
|
}, sudo())
|
||||||
|
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func securityHeaders() macaron.Handler {
|
func securityHeaders() func(http.Handler) http.Handler {
|
||||||
return func(ctx *macaron.Context) {
|
return func(next http.Handler) http.Handler {
|
||||||
ctx.Resp.Before(func(w macaron.ResponseWriter) {
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||||
// CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers
|
// CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers
|
||||||
// http://stackoverflow.com/a/3146618/244009
|
// http://stackoverflow.com/a/3146618/244009
|
||||||
w.Header().Set("x-content-type-options", "nosniff")
|
resp.Header().Set("x-content-type-options", "nosniff")
|
||||||
|
next.ServeHTTP(resp, req)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package misc
|
package misc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -13,12 +14,13 @@ import (
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
|
||||||
"mvdan.cc/xurls/v2"
|
"mvdan.cc/xurls/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Markdown render markdown document to HTML
|
// Markdown render markdown document to HTML
|
||||||
func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
func Markdown(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /markdown miscellaneous renderMarkdown
|
// swagger:operation POST /markdown miscellaneous renderMarkdown
|
||||||
// ---
|
// ---
|
||||||
// summary: Render a markdown document as HTML
|
// summary: Render a markdown document as HTML
|
||||||
|
@ -37,6 +39,8 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.MarkdownOption)
|
||||||
|
|
||||||
if ctx.HasAPIError() {
|
if ctx.HasAPIError() {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
|
ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
|
||||||
return
|
return
|
||||||
|
@ -117,7 +121,7 @@ func MarkdownRaw(ctx *context.APIContext) {
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
|
||||||
body, err := ctx.Req.Body().Bytes()
|
body, err := ioutil.ReadAll(ctx.Req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -15,10 +15,10 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/templates"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
|
||||||
"gitea.com/macaron/inject"
|
|
||||||
"gitea.com/macaron/macaron"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,25 +26,21 @@ const AppURL = "http://localhost:3000/"
|
||||||
const Repo = "gogits/gogs"
|
const Repo = "gogits/gogs"
|
||||||
const AppSubURL = AppURL + Repo + "/"
|
const AppSubURL = AppURL + Repo + "/"
|
||||||
|
|
||||||
func createContext(req *http.Request) (*macaron.Context, *httptest.ResponseRecorder) {
|
func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecorder) {
|
||||||
|
var rnd = templates.HTMLRenderer()
|
||||||
resp := httptest.NewRecorder()
|
resp := httptest.NewRecorder()
|
||||||
c := &macaron.Context{
|
c := &context.Context{
|
||||||
Injector: inject.New(),
|
Req: req,
|
||||||
Req: macaron.Request{Request: req},
|
Resp: context.NewResponse(resp),
|
||||||
Resp: macaron.NewResponseWriter(req.Method, resp),
|
Render: rnd,
|
||||||
Render: &macaron.DummyRender{ResponseWriter: resp},
|
Data: make(map[string]interface{}),
|
||||||
Data: make(map[string]interface{}),
|
|
||||||
}
|
}
|
||||||
c.Map(c)
|
|
||||||
c.Map(req)
|
|
||||||
return c, resp
|
return c, resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrap(ctx *macaron.Context) *context.APIContext {
|
func wrap(ctx *context.Context) *context.APIContext {
|
||||||
return &context.APIContext{
|
return &context.APIContext{
|
||||||
Context: &context.Context{
|
Context: ctx,
|
||||||
Context: ctx,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +111,8 @@ Here are some links to the most important topics. You can find the full list of
|
||||||
|
|
||||||
for i := 0; i < len(testCases); i += 2 {
|
for i := 0; i < len(testCases); i += 2 {
|
||||||
options.Text = testCases[i]
|
options.Text = testCases[i]
|
||||||
Markdown(ctx, options)
|
web.SetForm(ctx, &options)
|
||||||
|
Markdown(ctx)
|
||||||
assert.Equal(t, testCases[i+1], resp.Body.String())
|
assert.Equal(t, testCases[i+1], resp.Body.String())
|
||||||
resp.Body.Reset()
|
resp.Body.Reset()
|
||||||
}
|
}
|
||||||
|
@ -156,7 +153,8 @@ func TestAPI_RenderSimple(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < len(simpleCases); i += 2 {
|
for i := 0; i < len(simpleCases); i += 2 {
|
||||||
options.Text = simpleCases[i]
|
options.Text = simpleCases[i]
|
||||||
Markdown(ctx, options)
|
web.SetForm(ctx, &options)
|
||||||
|
Markdown(ctx)
|
||||||
assert.Equal(t, simpleCases[i+1], resp.Body.String())
|
assert.Equal(t, simpleCases[i+1], resp.Body.String())
|
||||||
resp.Body.Reset()
|
resp.Body.Reset()
|
||||||
}
|
}
|
||||||
|
@ -174,7 +172,7 @@ func TestAPI_RenderRaw(t *testing.T) {
|
||||||
ctx := wrap(m)
|
ctx := wrap(m)
|
||||||
|
|
||||||
for i := 0; i < len(simpleCases); i += 2 {
|
for i := 0; i < len(simpleCases); i += 2 {
|
||||||
ctx.Req.Request.Body = ioutil.NopCloser(strings.NewReader(simpleCases[i]))
|
ctx.Req.Body = ioutil.NopCloser(strings.NewReader(simpleCases[i]))
|
||||||
MarkdownRaw(ctx)
|
MarkdownRaw(ctx)
|
||||||
assert.Equal(t, simpleCases[i+1], resp.Body.String())
|
assert.Equal(t, simpleCases[i+1], resp.Body.String())
|
||||||
resp.Body.Reset()
|
resp.Body.Reset()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ func GetHook(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateHook create a hook for an organization
|
// CreateHook create a hook for an organization
|
||||||
func CreateHook(ctx *context.APIContext, form api.CreateHookOption) {
|
func CreateHook(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /orgs/{org}/hooks/ organization orgCreateHook
|
// swagger:operation POST /orgs/{org}/hooks/ organization orgCreateHook
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a hook
|
// summary: Create a hook
|
||||||
|
@ -108,15 +109,16 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) {
|
||||||
// "201":
|
// "201":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.CreateHookOption)
|
||||||
//TODO in body params
|
//TODO in body params
|
||||||
if !utils.CheckCreateHookOption(ctx, &form) {
|
if !utils.CheckCreateHookOption(ctx, form) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
utils.AddOrgHook(ctx, &form)
|
utils.AddOrgHook(ctx, form)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditHook modify a hook of a repository
|
// EditHook modify a hook of a repository
|
||||||
func EditHook(ctx *context.APIContext, form api.EditHookOption) {
|
func EditHook(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /orgs/{org}/hooks/{id} organization orgEditHook
|
// swagger:operation PATCH /orgs/{org}/hooks/{id} organization orgEditHook
|
||||||
// ---
|
// ---
|
||||||
// summary: Update a hook
|
// summary: Update a hook
|
||||||
|
@ -144,9 +146,11 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) {
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.EditHookOption)
|
||||||
|
|
||||||
//TODO in body params
|
//TODO in body params
|
||||||
hookID := ctx.ParamsInt64(":id")
|
hookID := ctx.ParamsInt64(":id")
|
||||||
utils.EditOrgHook(ctx, &form, hookID)
|
utils.EditOrgHook(ctx, form, hookID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHook delete a hook of an organization
|
// DeleteHook delete a hook of an organization
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ func ListLabels(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateLabel create a label for a repository
|
// CreateLabel create a label for a repository
|
||||||
func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) {
|
func CreateLabel(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /orgs/{org}/labels organization orgCreateLabel
|
// swagger:operation POST /orgs/{org}/labels organization orgCreateLabel
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a label for an organization
|
// summary: Create a label for an organization
|
||||||
|
@ -75,7 +76,7 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) {
|
||||||
// "$ref": "#/responses/Label"
|
// "$ref": "#/responses/Label"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateLabelOption)
|
||||||
form.Color = strings.Trim(form.Color, " ")
|
form.Color = strings.Trim(form.Color, " ")
|
||||||
if len(form.Color) == 6 {
|
if len(form.Color) == 6 {
|
||||||
form.Color = "#" + form.Color
|
form.Color = "#" + form.Color
|
||||||
|
@ -144,7 +145,7 @@ func GetLabel(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditLabel modify a label for an Organization
|
// EditLabel modify a label for an Organization
|
||||||
func EditLabel(ctx *context.APIContext, form api.EditLabelOption) {
|
func EditLabel(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /orgs/{org}/labels/{id} organization orgEditLabel
|
// swagger:operation PATCH /orgs/{org}/labels/{id} organization orgEditLabel
|
||||||
// ---
|
// ---
|
||||||
// summary: Update a label
|
// summary: Update a label
|
||||||
|
@ -173,7 +174,7 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) {
|
||||||
// "$ref": "#/responses/Label"
|
// "$ref": "#/responses/Label"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.EditLabelOption)
|
||||||
label, err := models.GetLabelInOrgByID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
|
label, err := models.GetLabelInOrgByID(ctx.Org.Organization.ID, ctx.ParamsInt64(":id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrOrgLabelNotExist(err) {
|
if models.IsErrOrgLabelNotExist(err) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/user"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
@ -149,7 +150,7 @@ func GetAll(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create api for create organization
|
// Create api for create organization
|
||||||
func Create(ctx *context.APIContext, form api.CreateOrgOption) {
|
func Create(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /orgs organization orgCreate
|
// swagger:operation POST /orgs organization orgCreate
|
||||||
// ---
|
// ---
|
||||||
// summary: Create an organization
|
// summary: Create an organization
|
||||||
|
@ -169,7 +170,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateOrgOption)
|
||||||
if !ctx.User.CanCreateOrganization() {
|
if !ctx.User.CanCreateOrganization() {
|
||||||
ctx.Error(http.StatusForbidden, "Create organization not allowed", nil)
|
ctx.Error(http.StatusForbidden, "Create organization not allowed", nil)
|
||||||
return
|
return
|
||||||
|
@ -231,7 +232,7 @@ func Get(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit change an organization's information
|
// Edit change an organization's information
|
||||||
func Edit(ctx *context.APIContext, form api.EditOrgOption) {
|
func Edit(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /orgs/{org} organization orgEdit
|
// swagger:operation PATCH /orgs/{org} organization orgEdit
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit an organization
|
// summary: Edit an organization
|
||||||
|
@ -253,7 +254,7 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) {
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/Organization"
|
// "$ref": "#/responses/Organization"
|
||||||
|
form := web.GetForm(ctx).(*api.EditOrgOption)
|
||||||
org := ctx.Org.Organization
|
org := ctx.Org.Organization
|
||||||
org.FullName = form.FullName
|
org.FullName = form.FullName
|
||||||
org.Description = form.Description
|
org.Description = form.Description
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/user"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
@ -131,7 +132,7 @@ func GetTeam(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateTeam api for create a team
|
// CreateTeam api for create a team
|
||||||
func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
|
func CreateTeam(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /orgs/{org}/teams organization orgCreateTeam
|
// swagger:operation POST /orgs/{org}/teams organization orgCreateTeam
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a team
|
// summary: Create a team
|
||||||
|
@ -154,7 +155,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
|
||||||
// "$ref": "#/responses/Team"
|
// "$ref": "#/responses/Team"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateTeamOption)
|
||||||
team := &models.Team{
|
team := &models.Team{
|
||||||
OrgID: ctx.Org.Organization.ID,
|
OrgID: ctx.Org.Organization.ID,
|
||||||
Name: form.Name,
|
Name: form.Name,
|
||||||
|
@ -190,7 +191,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditTeam api for edit a team
|
// EditTeam api for edit a team
|
||||||
func EditTeam(ctx *context.APIContext, form api.EditTeamOption) {
|
func EditTeam(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /teams/{id} organization orgEditTeam
|
// swagger:operation PATCH /teams/{id} organization orgEditTeam
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit a team
|
// summary: Edit a team
|
||||||
|
@ -212,6 +213,8 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) {
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/Team"
|
// "$ref": "#/responses/Team"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.EditTeamOption)
|
||||||
|
|
||||||
team := ctx.Org.Team
|
team := ctx.Org.Team
|
||||||
if err := team.GetUnits(); err != nil {
|
if err := team.GetUnits(); err != nil {
|
||||||
ctx.InternalServerError(err)
|
ctx.InternalServerError(err)
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
pull_service "code.gitea.io/gitea/services/pull"
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
@ -175,7 +176,7 @@ func DeleteBranch(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBranch creates a branch for a user's repository
|
// CreateBranch creates a branch for a user's repository
|
||||||
func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) {
|
func CreateBranch(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/branches repository repoCreateBranch
|
// swagger:operation POST /repos/{owner}/{repo}/branches repository repoCreateBranch
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a branch
|
// summary: Create a branch
|
||||||
|
@ -206,6 +207,7 @@ func CreateBranch(ctx *context.APIContext, opt api.CreateBranchRepoOption) {
|
||||||
// "409":
|
// "409":
|
||||||
// description: The branch with the same name already exists.
|
// description: The branch with the same name already exists.
|
||||||
|
|
||||||
|
opt := web.GetForm(ctx).(*api.CreateBranchRepoOption)
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
|
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
|
||||||
return
|
return
|
||||||
|
@ -395,7 +397,7 @@ func ListBranchProtections(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateBranchProtection creates a branch protection for a repo
|
// CreateBranchProtection creates a branch protection for a repo
|
||||||
func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtectionOption) {
|
func CreateBranchProtection(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection
|
// swagger:operation POST /repos/{owner}/{repo}/branch_protections repository repoCreateBranchProtection
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a branch protections for a repository
|
// summary: Create a branch protections for a repository
|
||||||
|
@ -428,6 +430,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.CreateBranchProtectionOption)
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
// Currently protection must match an actual branch
|
// Currently protection must match an actual branch
|
||||||
|
@ -561,7 +564,7 @@ func CreateBranchProtection(ctx *context.APIContext, form api.CreateBranchProtec
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditBranchProtection edits a branch protection for a repo
|
// EditBranchProtection edits a branch protection for a repo
|
||||||
func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtectionOption) {
|
func EditBranchProtection(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection
|
// swagger:operation PATCH /repos/{owner}/{repo}/branch_protections/{name} repository repoEditBranchProtection
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit a branch protections for a repository. Only fields that are set will be changed
|
// summary: Edit a branch protections for a repository. Only fields that are set will be changed
|
||||||
|
@ -596,7 +599,7 @@ func EditBranchProtection(ctx *context.APIContext, form api.EditBranchProtection
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.EditBranchProtectionOption)
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
bpName := ctx.Params(":name")
|
bpName := ctx.Params(":name")
|
||||||
protectBranch, err := models.GetProtectedBranchBy(repo.ID, bpName)
|
protectBranch, err := models.GetProtectedBranchBy(repo.ID, bpName)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -111,7 +112,7 @@ func IsCollaborator(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddCollaborator add a collaborator to a repository
|
// AddCollaborator add a collaborator to a repository
|
||||||
func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) {
|
func AddCollaborator(ctx *context.APIContext) {
|
||||||
// swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator
|
// swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator
|
||||||
// ---
|
// ---
|
||||||
// summary: Add a collaborator to a repository
|
// summary: Add a collaborator to a repository
|
||||||
|
@ -143,6 +144,8 @@ func AddCollaborator(ctx *context.APIContext, form api.AddCollaboratorOption) {
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.AddCollaboratorOption)
|
||||||
|
|
||||||
collaborator, err := models.GetUserByName(ctx.Params(":collaborator"))
|
collaborator, err := models.GetUserByName(ctx.Params(":collaborator"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrUserNotExist(err) {
|
if models.IsErrUserNotExist(err) {
|
||||||
|
|
|
@ -69,7 +69,11 @@ func getCommit(ctx *context.APIContext, identifier string) {
|
||||||
defer gitRepo.Close()
|
defer gitRepo.Close()
|
||||||
commit, err := gitRepo.GetCommit(identifier)
|
commit, err := gitRepo.GetCommit(identifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.NotFoundOrServerError("GetCommit", git.IsErrNotExist, err)
|
if git.IsErrNotExist(err) {
|
||||||
|
ctx.NotFound(identifier)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Error(http.StatusInternalServerError, "gitRepo.GetCommit", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/repofiles"
|
"code.gitea.io/gitea/modules/repofiles"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/repo"
|
"code.gitea.io/gitea/routers/repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -167,7 +168,7 @@ func canReadFiles(r *context.Repository) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFile handles API call for creating a file
|
// CreateFile handles API call for creating a file
|
||||||
func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
|
func CreateFile(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/contents/{filepath} repository repoCreateFile
|
// swagger:operation POST /repos/{owner}/{repo}/contents/{filepath} repository repoCreateFile
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a file in a repository
|
// summary: Create a file in a repository
|
||||||
|
@ -206,6 +207,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
apiOpts := web.GetForm(ctx).(*api.CreateFileOptions)
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
|
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
|
||||||
}
|
}
|
||||||
|
@ -253,7 +255,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFile handles API call for updating a file
|
// UpdateFile handles API call for updating a file
|
||||||
func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) {
|
func UpdateFile(ctx *context.APIContext) {
|
||||||
// swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoUpdateFile
|
// swagger:operation PUT /repos/{owner}/{repo}/contents/{filepath} repository repoUpdateFile
|
||||||
// ---
|
// ---
|
||||||
// summary: Update a file in a repository
|
// summary: Update a file in a repository
|
||||||
|
@ -291,7 +293,7 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) {
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
apiOpts := web.GetForm(ctx).(*api.UpdateFileOptions)
|
||||||
if ctx.Repo.Repository.IsEmpty {
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
|
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
|
||||||
}
|
}
|
||||||
|
@ -377,7 +379,7 @@ func createOrUpdateFile(ctx *context.APIContext, opts *repofiles.UpdateRepoFileO
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteFile Delete a fle in a repository
|
// DeleteFile Delete a fle in a repository
|
||||||
func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
func DeleteFile(ctx *context.APIContext) {
|
||||||
// swagger:operation DELETE /repos/{owner}/{repo}/contents/{filepath} repository repoDeleteFile
|
// swagger:operation DELETE /repos/{owner}/{repo}/contents/{filepath} repository repoDeleteFile
|
||||||
// ---
|
// ---
|
||||||
// summary: Delete a file in a repository
|
// summary: Delete a file in a repository
|
||||||
|
@ -416,6 +418,7 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
apiOpts := web.GetForm(ctx).(*api.DeleteFileOptions)
|
||||||
if !canWriteFiles(ctx.Repo) {
|
if !canWriteFiles(ctx.Repo) {
|
||||||
ctx.Error(http.StatusForbidden, "DeleteFile", models.ErrUserDoesNotHaveAccessToRepo{
|
ctx.Error(http.StatusForbidden, "DeleteFile", models.ErrUserDoesNotHaveAccessToRepo{
|
||||||
UserID: ctx.User.ID,
|
UserID: ctx.User.ID,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
@ -65,7 +66,7 @@ func ListForks(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFork create a fork of a repo
|
// CreateFork create a fork of a repo
|
||||||
func CreateFork(ctx *context.APIContext, form api.CreateForkOption) {
|
func CreateFork(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/forks repository createFork
|
// swagger:operation POST /repos/{owner}/{repo}/forks repository createFork
|
||||||
// ---
|
// ---
|
||||||
// summary: Fork a repository
|
// summary: Fork a repository
|
||||||
|
@ -94,6 +95,7 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) {
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.CreateForkOption)
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
var forker *models.User // user/org that will own the fork
|
var forker *models.User // user/org that will own the fork
|
||||||
if form.Organization == nil {
|
if form.Organization == nil {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListGitHooks list all Git hooks of a repository
|
// ListGitHooks list all Git hooks of a repository
|
||||||
|
@ -91,7 +92,7 @@ func GetGitHook(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditGitHook modify a Git hook of a repository
|
// EditGitHook modify a Git hook of a repository
|
||||||
func EditGitHook(ctx *context.APIContext, form api.EditGitHookOption) {
|
func EditGitHook(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /repos/{owner}/{repo}/hooks/git/{id} repository repoEditGitHook
|
// swagger:operation PATCH /repos/{owner}/{repo}/hooks/git/{id} repository repoEditGitHook
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit a Git hook in a repository
|
// summary: Edit a Git hook in a repository
|
||||||
|
@ -123,6 +124,7 @@ func EditGitHook(ctx *context.APIContext, form api.EditGitHookOption) {
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.EditGitHookOption)
|
||||||
hookID := ctx.Params(":id")
|
hookID := ctx.Params(":id")
|
||||||
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
|
hook, err := ctx.Repo.GitRepo.GetHook(hookID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/webhook"
|
"code.gitea.io/gitea/services/webhook"
|
||||||
)
|
)
|
||||||
|
@ -158,7 +159,7 @@ func TestHook(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateHook create a hook for a repository
|
// CreateHook create a hook for a repository
|
||||||
func CreateHook(ctx *context.APIContext, form api.CreateHookOption) {
|
func CreateHook(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/hooks repository repoCreateHook
|
// swagger:operation POST /repos/{owner}/{repo}/hooks repository repoCreateHook
|
||||||
// ---
|
// ---
|
||||||
// summary: Create a hook
|
// summary: Create a hook
|
||||||
|
@ -184,14 +185,16 @@ func CreateHook(ctx *context.APIContext, form api.CreateHookOption) {
|
||||||
// responses:
|
// responses:
|
||||||
// "201":
|
// "201":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
if !utils.CheckCreateHookOption(ctx, &form) {
|
form := web.GetForm(ctx).(*api.CreateHookOption)
|
||||||
|
|
||||||
|
if !utils.CheckCreateHookOption(ctx, form) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
utils.AddRepoHook(ctx, &form)
|
utils.AddRepoHook(ctx, form)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditHook modify a hook of a repository
|
// EditHook modify a hook of a repository
|
||||||
func EditHook(ctx *context.APIContext, form api.EditHookOption) {
|
func EditHook(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /repos/{owner}/{repo}/hooks/{id} repository repoEditHook
|
// swagger:operation PATCH /repos/{owner}/{repo}/hooks/{id} repository repoEditHook
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit a hook in a repository
|
// summary: Edit a hook in a repository
|
||||||
|
@ -221,8 +224,9 @@ func EditHook(ctx *context.APIContext, form api.EditHookOption) {
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/Hook"
|
// "$ref": "#/responses/Hook"
|
||||||
|
form := web.GetForm(ctx).(*api.EditHookOption)
|
||||||
hookID := ctx.ParamsInt64(":id")
|
hookID := ctx.ParamsInt64(":id")
|
||||||
utils.EditRepoHook(ctx, &form, hookID)
|
utils.EditRepoHook(ctx, form, hookID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteHook delete a hook of a repository
|
// DeleteHook delete a hook of a repository
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
)
|
)
|
||||||
|
@ -448,7 +449,7 @@ func GetIssue(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateIssue create an issue of a repository
|
// CreateIssue create an issue of a repository
|
||||||
func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
|
func CreateIssue(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/issues issue issueCreateIssue
|
// swagger:operation POST /repos/{owner}/{repo}/issues issue issueCreateIssue
|
||||||
// ---
|
// ---
|
||||||
// summary: Create an issue. If using deadline only the date will be taken into account, and time of day ignored.
|
// summary: Create an issue. If using deadline only the date will be taken into account, and time of day ignored.
|
||||||
|
@ -480,7 +481,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
// "422":
|
// "422":
|
||||||
// "$ref": "#/responses/validationError"
|
// "$ref": "#/responses/validationError"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateIssueOption)
|
||||||
var deadlineUnix timeutil.TimeStamp
|
var deadlineUnix timeutil.TimeStamp
|
||||||
if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
||||||
deadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
|
deadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
|
||||||
|
@ -564,7 +565,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditIssue modify an issue of a repository
|
// EditIssue modify an issue of a repository
|
||||||
func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
func EditIssue(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /repos/{owner}/{repo}/issues/{index} issue issueEditIssue
|
// swagger:operation PATCH /repos/{owner}/{repo}/issues/{index} issue issueEditIssue
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit an issue. If using deadline only the date will be taken into account, and time of day ignored.
|
// summary: Edit an issue. If using deadline only the date will be taken into account, and time of day ignored.
|
||||||
|
@ -603,6 +604,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
||||||
// "412":
|
// "412":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.EditIssueOption)
|
||||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrIssueNotExist(err) {
|
if models.IsErrIssueNotExist(err) {
|
||||||
|
@ -723,7 +725,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateIssueDeadline updates an issue deadline
|
// UpdateIssueDeadline updates an issue deadline
|
||||||
func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
|
func UpdateIssueDeadline(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/deadline issue issueEditIssueDeadline
|
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/deadline issue issueEditIssueDeadline
|
||||||
// ---
|
// ---
|
||||||
// summary: Set an issue deadline. If set to null, the deadline is deleted. If using deadline only the date will be taken into account, and time of day ignored.
|
// summary: Set an issue deadline. If set to null, the deadline is deleted. If using deadline only the date will be taken into account, and time of day ignored.
|
||||||
|
@ -759,7 +761,7 @@ func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
form := web.GetForm(ctx).(*api.EditDeadlineOption)
|
||||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrIssueNotExist(err) {
|
if models.IsErrIssueNotExist(err) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
comment_service "code.gitea.io/gitea/services/comments"
|
comment_service "code.gitea.io/gitea/services/comments"
|
||||||
)
|
)
|
||||||
|
@ -174,7 +175,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateIssueComment create a comment for an issue
|
// CreateIssueComment create a comment for an issue
|
||||||
func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOption) {
|
func CreateIssueComment(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/comments issue issueCreateComment
|
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/comments issue issueCreateComment
|
||||||
// ---
|
// ---
|
||||||
// summary: Add a comment to an issue
|
// summary: Add a comment to an issue
|
||||||
|
@ -208,7 +209,7 @@ func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOpti
|
||||||
// "$ref": "#/responses/Comment"
|
// "$ref": "#/responses/Comment"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
form := web.GetForm(ctx).(*api.CreateIssueCommentOption)
|
||||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||||
|
@ -298,7 +299,7 @@ func GetIssueComment(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditIssueComment modify a comment of an issue
|
// EditIssueComment modify a comment of an issue
|
||||||
func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
|
func EditIssueComment(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment
|
// swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit a comment
|
// summary: Edit a comment
|
||||||
|
@ -337,11 +338,12 @@ func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
editIssueComment(ctx, form)
|
form := web.GetForm(ctx).(*api.EditIssueCommentOption)
|
||||||
|
editIssueComment(ctx, *form)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EditIssueCommentDeprecated modify a comment of an issue
|
// EditIssueCommentDeprecated modify a comment of an issue
|
||||||
func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueCommentOption) {
|
func EditIssueCommentDeprecated(ctx *context.APIContext) {
|
||||||
// swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueEditCommentDeprecated
|
// swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueEditCommentDeprecated
|
||||||
// ---
|
// ---
|
||||||
// summary: Edit a comment
|
// summary: Edit a comment
|
||||||
|
@ -386,7 +388,8 @@ func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueComme
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
editIssueComment(ctx, form)
|
form := web.GetForm(ctx).(*api.EditIssueCommentOption)
|
||||||
|
editIssueComment(ctx, *form)
|
||||||
}
|
}
|
||||||
|
|
||||||
func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
|
func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,7 +65,7 @@ func ListIssueLabels(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddIssueLabels add labels for an issue
|
// AddIssueLabels add labels for an issue
|
||||||
func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
|
func AddIssueLabels(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/labels issue issueAddLabel
|
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/labels issue issueAddLabel
|
||||||
// ---
|
// ---
|
||||||
// summary: Add a label to an issue
|
// summary: Add a label to an issue
|
||||||
|
@ -99,7 +100,8 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
issue, labels, err := prepareForReplaceOrAdd(ctx, form)
|
form := web.GetForm(ctx).(*api.IssueLabelsOption)
|
||||||
|
issue, labels, err := prepareForReplaceOrAdd(ctx, *form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -190,7 +192,7 @@ func DeleteIssueLabel(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplaceIssueLabels replace labels for an issue
|
// ReplaceIssueLabels replace labels for an issue
|
||||||
func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
|
func ReplaceIssueLabels(ctx *context.APIContext) {
|
||||||
// swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/labels issue issueReplaceLabels
|
// swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/labels issue issueReplaceLabels
|
||||||
// ---
|
// ---
|
||||||
// summary: Replace an issue's labels
|
// summary: Replace an issue's labels
|
||||||
|
@ -224,8 +226,8 @@ func ReplaceIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
|
||||||
// "$ref": "#/responses/LabelList"
|
// "$ref": "#/responses/LabelList"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
form := web.GetForm(ctx).(*api.IssueLabelsOption)
|
||||||
issue, labels, err := prepareForReplaceOrAdd(ctx, form)
|
issue, labels, err := prepareForReplaceOrAdd(ctx, *form)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ func GetIssueCommentReactions(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostIssueCommentReaction add a reaction to a comment of an issue
|
// PostIssueCommentReaction add a reaction to a comment of an issue
|
||||||
func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) {
|
func PostIssueCommentReaction(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issuePostCommentReaction
|
// swagger:operation POST /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issuePostCommentReaction
|
||||||
// ---
|
// ---
|
||||||
// summary: Add a reaction to a comment of an issue
|
// summary: Add a reaction to a comment of an issue
|
||||||
|
@ -127,11 +128,13 @@ func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOpti
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
changeIssueCommentReaction(ctx, form, true)
|
form := web.GetForm(ctx).(*api.EditReactionOption)
|
||||||
|
|
||||||
|
changeIssueCommentReaction(ctx, *form, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteIssueCommentReaction remove a reaction from a comment of an issue
|
// DeleteIssueCommentReaction remove a reaction from a comment of an issue
|
||||||
func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) {
|
func DeleteIssueCommentReaction(ctx *context.APIContext) {
|
||||||
// swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueDeleteCommentReaction
|
// swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueDeleteCommentReaction
|
||||||
// ---
|
// ---
|
||||||
// summary: Remove a reaction from a comment of an issue
|
// summary: Remove a reaction from a comment of an issue
|
||||||
|
@ -166,7 +169,9 @@ func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOp
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
|
||||||
changeIssueCommentReaction(ctx, form, false)
|
form := web.GetForm(ctx).(*api.EditReactionOption)
|
||||||
|
|
||||||
|
changeIssueCommentReaction(ctx, *form, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
|
func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
|
||||||
|
@ -304,7 +309,7 @@ func GetIssueReactions(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PostIssueReaction add a reaction to an issue
|
// PostIssueReaction add a reaction to an issue
|
||||||
func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
|
func PostIssueReaction(ctx *context.APIContext) {
|
||||||
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/reactions issue issuePostIssueReaction
|
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/reactions issue issuePostIssueReaction
|
||||||
// ---
|
// ---
|
||||||
// summary: Add a reaction to an issue
|
// summary: Add a reaction to an issue
|
||||||
|
@ -340,12 +345,12 @@ func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
|
||||||
// "$ref": "#/responses/Reaction"
|
// "$ref": "#/responses/Reaction"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
form := web.GetForm(ctx).(*api.EditReactionOption)
|
||||||
changeIssueReaction(ctx, form, true)
|
changeIssueReaction(ctx, *form, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteIssueReaction remove a reaction from an issue
|
// DeleteIssueReaction remove a reaction from an issue
|
||||||
func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
|
func DeleteIssueReaction(ctx *context.APIContext) {
|
||||||
// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/reactions issue issueDeleteIssueReaction
|
// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/reactions issue issueDeleteIssueReaction
|
||||||
// ---
|
// ---
|
||||||
// summary: Remove a reaction from an issue
|
// summary: Remove a reaction from an issue
|
||||||
|
@ -379,8 +384,8 @@ func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
|
||||||
// "$ref": "#/responses/empty"
|
// "$ref": "#/responses/empty"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
form := web.GetForm(ctx).(*api.EditReactionOption)
|
||||||
changeIssueReaction(ctx, form, false)
|
changeIssueReaction(ctx, *form, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
|
func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
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/routers/api/v1/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTime add time manual to the given issue
|
// AddTime add time manual to the given issue
|
||||||
func AddTime(ctx *context.APIContext, form api.AddTimeOption) {
|
func AddTime(ctx *context.APIContext) {
|
||||||
// swagger:operation Post /repos/{owner}/{repo}/issues/{index}/times issue issueAddTime
|
// swagger:operation Post /repos/{owner}/{repo}/issues/{index}/times issue issueAddTime
|
||||||
// ---
|
// ---
|
||||||
// summary: Add tracked time to a issue
|
// summary: Add tracked time to a issue
|
||||||
|
@ -168,7 +169,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) {
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
// "403":
|
// "403":
|
||||||
// "$ref": "#/responses/forbidden"
|
// "$ref": "#/responses/forbidden"
|
||||||
|
form := web.GetForm(ctx).(*api.AddTimeOption)
|
||||||
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrIssueNotExist(err) {
|
if models.IsErrIssueNotExist(err) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue