diff --git a/cmd/web.go b/cmd/web.go index 063e41c94647b..c057b51847a60 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -134,7 +134,7 @@ func runWeb(ctx *cli.Context) error { } } c := routes.NewChi() - routes.RegisterInstallRoute(c) + c.Mount("/", routes.InstallRoutes()) err := listen(c, false) select { case <-graceful.GetManager().IsShutdown(): diff --git a/go.mod b/go.mod index 0d1ebc1915459..2a71dbc92abe7 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.14 require ( code.gitea.io/gitea-vet v0.2.1 code.gitea.io/sdk/gitea v0.13.1 + gitea.com/go-chi/binding v0.0.0-20201220025549-f1056649c959 gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27 gitea.com/lunny/levelqueue v0.3.0 gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b diff --git a/go.sum b/go.sum index ad51f4c67a892..b8d4218aa7526 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ 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/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gitea.com/go-chi/binding v0.0.0-20201220025549-f1056649c959 h1:xcFbqqw8JUPTivvrwgDl5X1Chiby0gzASqbY5LmcOZ0= +gitea.com/go-chi/binding v0.0.0-20201220025549-f1056649c959/go.mod h1:2F9XqNn+FUSSG6yXlEF/KRQk9Rf99eEqJkGBcrp6APM= gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27 h1:cdb1OTNXGLwQ55gg+9tIPWufdsnrHWcIq8Qs+j/E8JU= gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0= gitea.com/lunny/levelqueue v0.3.0 h1:MHn1GuSZkxvVEDMyAPqlc7A3cOW+q8RcGhRgH/xtm6I= @@ -1083,7 +1085,6 @@ github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ= github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo= github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= @@ -1104,7 +1105,6 @@ github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= -github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= @@ -1187,7 +1187,6 @@ golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/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-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 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1268,8 +1267,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= -golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -1280,7 +1277,6 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1331,10 +1327,8 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1351,16 +1345,11 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f h1:QdHQnPce6K4XQewki9WNbG5KOROuDzqO3NaYjI1cXJ0= golang.org/x/sys v0.0.0-20201211090839-8ad439b19e0f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1370,7 +1359,6 @@ golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fq golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1419,7 +1407,6 @@ golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1430,29 +1417,18 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200921210052-fa0125251cc4 h1:v8Jgq9X6Es9K9otVr9jxENEJigepKMZgA9OmrIZDtFA= golang.org/x/tools v0.0.0-20200921210052-fa0125251cc4/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f h1:18s2P7JILnVhIF2+ZtGJQ9czV5bvTsb13/UGtNPDbjA= golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9 h1:sEvmEcJVKBNUvgCUClbUQeHOAa9U0I2Ce1BooMvVCY4= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -1490,7 +1466,6 @@ google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -1567,7 +1542,6 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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= @@ -1611,10 +1585,7 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A= mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go index b94b8e0a4ebc6..4c15a193e7006 100644 --- a/modules/auth/user_form.go +++ b/modules/auth/user_form.go @@ -15,60 +15,6 @@ import ( "gitea.com/macaron/macaron" ) -// InstallForm form for installation page -type InstallForm struct { - DbType string `binding:"Required"` - DbHost string - DbUser string - DbPasswd string - DbName string - SSLMode string - Charset string `binding:"Required;In(utf8,utf8mb4)"` - DbPath string - DbSchema string - - AppName string `binding:"Required" locale:"install.app_name"` - RepoRootPath string `binding:"Required"` - LFSRootPath string - RunUser string `binding:"Required"` - Domain string `binding:"Required"` - SSHPort int - HTTPPort string `binding:"Required"` - AppURL string `binding:"Required"` - LogRootPath string `binding:"Required"` - - SMTPHost string - SMTPFrom string - SMTPUser string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"` - SMTPPasswd string - RegisterConfirm bool - MailNotify bool - - OfflineMode bool - DisableGravatar bool - EnableFederatedAvatar bool - EnableOpenIDSignIn bool - EnableOpenIDSignUp bool - DisableRegistration bool - AllowOnlyExternalRegistration bool - EnableCaptcha bool - RequireSignInView bool - DefaultKeepEmailPrivate bool - DefaultAllowCreateOrganization bool - DefaultEnableTimetracking bool - NoReplyAddress string - - AdminName string `binding:"OmitEmpty;AlphaDashDot;MaxSize(30)" locale:"install.admin_name"` - AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"` - AdminConfirmPasswd string - AdminEmail string `binding:"OmitEmpty;MinSize(3);MaxSize(254);Include(@)" locale:"install.admin_email"` -} - -// Validate validates the fields -func (f *InstallForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { - return validate(errs, ctx.Data, f, ctx.Locale) -} - // _____ ____ _________________ ___ // / _ \ | | \__ ___/ | \ // / /_\ \| | / | | / ~ \ diff --git a/modules/context/default.go b/modules/context/default.go new file mode 100644 index 0000000000000..1278724e91f1a --- /dev/null +++ b/modules/context/default.go @@ -0,0 +1,114 @@ +// 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" + + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/translation" + + "gitea.com/go-chi/binding" + "gitea.com/go-chi/session" + "github.com/unrolled/render" +) + +// flashes enumerates all the flash types +const ( + SuccessFlash = "SuccessMsg" + ErrorFlash = "ErrorMsg" + WarnFlash = "WarningMsg" + InfoFlash = "InfoMsg" +) + +// Flash represents flashs +type Flash map[string]string + +// DefaultContext represents a context for basic routes, all other context should +// be derived from the context but not add more fields on this context +type DefaultContext struct { + Resp http.ResponseWriter + Req *http.Request + Data map[string]interface{} + Render *render.Render + Session session.Store + translation.Locale + flash Flash +} + +// HTML wraps render HTML +func (ctx *DefaultContext) HTML(statusCode int, tmpl string) error { + return ctx.Render.HTML(ctx.Resp, statusCode, tmpl, ctx.Data) +} + +// Bind binding a form to a struct +func (ctx *DefaultContext) Bind(form interface{}) binding.Errors { + return binding.Bind(ctx.Req, form) +} + +// HasError returns true if error occurs in form validation. +func (ctx *DefaultContext) HasError() bool { + hasErr, ok := ctx.Data["HasError"] + if !ok { + return false + } + return hasErr.(bool) +} + +// HasValue returns true if value of given name exists. +func (ctx *DefaultContext) HasValue(name string) bool { + _, ok := ctx.Data[name] + return ok +} + +// RenderWithErr used for page has form validation but need to prompt error to users. +func (ctx *DefaultContext) RenderWithErr(msg string, tpl string, form interface{}) { + if form != nil { + auth.AssignForm(form, ctx.Data) + } + ctx.Flash(ErrorFlash, msg) + _ = ctx.HTML(200, tpl) +} + +// SetSession sets session key value +func (ctx *DefaultContext) SetSession(key string, val interface{}) error { + return ctx.Session.Set(key, val) +} + +// GetSession gets session via key +func (ctx *DefaultContext) GetSession(key string) (interface{}, error) { + v := ctx.Session.Get(key) + return v, nil +} + +// DestroySession deletes all the data of the session +func (ctx *DefaultContext) DestroySession() error { + return ctx.Session.Release() +} + +// Flash set message to flash +func (ctx *DefaultContext) Flash(tp, v string) { + if ctx.flash == nil { + ctx.flash = make(Flash) + } + ctx.flash[tp] = v + ctx.Data[tp] = v + ctx.Data["Flash"] = ctx.flash +} + +var ( + defaultContextKey interface{} = "default_context" +) + +// WithDefaultContext set up install context in request +func WithDefaultContext(req *http.Request, ctx *DefaultContext) *http.Request { + return req.WithContext(context.WithValue(req.Context(), defaultContextKey, ctx)) +} + +// GetDefaultContext retrieves install context from request +func GetDefaultContext(req *http.Request) *DefaultContext { + return req.Context().Value(defaultContextKey).(*DefaultContext) +} diff --git a/modules/forms/forms.go b/modules/forms/forms.go new file mode 100644 index 0000000000000..22311688a09d2 --- /dev/null +++ b/modules/forms/forms.go @@ -0,0 +1,119 @@ +// 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 forms + +import ( + "reflect" + "strings" + + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/modules/validation" + + "gitea.com/go-chi/binding" + "github.com/unknwon/com" +) + +// Form form binding interface +type Form interface { + binding.Validator +} + +func init() { + binding.SetNameMapper(com.ToSnakeCase) +} + +func getRuleBody(field reflect.StructField, prefix string) string { + for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { + if strings.HasPrefix(rule, prefix) { + return rule[len(prefix) : len(rule)-1] + } + } + return "" +} + +// GetSize get size int form tag +func GetSize(field reflect.StructField) string { + return getRuleBody(field, "Size(") +} + +// GetMinSize get minimal size in form tag +func GetMinSize(field reflect.StructField) string { + return getRuleBody(field, "MinSize(") +} + +// GetMaxSize get max size in form tag +func GetMaxSize(field reflect.StructField) string { + return getRuleBody(field, "MaxSize(") +} + +// GetInclude get include in form tag +func GetInclude(field reflect.StructField) string { + return getRuleBody(field, "Include(") +} + +func validate(errs binding.Errors, data map[string]interface{}, f Form, l translation.Locale) binding.Errors { + if errs.Len() == 0 { + return errs + } + + data["HasError"] = true + // If the field with name errs[0].FieldNames[0] is not found in form + // somehow, some code later on will panic on Data["ErrorMsg"].(string). + // So initialize it to some default. + data["ErrorMsg"] = l.Tr("form.unknown_error") + auth.AssignForm(f, data) + + typ := reflect.TypeOf(f) + val := reflect.ValueOf(f) + + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + val = val.Elem() + } + + if field, ok := typ.FieldByName(errs[0].FieldNames[0]); ok { + fieldName := field.Tag.Get("form") + if fieldName != "-" { + data["Err_"+field.Name] = true + + trName := field.Tag.Get("locale") + if len(trName) == 0 { + trName = l.Tr("form." + field.Name) + } else { + trName = l.Tr(trName) + } + + switch errs[0].Classification { + case binding.ERR_REQUIRED: + data["ErrorMsg"] = trName + l.Tr("form.require_error") + case binding.ERR_ALPHA_DASH: + data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error") + case binding.ERR_ALPHA_DASH_DOT: + data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error") + case validation.ErrGitRefName: + data["ErrorMsg"] = trName + l.Tr("form.git_ref_name_error") + case binding.ERR_SIZE: + data["ErrorMsg"] = trName + l.Tr("form.size_error", GetSize(field)) + case binding.ERR_MIN_SIZE: + data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinSize(field)) + case binding.ERR_MAX_SIZE: + data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMaxSize(field)) + case binding.ERR_EMAIL: + data["ErrorMsg"] = trName + l.Tr("form.email_error") + case binding.ERR_URL: + data["ErrorMsg"] = trName + l.Tr("form.url_error") + case binding.ERR_INCLUDE: + data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field)) + case validation.ErrGlobPattern: + data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message) + default: + data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification + } + return errs + } + } + return errs +} diff --git a/modules/forms/install.go b/modules/forms/install.go new file mode 100644 index 0000000000000..01d7b8c43bf77 --- /dev/null +++ b/modules/forms/install.go @@ -0,0 +1,73 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package forms + +import ( + "net/http" + + "code.gitea.io/gitea/modules/context" + + "gitea.com/go-chi/binding" +) + +var ( + _ binding.Validator = &InstallForm{} +) + +// InstallForm form for installation page +type InstallForm struct { + DbType string `binding:"Required"` + DbHost string + DbUser string + DbPasswd string + DbName string + SSLMode string + Charset string `binding:"Required;In(utf8,utf8mb4)"` + DbPath string + DbSchema string + + AppName string `binding:"Required" locale:"install.app_name"` + RepoRootPath string `binding:"Required"` + LFSRootPath string + RunUser string `binding:"Required"` + Domain string `binding:"Required"` + SSHPort int + HTTPPort string `binding:"Required"` + AppURL string `binding:"Required"` + LogRootPath string `binding:"Required"` + + SMTPHost string + SMTPFrom string + SMTPUser string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"` + SMTPPasswd string + RegisterConfirm bool + MailNotify bool + + OfflineMode bool + DisableGravatar bool + EnableFederatedAvatar bool + EnableOpenIDSignIn bool + EnableOpenIDSignUp bool + DisableRegistration bool + AllowOnlyExternalRegistration bool + EnableCaptcha bool + RequireSignInView bool + DefaultKeepEmailPrivate bool + DefaultAllowCreateOrganization bool + DefaultEnableTimetracking bool + NoReplyAddress string + + AdminName string `binding:"OmitEmpty;AlphaDashDot;MaxSize(30)" locale:"install.admin_name"` + AdminPasswd string `binding:"OmitEmpty;MaxSize(255)" locale:"install.admin_password"` + AdminConfirmPasswd string + AdminEmail string `binding:"OmitEmpty;MinSize(3);MaxSize(254);Include(@)" locale:"install.admin_email"` +} + +// Validate validates the fields +func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetDefaultContext(req) + return validate(errs, ctx.Data, f, ctx.Locale) +} diff --git a/routers/routes/chi.go b/routers/routes/chi.go index c0ac88957ee2b..a338e6f80cb33 100644 --- a/routers/routes/chi.go +++ b/routers/routes/chi.go @@ -12,10 +12,12 @@ import ( "net/http" "os" "path" + "reflect" "strings" "text/template" "time" + gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/metrics" @@ -24,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/routers" + "gitea.com/go-chi/binding" "gitea.com/go-chi/session" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" @@ -176,7 +179,25 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor } } -// NewChi creates a chi Router +// Wrap converts an install route to a chi route +func Wrap(f func(ctx *gitea_context.DefaultContext)) http.HandlerFunc { + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + ctx := gitea_context.GetDefaultContext(req) + f(ctx) + }) +} + +// Bind binding an obj to a handler +func Bind(obj interface{}, handler func(ctx *gitea_context.DefaultContext, form interface{})) http.HandlerFunc { + var tp = reflect.TypeOf(obj).Elem() + return Wrap(func(ctx *gitea_context.DefaultContext) { + var theObj = reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + binding.Bind(ctx.Req, theObj) + handler(ctx, theObj) + }) +} + +// NewChi creates a basic chi Router func NewChi() chi.Router { c := chi.NewRouter() c.Use(middleware.RealIP) @@ -213,35 +234,14 @@ func NewChi() chi.Router { }, )) - c.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars)) - c.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars)) - return c } -// RegisterInstallRoute registers the install routes -func RegisterInstallRoute(c chi.Router) { - m := NewMacaron() - RegisterMacaronInstallRoute(m) - - // We need at least one handler in chi so that it does not drop - // our middleware: https://github.com/go-gitea/gitea/issues/13725#issuecomment-735244395 - c.Get("/", func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - c.NotFound(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) - - c.MethodNotAllowed(func(w http.ResponseWriter, req *http.Request) { - m.ServeHTTP(w, req) - }) -} - // NormalRoutes represents non install routes func NormalRoutes() http.Handler { r := chi.NewRouter() + r.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars)) + r.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars)) // for health check r.Head("/", func(w http.ResponseWriter, req *http.Request) { diff --git a/routers/install.go b/routers/routes/install.go similarity index 81% rename from routers/install.go rename to routers/routes/install.go index 50e929b6f368f..c80889d180158 100644 --- a/routers/install.go +++ b/routers/routes/install.go @@ -1,8 +1,9 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. // Copyright 2014 The Gogs 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 routers +package routes import ( "fmt" @@ -11,44 +12,91 @@ import ( "os/exec" "path/filepath" "strings" + "time" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/forms" "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/middlewares" "code.gitea.io/gitea/modules/setting" + gitea_templates "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/routers" + "gitea.com/go-chi/session" + "github.com/go-chi/chi" + "github.com/unrolled/render" "gopkg.in/ini.v1" ) const ( // tplInstall template for installation page - tplInstall base.TplName = "install" - tplPostInstall base.TplName = "post-install" + tplInstall = "install" + tplPostInstall = "post-install" ) +// InstallRoutes represents the install routes +func InstallRoutes() http.Handler { + r := chi.NewRouter() + r.Use(InstallInit) + r.Get("/", Wrap(Install)) + r.Post("/", Bind(&forms.InstallForm{}, InstallPost)) + r.NotFound(func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, setting.AppURL, 302) + }) + return r +} + // InstallInit prepare for rendering installation page -func InstallInit(ctx *context.Context) { - if setting.InstallLock { - ctx.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") - ctx.HTML(200, tplPostInstall) - return - } +func InstallInit(next http.Handler) http.Handler { + rnd := render.New(render.Options{ + Directory: "templates", + Extensions: []string{".tmpl"}, + Funcs: gitea_templates.NewFuncMap(), + }) + + return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + if setting.InstallLock { + resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") + _ = rnd.HTML(resp, 200, tplPostInstall, nil) + return + } - ctx.Data["Title"] = ctx.Tr("install.install") - ctx.Data["PageIsInstall"] = true + var locale = middlewares.Locale(resp, req) + var startTime = time.Now() + var ctx = context.DefaultContext{ + Resp: resp, + Req: req, + Locale: locale, + Data: map[string]interface{}{ + "Title": locale.Tr("install.install"), + "PageIsInstall": true, + "DbOptions": setting.SupportedDatabases, + "i18n": locale, + "Language": locale.Language(), + "CurrentURL": setting.AppSubURL + req.URL.RequestURI(), + "PageStartTime": startTime, + "TmplLoadTimes": func() string { + return time.Since(startTime).String() + }, + }, + Render: rnd, + Session: session.GetSession(req), + } - ctx.Data["DbOptions"] = setting.SupportedDatabases + ctx.Req = context.WithDefaultContext(req, &ctx) + next.ServeHTTP(resp, ctx.Req) + }) } // Install render installation page -func Install(ctx *context.Context) { - form := auth.InstallForm{} +func Install(ctx *context.DefaultContext) { + form := forms.InstallForm{} // Database settings form.DbHost = setting.Database.Host @@ -117,14 +165,14 @@ func Install(ctx *context.Context) { form.NoReplyAddress = setting.Service.NoReplyAddress auth.AssignForm(form, ctx.Data) - ctx.HTML(200, tplInstall) + _ = ctx.HTML(200, tplInstall) } // InstallPost response for submit install items -func InstallPost(ctx *context.Context, form auth.InstallForm) { - var err error - ctx.Data["CurDbOption"] = form.DbType +func InstallPost(ctx *context.DefaultContext, iForm interface{}) { + form := iForm.(*forms.InstallForm) + ctx.Data["CurDbOption"] = form.DbType if ctx.HasError() { if ctx.HasValue("Err_SMTPUser") { ctx.Data["Err_SMTP"] = true @@ -135,18 +183,18 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { ctx.Data["Err_Admin"] = true } - ctx.HTML(200, tplInstall) + _ = ctx.HTML(200, tplInstall) return } + var err error if _, err = exec.LookPath("git"); err != nil { - ctx.RenderWithErr(ctx.Tr("install.test_git_failed", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.test_git_failed", err), tplInstall, form) return } // Pass basic check, now test configuration. // Test database setting. - setting.Database.Type = setting.GetDBTypeByName(form.DbType) setting.Database.Host = form.DbHost setting.Database.User = form.DbUser @@ -160,7 +208,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { if (setting.Database.Type == "sqlite3") && len(setting.Database.Path) == 0 { ctx.Data["Err_DbPath"] = true - ctx.RenderWithErr(ctx.Tr("install.err_empty_db_path"), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.err_empty_db_path"), tplInstall, form) return } @@ -171,7 +219,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "https://docs.gitea.io/en-us/install-from-binary/"), tplInstall, &form) } else { ctx.Data["Err_DbSetting"] = true - ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, form) } return } @@ -180,7 +228,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { form.RepoRootPath = strings.ReplaceAll(form.RepoRootPath, "\\", "/") if err = os.MkdirAll(form.RepoRootPath, os.ModePerm); err != nil { ctx.Data["Err_RepoRootPath"] = true - ctx.RenderWithErr(ctx.Tr("install.invalid_repo_path", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.invalid_repo_path", err), tplInstall, form) return } @@ -189,7 +237,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { form.LFSRootPath = strings.ReplaceAll(form.LFSRootPath, "\\", "/") if err := os.MkdirAll(form.LFSRootPath, os.ModePerm); err != nil { ctx.Data["Err_LFSRootPath"] = true - ctx.RenderWithErr(ctx.Tr("install.invalid_lfs_path", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.invalid_lfs_path", err), tplInstall, form) return } } @@ -198,14 +246,14 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { form.LogRootPath = strings.ReplaceAll(form.LogRootPath, "\\", "/") if err = os.MkdirAll(form.LogRootPath, os.ModePerm); err != nil { ctx.Data["Err_LogRootPath"] = true - ctx.RenderWithErr(ctx.Tr("install.invalid_log_root_path", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.invalid_log_root_path", err), tplInstall, form) return } currentUser, match := setting.IsRunUserMatchCurrentUser(form.RunUser) if !match { ctx.Data["Err_RunUser"] = true - ctx.RenderWithErr(ctx.Tr("install.run_user_not_match", form.RunUser, currentUser), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.run_user_not_match", form.RunUser, currentUser), tplInstall, form) return } @@ -350,24 +398,24 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { cfg.Section("security").Key("INSTALL_LOCK").SetValue("true") var secretKey string if secretKey, err = generate.NewSecretKey(); err != nil { - ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, form) return } cfg.Section("security").Key("SECRET_KEY").SetValue(secretKey) err = os.MkdirAll(filepath.Dir(setting.CustomConf), os.ModePerm) if err != nil { - ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, form) return } if err = cfg.SaveTo(setting.CustomConf); err != nil { - ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, form) return } // Re-read settings - PostInstallInit(ctx.Req.Context()) + routers.PostInstallInit(ctx.Req.Context()) // Create admin account if len(form.AdminName) > 0 { @@ -383,7 +431,7 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { setting.InstallLock = false ctx.Data["Err_AdminName"] = true ctx.Data["Err_AdminEmail"] = true - ctx.RenderWithErr(ctx.Tr("install.invalid_admin_setting", err), tplInstall, &form) + ctx.RenderWithErr(ctx.Tr("install.invalid_admin_setting", err), tplInstall, form) return } log.Info("Admin account already exist") @@ -391,32 +439,35 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) { } days := 86400 * setting.LogInRememberDays - ctx.SetCookie(setting.CookieUserName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) - ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd), - setting.CookieRememberName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) + ctx.Req.AddCookie(middlewares.NewCookie(setting.CookieUserName, u.Name, days)) + //ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd), + // setting.CookieRememberName, u.Name, days, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) // Auto-login for admin - if err = ctx.Session.Set("uid", u.ID); err != nil { - ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) + if err = ctx.SetSession("uid", u.ID); err != nil { + ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, form) return } - if err = ctx.Session.Set("uname", u.Name); err != nil { - ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) + if err = ctx.SetSession("uname", u.Name); err != nil { + ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, form) return } - if err = ctx.Session.Release(); err != nil { - ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) + if err = ctx.DestroySession(); err != nil { + ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, form) return } } log.Info("First-time run install finished!") - ctx.Flash.Success(ctx.Tr("install.install_success")) + ctx.Flash(context.SuccessFlash, ctx.Tr("install.install_success")) - ctx.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") - ctx.HTML(200, tplPostInstall) + ctx.Resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login") + if err := ctx.HTML(200, tplPostInstall); err != nil { + http.Error(ctx.Resp, fmt.Sprintf("render %s failed: %v", tplPostInstall, err), 500) + return + } // Now get the http.Server from this request and shut it down // NB: This is not our hammerable graceful shutdown this is http.Server.Shutdown diff --git a/routers/routes/macaron.go b/routers/routes/macaron.go index ca3599b7a0a52..5515019edfcd7 100644 --- a/routers/routes/macaron.go +++ b/routers/routes/macaron.go @@ -135,15 +135,6 @@ func NewMacaron() *macaron.Macaron { return m } -// RegisterMacaronInstallRoute registers the install routes -func RegisterMacaronInstallRoute(m *macaron.Macaron) { - m.Combo("/", routers.InstallInit).Get(routers.Install). - Post(binding.BindIgnErr(auth.InstallForm{}), routers.InstallPost) - m.NotFound(func(ctx *context.Context) { - ctx.Redirect(setting.AppURL, 302) - }) -} - // RegisterMacaronRoutes routes routes to Macaron func RegisterMacaronRoutes(m *macaron.Macaron) { reqSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: true}) @@ -197,8 +188,6 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { m.Get("/organizations", routers.ExploreOrganizations) m.Get("/code", routers.ExploreCode) }, ignSignIn) - m.Combo("/install", routers.InstallInit).Get(routers.Install). - Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues) m.Get("/milestones", reqSignIn, reqMilestonesDashboardPageEnabled, user.Milestones) diff --git a/services/mailer/mail.go b/services/mailer/mail.go index b4217c046612b..ec3a5184c16f5 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/translation" "gopkg.in/gomail.v2" ) @@ -77,24 +78,18 @@ func SendUserMail(language string, u *models.User, tpl base.TplName, code, subje SendAsync(msg) } -// Locale represents an interface to translation -type Locale interface { - Language() string - Tr(string, ...interface{}) string -} - // SendActivateAccountMail sends an activation mail to the user (new user registration) -func SendActivateAccountMail(locale Locale, u *models.User) { +func SendActivateAccountMail(locale translation.Locale, u *models.User) { SendUserMail(locale.Language(), u, mailAuthActivate, u.GenerateActivateCode(), locale.Tr("mail.activate_account"), "activate account") } // SendResetPasswordMail sends a password reset mail to the user -func SendResetPasswordMail(locale Locale, u *models.User) { +func SendResetPasswordMail(locale translation.Locale, u *models.User) { SendUserMail(locale.Language(), u, mailAuthResetPassword, u.GenerateActivateCode(), locale.Tr("mail.reset_password"), "recover account") } // SendActivateEmailMail sends confirmation email to confirm new email address -func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAddress) { +func SendActivateEmailMail(locale translation.Locale, u *models.User, email *models.EmailAddress) { data := map[string]interface{}{ "DisplayName": u.DisplayName(), "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale.Language()), @@ -116,7 +111,7 @@ func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAdd } // SendRegisterNotifyMail triggers a notify e-mail by admin created a account. -func SendRegisterNotifyMail(locale Locale, u *models.User) { +func SendRegisterNotifyMail(locale translation.Locale, u *models.User) { if setting.MailService == nil { log.Warn("SendRegisterNotifyMail is being invoked but mail service hasn't been initialized") return diff --git a/templates/base/alert_new.tmpl b/templates/base/alert_new.tmpl new file mode 100644 index 0000000000000..0cbd8bb396e90 --- /dev/null +++ b/templates/base/alert_new.tmpl @@ -0,0 +1,15 @@ +{{if .ErrorMsg}} +
+{{end}} +{{if .SuccessMsg}} + +{{end}} +{{if .InfoMsg}} + +{{end}} diff --git a/templates/install.tmpl b/templates/install.tmpl index 62aaeaed9a351..f3825241b1152 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -6,7 +6,7 @@ {{.i18n.Tr "install.title"}}{{.i18n.Tr "install.docker_helper" "https://docs.gitea.io/en-us/install-with-docker/" | Safe}}
diff --git a/vendor/gitea.com/go-chi/binding/.drone.yml b/vendor/gitea.com/go-chi/binding/.drone.yml new file mode 100644 index 0000000000000..9baf4f1cf7594 --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/.drone.yml @@ -0,0 +1,24 @@ +kind: pipeline +name: go1-1-1 + +steps: +- name: test + image: golang:1.11 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic + +--- +kind: pipeline +name: go1-1-2 + +steps: +- name: test + image: golang:1.12 + environment: + GOPROXY: https://goproxy.cn + commands: + - go build -v + - go test -v -race -coverprofile=coverage.txt -covermode=atomic \ No newline at end of file diff --git a/vendor/gitea.com/go-chi/binding/.gitignore b/vendor/gitea.com/go-chi/binding/.gitignore new file mode 100644 index 0000000000000..485dee64bcfb4 --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/vendor/gitea.com/go-chi/binding/LICENSE b/vendor/gitea.com/go-chi/binding/LICENSE new file mode 100644 index 0000000000000..8405e89a0b120 --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/vendor/gitea.com/go-chi/binding/README.md b/vendor/gitea.com/go-chi/binding/README.md new file mode 100644 index 0000000000000..80ea7637359d7 --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/README.md @@ -0,0 +1,5 @@ +Middleware binding provides request data binding and validation for net/http, It's a fork of [Macaron](https://github.com/go-macaron/macaron). + +## License + +This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text. \ No newline at end of file diff --git a/vendor/gitea.com/go-chi/binding/binding.go b/vendor/gitea.com/go-chi/binding/binding.go new file mode 100644 index 0000000000000..4c0ecb9baf10a --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/binding.go @@ -0,0 +1,708 @@ +// Copyright 2014 Martini Authors +// 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 binding is a middleware that provides request data binding and validation for Macaron. +package binding + +import ( + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/http" + "net/url" + "reflect" + "regexp" + "strconv" + "strings" + "unicode/utf8" + + "github.com/unknwon/com" +) + +// Bind wraps up the functionality of the Form and Json middleware +// according to the Content-Type and verb of the request. +// A Content-Type is required for POST and PUT requests. +// Bind invokes the ErrorHandler middleware to bail out if errors +// occurred. If you want to perform your own error handling, use +// Form or Json middleware directly. An interface pointer can +// be added as a second argument in order to map the struct to +// a specific interface. +func Bind(req *http.Request, obj interface{}) Errors { + contentType := req.Header.Get("Content-Type") + if req.Method == "POST" || req.Method == "PUT" || len(contentType) > 0 { + switch { + case strings.Contains(contentType, "form-urlencoded"): + return Form(req, obj) + case strings.Contains(contentType, "multipart/form-data"): + return MultipartForm(req, obj) + case strings.Contains(contentType, "json"): + return JSON(req, obj) + default: + var errors Errors + if contentType == "" { + errors.Add([]string{}, ERR_CONTENT_TYPE, "Empty Content-Type") + } else { + errors.Add([]string{}, ERR_CONTENT_TYPE, "Unsupported Content-Type") + } + return errors + } + } else { + return Form(req, obj) + } +} + +const ( + _JSON_CONTENT_TYPE = "application/json; charset=utf-8" + STATUS_UNPROCESSABLE_ENTITY = 422 +) + +// errorHandler simply counts the number of errors in the +// context and, if more than 0, writes a response with an +// error code and a JSON payload describing the errors. +// The response will have a JSON content-type. +// Middleware remaining on the stack will not even see the request +// if, by this point, there are any errors. +// This is a "default" handler, of sorts, and you are +// welcome to use your own instead. The Bind middleware +// invokes this automatically for convenience. +func errorHandler(errs Errors, rw http.ResponseWriter) { + if len(errs) > 0 { + rw.Header().Set("Content-Type", _JSON_CONTENT_TYPE) + if errs.Has(ERR_DESERIALIZATION) { + rw.WriteHeader(http.StatusBadRequest) + } else if errs.Has(ERR_CONTENT_TYPE) { + rw.WriteHeader(http.StatusUnsupportedMediaType) + } else { + rw.WriteHeader(STATUS_UNPROCESSABLE_ENTITY) + } + errOutput, _ := json.Marshal(errs) + rw.Write(errOutput) + return + } +} + +// Form is middleware to deserialize form-urlencoded data from the request. +// It gets data from the form-urlencoded body, if present, or from the +// query string. It uses the http.Request.ParseForm() method +// to perform deserialization, then reflection is used to map each field +// into the struct with the proper type. Structs with primitive slice types +// (bool, float, int, string) can support deserialization of repeated form +// keys, for example: key=val1&key=val2&key=val3 +// An interface pointer can be added as a second argument in order +// to map the struct to a specific interface. +func Form(req *http.Request, formStruct interface{}) Errors { + var errors Errors + + ensurePointer(formStruct) + formStructV := reflect.ValueOf(formStruct) + parseErr := req.ParseForm() + + // Format validation of the request body or the URL would add considerable overhead, + // and ParseForm does not complain when URL encoding is off. + // Because an empty request body or url can also mean absence of all needed values, + // it is not in all cases a bad request, so let's return 422. + if parseErr != nil { + errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error()) + } + errors = mapForm(formStructV, req.Form, nil, errors) + return append(errors, Validate(req, formStruct)...) +} + +// MaxMemory represents maximum amount of memory to use when parsing a multipart form. +// Set this to whatever value you prefer; default is 10 MB. +var MaxMemory = int64(1024 * 1024 * 10) + +// MultipartForm works much like Form, except it can parse multipart forms +// and handle file uploads. Like the other deserialization middleware handlers, +// you can pass in an interface to make the interface available for injection +// into other handlers later. +func MultipartForm(req *http.Request, formStruct interface{}) Errors { + var errors Errors + ensurePointer(formStruct) + formStructV := reflect.ValueOf(formStruct) + // This if check is necessary due to https://github.com/martini-contrib/csrf/issues/6 + if req.MultipartForm == nil { + // Workaround for multipart forms returning nil instead of an error + // when content is not multipart; see https://code.google.com/p/go/issues/detail?id=6334 + if multipartReader, err := req.MultipartReader(); err != nil { + errors.Add([]string{}, ERR_DESERIALIZATION, err.Error()) + } else { + form, parseErr := multipartReader.ReadForm(MaxMemory) + if parseErr != nil { + errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error()) + } + + if req.Form == nil { + req.ParseForm() + } + for k, v := range form.Value { + req.Form[k] = append(req.Form[k], v...) + } + + req.MultipartForm = form + } + } + errors = mapForm(formStructV, req.MultipartForm.Value, req.MultipartForm.File, errors) + return append(errors, Validate(req, formStruct)...) +} + +// JSON is middleware to deserialize a JSON payload from the request +// into the struct that is passed in. The resulting struct is then +// validated, but no error handling is actually performed here. +// An interface pointer can be added as a second argument in order +// to map the struct to a specific interface. +func JSON(req *http.Request, jsonStruct interface{}) Errors { + var errors Errors + ensurePointer(jsonStruct) + + if req.Body != nil { + defer req.Body.Close() + err := json.NewDecoder(req.Body).Decode(jsonStruct) + if err != nil && err != io.EOF { + errors.Add([]string{}, ERR_DESERIALIZATION, err.Error()) + } + } + return append(errors, Validate(req, jsonStruct)...) +} + +// RawValidate is same as Validate but does not require a HTTP context, +// and can be used independently just for validation. +// This function does not support Validator interface. +func RawValidate(obj interface{}) Errors { + var errs Errors + v := reflect.ValueOf(obj) + k := v.Kind() + if k == reflect.Interface || k == reflect.Ptr { + v = v.Elem() + k = v.Kind() + } + if k == reflect.Slice || k == reflect.Array { + for i := 0; i < v.Len(); i++ { + e := v.Index(i).Interface() + errs = validateStruct(errs, e) + } + } else { + errs = validateStruct(errs, obj) + } + return errs +} + +// Validate is middleware to enforce required fields. If the struct +// passed in implements Validator, then the user-defined Validate method +// is executed, and its errors are mapped to the context. This middleware +// performs no error handling: it merely detects errors and maps them. +func Validate(req *http.Request, obj interface{}) Errors { + var errs Errors + v := reflect.ValueOf(obj) + k := v.Kind() + if k == reflect.Interface || k == reflect.Ptr { + v = v.Elem() + k = v.Kind() + } + if k == reflect.Slice || k == reflect.Array { + for i := 0; i < v.Len(); i++ { + e := v.Index(i).Interface() + errs = validateStruct(errs, e) + if validator, ok := e.(Validator); ok { + errs = validator.Validate(req, errs) + } + } + } else { + errs = validateStruct(errs, obj) + if validator, ok := obj.(Validator); ok { + errs = validator.Validate(req, errs) + } + } + return errs +} + +var ( + AlphaDashPattern = regexp.MustCompile("[^\\d\\w-_]") + AlphaDashDotPattern = regexp.MustCompile("[^\\d\\w-_\\.]") + EmailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?") +) + +// Copied from github.com/asaskevich/govalidator. +const _MAX_URL_RUNE_COUNT = 2083 +const _MIN_URL_RUNE_COUNT = 3 + +var ( + urlSchemaRx = `((ftp|tcp|udp|wss?|https?):\/\/)` + urlUsernameRx = `(\S+(:\S*)?@)` + urlIPRx = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))` + ipRx = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))` + urlSubdomainRx = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))` + urlPortRx = `(:(\d{1,5}))` + urlPathRx = `((\/|\?|#)[^\s]*)` + URLPattern = regexp.MustCompile(`^` + urlSchemaRx + `?` + urlUsernameRx + `?` + `((` + urlIPRx + `|(\[` + ipRx + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + urlSubdomainRx + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + urlPortRx + `?` + urlPathRx + `?$`) +) + +// IsURL check if the string is an URL. +func isURL(str string) bool { + if str == "" || utf8.RuneCountInString(str) >= _MAX_URL_RUNE_COUNT || len(str) <= _MIN_URL_RUNE_COUNT || strings.HasPrefix(str, ".") { + return false + } + u, err := url.Parse(str) + if err != nil { + return false + } + if strings.HasPrefix(u.Host, ".") { + return false + } + if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) { + return false + } + return URLPattern.MatchString(str) + +} + +type ( + // Rule represents a validation rule. + Rule struct { + // IsMatch checks if rule matches. + IsMatch func(string) bool + // IsValid applies validation rule to condition. + IsValid func(Errors, string, interface{}) (bool, Errors) + } + + // ParamRule does same thing as Rule but passes rule itself to IsValid method. + ParamRule struct { + // IsMatch checks if rule matches. + IsMatch func(string) bool + // IsValid applies validation rule to condition. + IsValid func(Errors, string, string, interface{}) (bool, Errors) + } + + // RuleMapper and ParamRuleMapper represent validation rule mappers, + // it allwos users to add custom validation rules. + RuleMapper []*Rule + ParamRuleMapper []*ParamRule +) + +var ruleMapper RuleMapper +var paramRuleMapper ParamRuleMapper + +// AddRule adds new validation rule. +func AddRule(r *Rule) { + ruleMapper = append(ruleMapper, r) +} + +// AddParamRule adds new validation rule. +func AddParamRule(r *ParamRule) { + paramRuleMapper = append(paramRuleMapper, r) +} + +func in(fieldValue interface{}, arr string) bool { + val := fmt.Sprintf("%v", fieldValue) + vals := strings.Split(arr, ",") + isIn := false + for _, v := range vals { + if v == val { + isIn = true + break + } + } + return isIn +} + +func parseFormName(raw, actual string) string { + if len(actual) > 0 { + return actual + } + return nameMapper(raw) +} + +// Performs required field checking on a struct +func validateStruct(errors Errors, obj interface{}) Errors { + typ := reflect.TypeOf(obj) + val := reflect.ValueOf(obj) + + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + val = val.Elem() + } + + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + + // Allow ignored fields in the struct + if field.Tag.Get("form") == "-" || !val.Field(i).CanInterface() { + continue + } + + fieldVal := val.Field(i) + fieldValue := fieldVal.Interface() + zero := reflect.Zero(field.Type).Interface() + + // Validate nested and embedded structs (if pointer, only do so if not nil) + if field.Type.Kind() == reflect.Struct || + (field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue) && + field.Type.Elem().Kind() == reflect.Struct) { + errors = validateStruct(errors, fieldValue) + } + errors = validateField(errors, zero, field, fieldVal, fieldValue) + } + return errors +} + +func validateField(errors Errors, zero interface{}, field reflect.StructField, fieldVal reflect.Value, fieldValue interface{}) Errors { + if fieldVal.Kind() == reflect.Slice { + for i := 0; i < fieldVal.Len(); i++ { + sliceVal := fieldVal.Index(i) + if sliceVal.Kind() == reflect.Ptr { + sliceVal = sliceVal.Elem() + } + + sliceValue := sliceVal.Interface() + zero := reflect.Zero(sliceVal.Type()).Interface() + if sliceVal.Kind() == reflect.Struct || + (sliceVal.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, sliceValue) && + sliceVal.Elem().Kind() == reflect.Struct) { + errors = validateStruct(errors, sliceValue) + } + /* Apply validation rules to each item in a slice. ISSUE #3 + else { + errors = validateField(errors, zero, field, sliceVal, sliceValue) + }*/ + } + } + +VALIDATE_RULES: + for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { + if len(rule) == 0 { + continue + } + + switch { + case rule == "OmitEmpty": + if reflect.DeepEqual(zero, fieldValue) { + break VALIDATE_RULES + } + case rule == "Required": + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice { + if v.Len() == 0 { + errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") + break VALIDATE_RULES + } + + continue + } + + if reflect.DeepEqual(zero, fieldValue) { + errors.Add([]string{field.Name}, ERR_REQUIRED, "Required") + break VALIDATE_RULES + } + case rule == "AlphaDash": + if AlphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash") + break VALIDATE_RULES + } + case rule == "AlphaDashDot": + if AlphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_ALPHA_DASH_DOT, "AlphaDashDot") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Size("): + size, _ := strconv.Atoi(rule[5 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) != size { + errors.Add([]string{field.Name}, ERR_SIZE, "Size") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() != size { + errors.Add([]string{field.Name}, ERR_SIZE, "Size") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "MinSize("): + min, _ := strconv.Atoi(rule[8 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min { + errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() < min { + errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "MaxSize("): + max, _ := strconv.Atoi(rule[8 : len(rule)-1]) + if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max { + errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") + break VALIDATE_RULES + } + v := reflect.ValueOf(fieldValue) + if v.Kind() == reflect.Slice && v.Len() > max { + errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Range("): + nums := strings.Split(rule[6:len(rule)-1], ",") + if len(nums) != 2 { + break VALIDATE_RULES + } + val := com.StrTo(fmt.Sprintf("%v", fieldValue)).MustInt() + if val < com.StrTo(nums[0]).MustInt() || val > com.StrTo(nums[1]).MustInt() { + errors.Add([]string{field.Name}, ERR_RANGE, "Range") + break VALIDATE_RULES + } + case rule == "Email": + if !EmailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) { + errors.Add([]string{field.Name}, ERR_EMAIL, "Email") + break VALIDATE_RULES + } + case rule == "Url": + str := fmt.Sprintf("%v", fieldValue) + if len(str) == 0 { + continue + } else if !isURL(str) { + errors.Add([]string{field.Name}, ERR_URL, "Url") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "In("): + if !in(fieldValue, rule[3:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_IN, "In") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "NotIn("): + if in(fieldValue, rule[6:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_NOT_INT, "NotIn") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Include("): + if !strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_INCLUDE, "Include") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Exclude("): + if strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) { + errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude") + break VALIDATE_RULES + } + case strings.HasPrefix(rule, "Default("): + if reflect.DeepEqual(zero, fieldValue) { + if fieldVal.CanAddr() { + errors = setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors) + } else { + errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default") + break VALIDATE_RULES + } + } + default: + // Apply custom validation rules + var isValid bool + for i := range ruleMapper { + if ruleMapper[i].IsMatch(rule) { + isValid, errors = ruleMapper[i].IsValid(errors, field.Name, fieldValue) + if !isValid { + break VALIDATE_RULES + } + } + } + for i := range paramRuleMapper { + if paramRuleMapper[i].IsMatch(rule) { + isValid, errors = paramRuleMapper[i].IsValid(errors, rule, field.Name, fieldValue) + if !isValid { + break VALIDATE_RULES + } + } + } + } + } + return errors +} + +// NameMapper represents a form tag name mapper. +type NameMapper func(string) string + +var ( + nameMapper = func(field string) string { + newstr := make([]rune, 0, len(field)) + for i, chr := range field { + if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { + if i > 0 { + newstr = append(newstr, '_') + } + chr -= ('A' - 'a') + } + newstr = append(newstr, chr) + } + return string(newstr) + } +) + +// SetNameMapper sets name mapper. +func SetNameMapper(nm NameMapper) { + nameMapper = nm +} + +// Takes values from the form data and puts them into a struct +func mapForm(formStruct reflect.Value, form map[string][]string, + formfile map[string][]*multipart.FileHeader, errors Errors) Errors { + + if formStruct.Kind() == reflect.Ptr { + formStruct = formStruct.Elem() + } + typ := formStruct.Type() + + for i := 0; i < typ.NumField(); i++ { + typeField := typ.Field(i) + structField := formStruct.Field(i) + + if typeField.Type.Kind() == reflect.Ptr && typeField.Anonymous { + structField.Set(reflect.New(typeField.Type.Elem())) + errors = mapForm(structField.Elem(), form, formfile, errors) + if reflect.DeepEqual(structField.Elem().Interface(), reflect.Zero(structField.Elem().Type()).Interface()) { + structField.Set(reflect.Zero(structField.Type())) + } + } else if typeField.Type.Kind() == reflect.Struct { + errors = mapForm(structField, form, formfile, errors) + } + + inputFieldName := parseFormName(typeField.Name, typeField.Tag.Get("form")) + if len(inputFieldName) == 0 || !structField.CanSet() { + continue + } + + inputValue, exists := form[inputFieldName] + if exists { + numElems := len(inputValue) + if structField.Kind() == reflect.Slice && numElems > 0 { + sliceOf := structField.Type().Elem().Kind() + slice := reflect.MakeSlice(structField.Type(), numElems, numElems) + for i := 0; i < numElems; i++ { + errors = setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors) + } + formStruct.Field(i).Set(slice) + } else { + errors = setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors) + } + continue + } + + inputFile, exists := formfile[inputFieldName] + if !exists { + continue + } + fhType := reflect.TypeOf((*multipart.FileHeader)(nil)) + numElems := len(inputFile) + if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType { + slice := reflect.MakeSlice(structField.Type(), numElems, numElems) + for i := 0; i < numElems; i++ { + slice.Index(i).Set(reflect.ValueOf(inputFile[i])) + } + structField.Set(slice) + } else if structField.Type() == fhType { + structField.Set(reflect.ValueOf(inputFile[0])) + } + } + return errors +} + +// This sets the value in a struct of an indeterminate type to the +// matching value from the request (via Form middleware) in the +// same type, so that not all deserialized values have to be strings. +// Supported types are string, int, float, and bool. +func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value, nameInTag string, errors Errors) Errors { + switch valueKind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if val == "" { + val = "0" + } + intVal, err := strconv.ParseInt(val, 10, 64) + if err != nil { + errors.Add([]string{nameInTag}, ERR_INTERGER_TYPE, "Value could not be parsed as integer") + } else { + structField.SetInt(intVal) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if val == "" { + val = "0" + } + uintVal, err := strconv.ParseUint(val, 10, 64) + if err != nil { + errors.Add([]string{nameInTag}, ERR_INTERGER_TYPE, "Value could not be parsed as unsigned integer") + } else { + structField.SetUint(uintVal) + } + case reflect.Bool: + if val == "on" { + structField.SetBool(true) + break + } + + if val == "" { + val = "false" + } + boolVal, err := strconv.ParseBool(val) + if err != nil { + errors.Add([]string{nameInTag}, ERR_BOOLEAN_TYPE, "Value could not be parsed as boolean") + } else if boolVal { + structField.SetBool(true) + } + case reflect.Float32: + if val == "" { + val = "0.0" + } + floatVal, err := strconv.ParseFloat(val, 32) + if err != nil { + errors.Add([]string{nameInTag}, ERR_FLOAT_TYPE, "Value could not be parsed as 32-bit float") + } else { + structField.SetFloat(floatVal) + } + case reflect.Float64: + if val == "" { + val = "0.0" + } + floatVal, err := strconv.ParseFloat(val, 64) + if err != nil { + errors.Add([]string{nameInTag}, ERR_FLOAT_TYPE, "Value could not be parsed as 64-bit float") + } else { + structField.SetFloat(floatVal) + } + case reflect.String: + structField.SetString(val) + } + return errors +} + +// Pointers must be bind to. +func ensurePointer(obj interface{}) { + if reflect.TypeOf(obj).Kind() != reflect.Ptr { + panic("Pointers are only accepted as binding models") + } +} + +type ( + // ErrorHandler is the interface that has custom error handling process. + ErrorHandler interface { + // Error handles validation errors with custom process. + Error(*http.Request, Errors) + } + + // Validator is the interface that handles some rudimentary + // request validation logic so your application doesn't have to. + Validator interface { + // Validate validates that the request is OK. It is recommended + // that validation be limited to checking values for syntax and + // semantics, enough to know that you can make sense of the request + // in your application. For example, you might verify that a credit + // card number matches a valid pattern, but you probably wouldn't + // perform an actual credit card authorization here. + Validate(*http.Request, Errors) Errors + } +) diff --git a/vendor/gitea.com/go-chi/binding/errors.go b/vendor/gitea.com/go-chi/binding/errors.go new file mode 100644 index 0000000000000..8cbe44a9d1726 --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/errors.go @@ -0,0 +1,159 @@ +// Copyright 2014 Martini Authors +// Copyright 2014 The Macaron 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 binding + +const ( + // Type mismatch errors. + ERR_CONTENT_TYPE = "ContentTypeError" + ERR_DESERIALIZATION = "DeserializationError" + ERR_INTERGER_TYPE = "IntegerTypeError" + ERR_BOOLEAN_TYPE = "BooleanTypeError" + ERR_FLOAT_TYPE = "FloatTypeError" + + // Validation errors. + ERR_REQUIRED = "RequiredError" + ERR_ALPHA_DASH = "AlphaDashError" + ERR_ALPHA_DASH_DOT = "AlphaDashDotError" + ERR_SIZE = "SizeError" + ERR_MIN_SIZE = "MinSizeError" + ERR_MAX_SIZE = "MaxSizeError" + ERR_RANGE = "RangeError" + ERR_EMAIL = "EmailError" + ERR_URL = "UrlError" + ERR_IN = "InError" + ERR_NOT_INT = "NotInError" + ERR_INCLUDE = "IncludeError" + ERR_EXCLUDE = "ExcludeError" + ERR_DEFAULT = "DefaultError" +) + +type ( + // Errors may be generated during deserialization, binding, + // or validation. This type is mapped to the context so you + // can inject it into your own handlers and use it in your + // application if you want all your errors to look the same. + Errors []Error + + Error struct { + // An error supports zero or more field names, because an + // error can morph three ways: (1) it can indicate something + // wrong with the request as a whole, (2) it can point to a + // specific problem with a particular input field, or (3) it + // can span multiple related input fields. + FieldNames []string `json:"fieldNames,omitempty"` + + // The classification is like an error code, convenient to + // use when processing or categorizing an error programmatically. + // It may also be called the "kind" of error. + Classification string `json:"classification,omitempty"` + + // Message should be human-readable and detailed enough to + // pinpoint and resolve the problem, but it should be brief. For + // example, a payload of 100 objects in a JSON array might have + // an error in the 41st object. The message should help the + // end user find and fix the error with their request. + Message string `json:"message,omitempty"` + } +) + +// Add adds an error associated with the fields indicated +// by fieldNames, with the given classification and message. +func (e *Errors) Add(fieldNames []string, classification, message string) { + *e = append(*e, Error{ + FieldNames: fieldNames, + Classification: classification, + Message: message, + }) +} + +// Len returns the number of errors. +func (e *Errors) Len() int { + return len(*e) +} + +// Has determines whether an Errors slice has an Error with +// a given classification in it; it does not search on messages +// or field names. +func (e *Errors) Has(class string) bool { + for _, err := range *e { + if err.Kind() == class { + return true + } + } + return false +} + +/* +// WithClass gets a copy of errors that are classified by the +// the given classification. +func (e *Errors) WithClass(classification string) Errors { + var errs Errors + for _, err := range *e { + if err.Kind() == classification { + errs = append(errs, err) + } + } + return errs +} + +// ForField gets a copy of errors that are associated with the +// field by the given name. +func (e *Errors) ForField(name string) Errors { + var errs Errors + for _, err := range *e { + for _, fieldName := range err.Fields() { + if fieldName == name { + errs = append(errs, err) + break + } + } + } + return errs +} + +// Get gets errors of a particular class for the specified +// field name. +func (e *Errors) Get(class, fieldName string) Errors { + var errs Errors + for _, err := range *e { + if err.Kind() == class { + for _, nameOfField := range err.Fields() { + if nameOfField == fieldName { + errs = append(errs, err) + break + } + } + } + } + return errs +} +*/ + +// Fields returns the list of field names this error is +// associated with. +func (e Error) Fields() []string { + return e.FieldNames +} + +// Kind returns this error's classification. +func (e Error) Kind() string { + return e.Classification +} + +// Error returns this error's message. +func (e Error) Error() string { + return e.Message +} diff --git a/vendor/gitea.com/go-chi/binding/go.mod b/vendor/gitea.com/go-chi/binding/go.mod new file mode 100644 index 0000000000000..194751d004269 --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/go.mod @@ -0,0 +1,10 @@ +module gitea.com/go-chi/binding + +go 1.13 + +require ( + gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb + github.com/go-chi/chi v1.5.1 + github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 + github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e +) diff --git a/vendor/gitea.com/go-chi/binding/go.sum b/vendor/gitea.com/go-chi/binding/go.sum new file mode 100644 index 0000000000000..aa4f5c6da6b9f --- /dev/null +++ b/vendor/gitea.com/go-chi/binding/go.sum @@ -0,0 +1,32 @@ +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591 h1:UbCTjPcLrNxR9LzKDjQBMT2zoxZuEnca1pZCpgeMuhQ= +gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA= +gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= +github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY= +github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= +github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM= +github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/ini.v1 v1.44.0 h1:YRJzTUp0kSYWUVFF5XAbDFfyiqwsl0Vb9R8TVP5eRi0= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/vendor/modules.txt b/vendor/modules.txt index 426b0dc9473a5..f890bed843257 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,6 +7,9 @@ code.gitea.io/gitea-vet/checks # code.gitea.io/sdk/gitea v0.13.1 ## explicit code.gitea.io/sdk/gitea +# gitea.com/go-chi/binding v0.0.0-20201220025549-f1056649c959 +## explicit +gitea.com/go-chi/binding # gitea.com/go-chi/session v0.0.0-20201218134809-7209fa084f27 ## explicit gitea.com/go-chi/session