Compare commits

...

7 Commits

Author SHA1 Message Date
Naxdy
8f263371a4
Merge fc4b73eac934a2931e153a44031ff5bb087c9197 into b907b9fb1a1a792b9bc25112fa3eb3f8d2fb4397 2025-10-01 15:23:37 +02:00
silverwind
b907b9fb1a
Enable a few more tsconfig options (#35553)
Enable a few more useful options in tsconfig. `noImplicitReturns` had
two cases which I've fixed. Also, partially sort the file.
2025-09-30 21:43:41 -07:00
dependabot[bot]
c5d74e5869
Bump github.com/wneessen/go-mail from 0.6.2 to 0.7.1 (#35557) 2025-10-01 00:14:53 +00:00
Steven Noonan
c5332fdc55
add more routes to the "expensive" list (#35547)
Signed-off-by: Steven Noonan <steven@uplinklabs.net>
2025-09-29 17:33:28 +08:00
wxiaoguang
0f668145e9
Drop json-iterator dependency (#35544) 2025-09-28 22:30:28 +08:00
6543
fbe80e6df2
Add proper error message if session provider can not be created (#35520)
the middleware that creates the session provider just panics if on
creation the config is wrong.
this is not catched and so you just get an cryptic stacktrace with no
point where to look at (as user).

## Before

```
2025/09/16 03:56:37 ...xer/stats/indexer.go:87:populateRepoIndexer() [I] Done (re)populating the repo stats indexer with existing repositories
2025/09/16 03:56:37 modules/ssh/ssh.go:387:Listen() [I] Adding SSH host key: /var/lib/gitea/data/ssh/gitea.rsa
2025/09/16 03:56:37 modules/ssh/init.go:26:Init() [I] SSH server started on :1234. Cipher list ([chacha20-poly1305@openssh.com aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com]), key exchange algorithms ([curve25519-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 diffie-hellman-group14-sha256 diffie-hellman-group14-sha1]), MACs ([hmac-sha2-256-etm@openssh.com hmac-sha2-256 hmac-sha1])
2025/09/16 03:56:37 ...s/graceful/server.go:50:NewServer() [I] Starting new SSH server: tcp::1234 on PID: 83337
2025/09/16 03:56:38 cmd/web.go:231:func1() [F] PANIC: dial tcp 127.0.0.1:6379: connect: connection refused
gitea.com/go-chi/session@v0.0.0-20240316035857-16768d98ec96/session.go:239 (0x1cdb908)
code.gitea.io/gitea/routers/common/middleware.go:108 (0x2547f5a)
code.gitea.io/gitea/routers/web/web.go:270 (0x278b8e9)
code.gitea.io/gitea/routers/init.go:185 (0x2850d89)
code.gitea.io/gitea/cmd/web.go:211 (0x295c5ad)
code.gitea.io/gitea/cmd/web.go:262 (0x295cacb)
code.gitea.io/gitea/cmd/main.go:111 (0x2953422)
github.com/urfave/cli/v2@v2.27.2/command.go:276 (0x1cc3dfd)
github.com/urfave/cli/v2@v2.27.2/command.go:269 (0x1cc4084)
github.com/urfave/cli/v2@v2.27.2/app.go:333 (0x1cc086a)
github.com/urfave/cli/v2@v2.27.2/app.go:307 (0x2953f18)
code.gitea.io/gitea/cmd/main.go:172 (0x2953efc)
code.gitea.io/gitea/main.go:46 (0x2998498)
runtime/proc.go:283 (0x4471ca)
runtime/asm_amd64.s:1700 (0x484a20)
```

## After

```
2025/09/22 22:52:35 .../templates/htmlrenderer.go:118:initHTMLRenderer() [D] Creating static HTML Renderer
2025/09/22 22:52:35 routers/web/web.go:273:Routes() [F] common.Sessioner failed: failed to create session middleware: dial tcp 127.0.0.1:6379: connect: connection refused
```

---------

Signed-off-by: 6543 <6543@obermui.de>
2025-09-28 12:24:19 +00:00
Naxdy
fc4b73eac9 Display badge on blocked & blocking issues 2025-08-04 17:47:49 +02:00
24 changed files with 341 additions and 134 deletions

12
go.mod
View File

@ -16,7 +16,7 @@ require (
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
gitea.com/go-chi/cache v0.2.1 gitea.com/go-chi/cache v0.2.1
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/httpsig v1.2.3 github.com/42wim/httpsig v1.2.3
@ -61,6 +61,7 @@ require (
github.com/go-redsync/redsync/v4 v4.13.0 github.com/go-redsync/redsync/v4 v4.13.0
github.com/go-sql-driver/mysql v1.9.3 github.com/go-sql-driver/mysql v1.9.3
github.com/go-webauthn/webauthn v0.13.4 github.com/go-webauthn/webauthn v0.13.4
github.com/goccy/go-json v0.10.5
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.3.0 github.com/golang-jwt/jwt/v5 v5.3.0
@ -75,7 +76,6 @@ require (
github.com/huandu/xstrings v1.5.0 github.com/huandu/xstrings v1.5.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/jhillyerd/enmime v1.3.0 github.com/jhillyerd/enmime v1.3.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/klauspost/compress v1.18.0 github.com/klauspost/compress v1.18.0
github.com/klauspost/cpuid/v2 v2.3.0 github.com/klauspost/cpuid/v2 v2.3.0
@ -109,7 +109,7 @@ require (
github.com/ulikunitz/xz v0.5.15 github.com/ulikunitz/xz v0.5.15
github.com/urfave/cli-docs/v3 v3.0.0-alpha6 github.com/urfave/cli-docs/v3 v3.0.0-alpha6
github.com/urfave/cli/v3 v3.4.1 github.com/urfave/cli/v3 v3.4.1
github.com/wneessen/go-mail v0.6.2 github.com/wneessen/go-mail v0.7.1
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1 github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.7.13 github.com/yuin/goldmark v1.7.13
@ -120,9 +120,9 @@ require (
golang.org/x/image v0.30.0 golang.org/x/image v0.30.0
golang.org/x/net v0.43.0 golang.org/x/net v0.43.0
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.16.0 golang.org/x/sync v0.17.0
golang.org/x/sys v0.35.0 golang.org/x/sys v0.35.0
golang.org/x/text v0.28.0 golang.org/x/text v0.29.0
google.golang.org/grpc v1.75.0 google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.8 google.golang.org/protobuf v1.36.8
gopkg.in/ini.v1 v1.67.0 gopkg.in/ini.v1 v1.67.0
@ -200,7 +200,6 @@ require (
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect github.com/go-ini/ini v1.67.0 // indirect
github.com/go-webauthn/x v0.1.24 // indirect github.com/go-webauthn/x v0.1.24 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect
@ -220,6 +219,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.4.0 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect github.com/klauspost/pgzip v1.2.6 // indirect
github.com/libdns/libdns v1.1.1 // indirect github.com/libdns/libdns v1.1.1 // indirect

21
go.sum
View File

@ -43,8 +43,8 @@ gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q= gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo= gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk= gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 h1:IFDiMBObsP6CZIRaDLd54SR6zPYAffPXiXck5Xslu0Q= gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15 h1:qFYmz05u/s9664o7+XEgrlHXSPQ4uHO8/ccZGUb1uxA=
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM= gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw= gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY= gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o= gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o=
@ -765,8 +765,8 @@ github.com/urfave/cli/v3 v3.4.1/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZ
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/wneessen/go-mail v0.6.2 h1:c6V7c8D2mz868z9WJ+8zDKtUyLfZ1++uAZmo2GRFji8= github.com/wneessen/go-mail v0.7.1 h1:rvy63sp14N06/kdGqCYwW8Na5gDCXjTQM1E7So4PuKk=
github.com/wneessen/go-mail v0.6.2/go.mod h1:L/PYjPK3/2ZlNb2/FjEBIn9n1rUWjW+Toy531oVmeb4= github.com/wneessen/go-mail v0.7.1/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
@ -837,7 +837,6 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
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=
@ -930,9 +929,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -974,7 +972,6 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
@ -987,7 +984,6 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1003,9 +999,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=

View File

@ -559,6 +559,74 @@ func (issues IssueList) LoadDiscussComments(ctx context.Context) error {
return issues.loadComments(ctx, builder.Eq{"comment.type": CommentTypeComment}) return issues.loadComments(ctx, builder.Eq{"comment.type": CommentTypeComment})
} }
// GetBlockedByCounts returns a map of issue ID to number of open issues that are blocking it
func (issues IssueList) GetBlockedByCount(ctx context.Context) (map[int64]int64, error) {
type BlockedByCount struct {
IssueID int64
Count int64
}
bCounts := make([]*BlockedByCount, len(issues))
ids := make([]int64, len(issues))
for i, issue := range issues {
ids[i] = issue.ID
}
sess := db.GetEngine(ctx).In("issue_id", ids)
err := sess.Select("issue_id, count(issue_dependency.id) as `count`").
Join("INNER", "issue", "issue.id = issue_dependency.dependency_id").
Where("is_closed = ?", false).
GroupBy("issue_id").
OrderBy("issue_id").
Table("issue_dependency").
Find(&bCounts)
if err != nil {
return nil, err
}
blockedByCountMap := make(map[int64]int64, len(issues))
for _, c := range bCounts {
if c != nil {
blockedByCountMap[c.IssueID] = c.Count
}
}
return blockedByCountMap, nil
}
// GetBlockingCounts returns a map of issue ID to number of issues that are blocked by it
func (issues IssueList) GetBlockingCount(ctx context.Context) (map[int64]int64, error) {
type BlockingCount struct {
IssueID int64
Count int64
}
bCounts := make([]*BlockingCount, 0, len(issues))
ids := make([]int64, len(issues))
for i, issue := range issues {
ids[i] = issue.ID
}
sess := db.GetEngine(ctx).In("dependency_id", ids)
err := sess.Select("dependency_id as `issue_id`, count(id) as `count`").
GroupBy("dependency_id").
OrderBy("dependency_id").
Table("issue_dependency").
Find(&bCounts)
if err != nil {
return nil, err
}
blockingCountMap := make(map[int64]int64, len(issues))
for _, c := range bCounts {
if c != nil {
blockingCountMap[c.IssueID] = c.Count
}
}
return blockingCountMap, nil
}
// GetApprovalCounts returns a map of issue ID to slice of approval counts // GetApprovalCounts returns a map of issue ID to slice of approval counts
// FIXME: only returns official counts due to double counting of non-official approvals // FIXME: only returns official counts due to double counting of non-official approvals
func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*ReviewCount, error) { func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*ReviewCount, error) {

View File

@ -8,8 +8,6 @@ import (
"encoding/binary" "encoding/binary"
"encoding/json" //nolint:depguard // this package wraps it "encoding/json" //nolint:depguard // this package wraps it
"io" "io"
jsoniter "github.com/json-iterator/go"
) )
// Encoder represents an encoder for json // Encoder represents an encoder for json
@ -31,70 +29,7 @@ type Interface interface {
Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
} }
var ( var DefaultJSONHandler = getDefaultJSONHandler()
DefaultJSONHandler = getDefaultJSONHandler()
_ Interface = StdJSON{}
_ Interface = JSONiter{}
)
// StdJSON implements Interface via encoding/json
type StdJSON struct{}
// Marshal implements Interface
func (StdJSON) Marshal(v any) ([]byte, error) {
return json.Marshal(v)
}
// Unmarshal implements Interface
func (StdJSON) Unmarshal(data []byte, v any) error {
return json.Unmarshal(data, v)
}
// NewEncoder implements Interface
func (StdJSON) NewEncoder(writer io.Writer) Encoder {
return json.NewEncoder(writer)
}
// NewDecoder implements Interface
func (StdJSON) NewDecoder(reader io.Reader) Decoder {
return json.NewDecoder(reader)
}
// Indent implements Interface
func (StdJSON) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
return json.Indent(dst, src, prefix, indent)
}
// JSONiter implements Interface via jsoniter
type JSONiter struct {
jsoniter.API
}
// Marshal implements Interface
func (j JSONiter) Marshal(v any) ([]byte, error) {
return j.API.Marshal(v)
}
// Unmarshal implements Interface
func (j JSONiter) Unmarshal(data []byte, v any) error {
return j.API.Unmarshal(data, v)
}
// NewEncoder implements Interface
func (j JSONiter) NewEncoder(writer io.Writer) Encoder {
return j.API.NewEncoder(writer)
}
// NewDecoder implements Interface
func (j JSONiter) NewDecoder(reader io.Reader) Decoder {
return j.API.NewDecoder(reader)
}
// Indent implements Interface, since jsoniter don't support Indent, just use encoding/json's
func (j JSONiter) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
return json.Indent(dst, src, prefix, indent)
}
// Marshal converts object as bytes // Marshal converts object as bytes
func Marshal(v any) ([]byte, error) { func Marshal(v any) ([]byte, error) {

35
modules/json/jsongoccy.go Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package json
import (
"bytes"
"io"
"github.com/goccy/go-json"
)
var _ Interface = jsonGoccy{}
type jsonGoccy struct{}
func (jsonGoccy) Marshal(v any) ([]byte, error) {
return json.Marshal(v)
}
func (jsonGoccy) Unmarshal(data []byte, v any) error {
return json.Unmarshal(data, v)
}
func (jsonGoccy) NewEncoder(writer io.Writer) Encoder {
return json.NewEncoder(writer)
}
func (jsonGoccy) NewDecoder(reader io.Reader) Decoder {
return json.NewDecoder(reader)
}
func (jsonGoccy) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
return json.Indent(dst, src, prefix, indent)
}

View File

@ -7,12 +7,10 @@ package json
import ( import (
"io" "io"
jsoniter "github.com/json-iterator/go"
) )
func getDefaultJSONHandler() Interface { func getDefaultJSONHandler() Interface {
return JSONiter{jsoniter.ConfigCompatibleWithStandardLibrary} return jsonGoccy{}
} }
func MarshalKeepOptionalEmpty(v any) ([]byte, error) { func MarshalKeepOptionalEmpty(v any) ([]byte, error) {

34
modules/json/jsonv1.go Normal file
View File

@ -0,0 +1,34 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package json
import (
"bytes"
"encoding/json" //nolint:depguard // this package wraps it
"io"
)
type jsonV1 struct{}
var _ Interface = jsonV1{}
func (jsonV1) Marshal(v any) ([]byte, error) {
return json.Marshal(v)
}
func (jsonV1) Unmarshal(data []byte, v any) error {
return json.Unmarshal(data, v)
}
func (jsonV1) NewEncoder(writer io.Writer) Encoder {
return json.NewEncoder(writer)
}
func (jsonV1) NewDecoder(reader io.Reader) Decoder {
return json.NewDecoder(reader)
}
func (jsonV1) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
return json.Indent(dst, src, prefix, indent)
}

View File

@ -193,7 +193,7 @@ func TestHTTPClientDownload(t *testing.T) {
}, },
{ {
endpoint: "https://invalid-json-response.io", endpoint: "https://invalid-json-response.io",
expectedError: "/(invalid json|jsontext: invalid character)/", expectedError: "/(invalid json|invalid character)/",
}, },
{ {
endpoint: "https://valid-batch-request-download.io", endpoint: "https://valid-batch-request-download.io",
@ -301,7 +301,7 @@ func TestHTTPClientUpload(t *testing.T) {
}, },
{ {
endpoint: "https://invalid-json-response.io", endpoint: "https://invalid-json-response.io",
expectedError: "/(invalid json|jsontext: invalid character)/", expectedError: "/(invalid json|invalid character)/",
}, },
{ {
endpoint: "https://valid-batch-request-upload.io", endpoint: "https://valid-batch-request-upload.io",

View File

@ -5,6 +5,7 @@ package session
import ( import (
"context" "context"
"fmt"
"log" "log"
"sync" "sync"
@ -121,12 +122,12 @@ func (p *DBProvider) Read(sid string) (session.RawStore, error) {
} }
// Exist returns true if session with given ID exists. // Exist returns true if session with given ID exists.
func (p *DBProvider) Exist(sid string) bool { func (p *DBProvider) Exist(sid string) (bool, error) {
has, err := auth.ExistSession(dbContext(), sid) has, err := auth.ExistSession(dbContext(), sid)
if err != nil { if err != nil {
panic("session/DB: error checking existence: " + err.Error()) return false, fmt.Errorf("session/DB: error checking existence: %w", err)
} }
return has return has, nil
} }
// Destroy deletes a session by session ID. // Destroy deletes a session by session ID.
@ -155,12 +156,12 @@ func (p *DBProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err err
} }
// Count counts and returns number of sessions. // Count counts and returns number of sessions.
func (p *DBProvider) Count() int { func (p *DBProvider) Count() (int, error) {
total, err := auth.CountSessions(dbContext()) total, err := auth.CountSessions(dbContext())
if err != nil { if err != nil {
panic("session/DB: error counting records: " + err.Error()) return 0, fmt.Errorf("session/DB: error counting records: %w", err)
} }
return int(total) return int(total), nil
} }
// GC calls GC to clean expired sessions. // GC calls GC to clean expired sessions.

View File

@ -135,10 +135,12 @@ func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) {
// Read returns raw session store by session ID. // Read returns raw session store by session ID.
func (p *RedisProvider) Read(sid string) (session.RawStore, error) { func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
psid := p.prefix + sid psid := p.prefix + sid
if !p.Exist(sid) { if exist, err := p.Exist(sid); err == nil && !exist {
if err := p.c.Set(graceful.GetManager().HammerContext(), psid, "", p.duration).Err(); err != nil { if err := p.c.Set(graceful.GetManager().HammerContext(), psid, "", p.duration).Err(); err != nil {
return nil, err return nil, err
} }
} else if err != nil {
return nil, err
} }
var kv map[any]any var kv map[any]any
@ -159,9 +161,9 @@ func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
} }
// Exist returns true if session with given ID exists. // Exist returns true if session with given ID exists.
func (p *RedisProvider) Exist(sid string) bool { func (p *RedisProvider) Exist(sid string) (bool, error) {
v, err := p.c.Exists(graceful.GetManager().HammerContext(), p.prefix+sid).Result() v, err := p.c.Exists(graceful.GetManager().HammerContext(), p.prefix+sid).Result()
return err == nil && v == 1 return err == nil && v == 1, err
} }
// Destroy deletes a session by session ID. // Destroy deletes a session by session ID.
@ -174,13 +176,18 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err
poldsid := p.prefix + oldsid poldsid := p.prefix + oldsid
psid := p.prefix + sid psid := p.prefix + sid
if p.Exist(sid) { if exist, err := p.Exist(sid); err != nil {
return nil, err
} else if exist {
return nil, fmt.Errorf("new sid '%s' already exists", sid) return nil, fmt.Errorf("new sid '%s' already exists", sid)
} else if !p.Exist(oldsid) { }
if exist, err := p.Exist(oldsid); err == nil && !exist {
// Make a fake old session. // Make a fake old session.
if err = p.c.Set(graceful.GetManager().HammerContext(), poldsid, "", p.duration).Err(); err != nil { if err := p.c.Set(graceful.GetManager().HammerContext(), poldsid, "", p.duration).Err(); err != nil {
return nil, err return nil, err
} }
} else if err != nil {
return nil, err
} }
// do not use Rename here, because the old sid and new sid may be in different redis cluster slot. // do not use Rename here, because the old sid and new sid may be in different redis cluster slot.
@ -211,12 +218,9 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err
} }
// Count counts and returns number of sessions. // Count counts and returns number of sessions.
func (p *RedisProvider) Count() int { func (p *RedisProvider) Count() (int, error) {
size, err := p.c.DBSize(graceful.GetManager().HammerContext()).Result() size, err := p.c.DBSize(graceful.GetManager().HammerContext()).Result()
if err != nil { return int(size), err
return 0
}
return int(size)
} }
// GC calls GC to clean expired sessions. // GC calls GC to clean expired sessions.

View File

@ -59,8 +59,10 @@ func (o *VirtualSessionProvider) Init(gcLifetime int64, config string) error {
func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) { func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
o.lock.RLock() o.lock.RLock()
defer o.lock.RUnlock() defer o.lock.RUnlock()
if o.provider.Exist(sid) { if exist, err := o.provider.Exist(sid); err == nil && exist {
return o.provider.Read(sid) return o.provider.Read(sid)
} else if err != nil {
return nil, fmt.Errorf("check if '%s' exist failed: %w", sid, err)
} }
kv := make(map[any]any) kv := make(map[any]any)
kv["_old_uid"] = "0" kv["_old_uid"] = "0"
@ -68,8 +70,8 @@ func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
} }
// Exist returns true if session with given ID exists. // Exist returns true if session with given ID exists.
func (o *VirtualSessionProvider) Exist(sid string) bool { func (o *VirtualSessionProvider) Exist(sid string) (bool, error) {
return true return true, nil
} }
// Destroy deletes a session by session ID. // Destroy deletes a session by session ID.
@ -87,7 +89,7 @@ func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStor
} }
// Count counts and returns number of sessions. // Count counts and returns number of sessions.
func (o *VirtualSessionProvider) Count() int { func (o *VirtualSessionProvider) Count() (int, error) {
o.lock.RLock() o.lock.RLock()
defer o.lock.RUnlock() defer o.lock.RUnlock()
return o.provider.Count() return o.provider.Count()
@ -162,9 +164,13 @@ func (s *VirtualStore) Release() error {
// Now ensure that we don't exist! // Now ensure that we don't exist!
realProvider := s.p.provider realProvider := s.p.provider
if !s.released && realProvider.Exist(s.sid) { if !s.released {
if exist, err := realProvider.Exist(s.sid); err == nil && exist {
// This is an error! // This is an error!
return fmt.Errorf("new sid '%s' already exists", s.sid) return fmt.Errorf("new sid '%s' already exists", s.sid)
} else if err != nil {
return fmt.Errorf("check if '%s' exist failed: %w", s.sid, err)
}
} }
realStore, err := realProvider.Read(s.sid) realStore, err := realProvider.Read(s.sid)
if err != nil { if err != nil {

View File

@ -1805,6 +1805,10 @@ issues.dependency.add_error_dep_not_exist = Dependency does not exist.
issues.dependency.add_error_dep_exists = Dependency already exists. issues.dependency.add_error_dep_exists = Dependency already exists.
issues.dependency.add_error_cannot_create_circular = You cannot create a dependency with two issues that block each other. issues.dependency.add_error_cannot_create_circular = You cannot create a dependency with two issues that block each other.
issues.dependency.add_error_dep_not_same_repo = Both issues must be in the same repository. issues.dependency.add_error_dep_not_same_repo = Both issues must be in the same repository.
issues.dependency.blocking_count_1 = "This issue is blocking %d other issue."
issues.dependency.blocking_count_n = "This issue is blocking %d other issues."
issues.dependency.blocked_by_count_1 = "This issue is blocked by %d issue."
issues.dependency.blocked_by_count_n = "This issue is blocked by %d issues."
issues.review.self.approval = You cannot approve your own pull request. issues.review.self.approval = You cannot approve your own pull request.
issues.review.self.rejection = You cannot request changes on your own pull request. issues.review.self.rejection = You cannot request changes on your own pull request.
issues.review.approve = "approved these changes %s" issues.review.approve = "approved these changes %s"

View File

@ -44,9 +44,11 @@ func isRoutePathExpensive(routePattern string) bool {
"/{username}/{reponame}/blame/", "/{username}/{reponame}/blame/",
"/{username}/{reponame}/commit/", "/{username}/{reponame}/commit/",
"/{username}/{reponame}/commits/", "/{username}/{reponame}/commits/",
"/{username}/{reponame}/compare/",
"/{username}/{reponame}/graph", "/{username}/{reponame}/graph",
"/{username}/{reponame}/media/", "/{username}/{reponame}/media/",
"/{username}/{reponame}/raw/", "/{username}/{reponame}/raw/",
"/{username}/{reponame}/rss/branch/",
"/{username}/{reponame}/src/", "/{username}/{reponame}/src/",
// issue & PR related (no trailing slash) // issue & PR related (no trailing slash)

View File

@ -107,8 +107,8 @@ func ForwardedHeadersHandler(limit int, trustedProxies []string) func(h http.Han
return proxy.ForwardedHeaders(opt) return proxy.ForwardedHeaders(opt)
} }
func Sessioner() func(next http.Handler) http.Handler { func Sessioner() (func(next http.Handler) http.Handler, error) {
return session.Sessioner(session.Options{ middleware, err := session.Sessioner(session.Options{
Provider: setting.SessionConfig.Provider, Provider: setting.SessionConfig.Provider,
ProviderConfig: setting.SessionConfig.ProviderConfig, ProviderConfig: setting.SessionConfig.ProviderConfig,
CookieName: setting.SessionConfig.CookieName, CookieName: setting.SessionConfig.CookieName,
@ -119,4 +119,9 @@ func Sessioner() func(next http.Handler) http.Handler {
SameSite: setting.SessionConfig.SameSite, SameSite: setting.SessionConfig.SameSite,
Domain: setting.SessionConfig.Domain, Domain: setting.SessionConfig.Domain,
}) })
if err != nil {
return nil, fmt.Errorf("failed to create session middleware: %w", err)
}
return middleware, nil
} }

View File

@ -8,6 +8,7 @@ import (
"html" "html"
"net/http" "net/http"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
@ -23,7 +24,11 @@ func Routes() *web.Router {
base.Methods("GET, HEAD", "/assets/*", public.FileHandlerFunc()) base.Methods("GET, HEAD", "/assets/*", public.FileHandlerFunc())
r := web.NewRouter() r := web.NewRouter()
r.Use(common.Sessioner(), Contexter()) if sessionMid, err := common.Sessioner(); err == nil && sessionMid != nil {
r.Use(sessionMid, Contexter())
} else {
log.Fatal("common.Sessioner failed: %v", err)
}
r.Get("/", Install) // it must be on the root, because the "install.js" use the window.location to replace the "localhost" AppURL r.Get("/", Install) // it must be on the root, because the "install.js" use the window.location to replace the "localhost" AppURL
r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall) r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall)
r.Get("/post-install", InstallDone) r.Get("/post-install", InstallDone)

View File

@ -654,6 +654,18 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
return return
} }
blockingCounts, err := issues.GetBlockingCount(ctx)
if err != nil {
ctx.ServerError("BlockingCounts", err)
return
}
blockedByCounts, err := issues.GetBlockedByCount(ctx)
if err != nil {
ctx.ServerError("BlockedByCounts", err)
return
}
if ctx.IsSigned { if ctx.IsSigned {
if err := issues.LoadIsRead(ctx, ctx.Doer.ID); err != nil { if err := issues.LoadIsRead(ctx, ctx.Doer.ID); err != nil {
ctx.ServerError("LoadIsRead", err) ctx.ServerError("LoadIsRead", err)
@ -718,6 +730,21 @@ func prepareIssueFilterAndList(ctx *context.Context, milestoneID, projectID int6
return 0 return 0
} }
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
counts, ok := blockingCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
counts, ok := blockedByCounts[issueID]
if !ok {
return 0
}
return counts
}
retrieveProjectsForIssueList(ctx, repo) retrieveProjectsForIssueList(ctx, repo)
if ctx.Written() { if ctx.Written() {
return return

View File

@ -627,6 +627,33 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
} }
return 0 return 0
} }
blockingCounts, err := issues.GetBlockingCount(ctx)
if err != nil {
ctx.ServerError("BlockingCounts", err)
return
}
blockedByCounts, err := issues.GetBlockedByCount(ctx)
if err != nil {
ctx.ServerError("BlockedByCounts", err)
return
}
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
counts, ok := blockingCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
counts, ok := blockedByCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["CommitLastStatus"] = lastStatus ctx.Data["CommitLastStatus"] = lastStatus
ctx.Data["CommitStatuses"] = commitStatuses ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["IssueStats"] = issueStats ctx.Data["IssueStats"] = issueStats

View File

@ -279,6 +279,32 @@ func NotificationSubscriptions(ctx *context.Context) {
return 0 return 0
} }
blockingCounts, err := issues.GetBlockingCount(ctx)
if err != nil {
ctx.ServerError("BlockingCounts", err)
return
}
blockedByCounts, err := issues.GetBlockedByCount(ctx)
if err != nil {
ctx.ServerError("BlockedByCounts", err)
return
}
ctx.Data["BlockingCounts"] = func(issueID int64) int64 {
counts, ok := blockingCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["BlockedByCounts"] = func(issueID int64) int64 {
counts, ok := blockedByCounts[issueID]
if !ok {
return 0
}
return counts
}
ctx.Data["Status"] = 1 ctx.Data["Status"] = 1
ctx.Data["Title"] = ctx.Tr("notification.subscriptions") ctx.Data["Title"] = ctx.Tr("notification.subscriptions")

View File

@ -267,7 +267,11 @@ func Routes() *web.Router {
routes.Get("/ssh_info", misc.SSHInfo) routes.Get("/ssh_info", misc.SSHInfo)
routes.Get("/api/healthz", healthcheck.Check) routes.Get("/api/healthz", healthcheck.Check)
mid = append(mid, common.Sessioner(), context.Contexter()) if sessionMid, err := common.Sessioner(); err == nil && sessionMid != nil {
mid = append(mid, sessionMid, context.Contexter())
} else {
log.Fatal("common.Sessioner failed: %v", err)
}
// Get user from session if logged in. // Get user from session if logged in.
mid = append(mid, webAuth(buildAuthGroup())) mid = append(mid, webAuth(buildAuthGroup()))

View File

@ -1,6 +1,10 @@
<div id="issue-list" class="flex-list"> <div id="issue-list" class="flex-list">
{{$approvalCounts := .ApprovalCounts}} {{$approvalCounts := .ApprovalCounts}}
{{$blockedByCounts := .BlockedByCounts}}
{{$blockingCounts := .BlockingCounts}}
{{range .Issues}} {{range .Issues}}
{{$blockedByCount := call $blockedByCounts .ID}}
{{$blockingCount := call $blockingCounts .ID}}
<div class="flex-item"> <div class="flex-item">
<div class="flex-item-leading"> <div class="flex-item-leading">
@ -22,6 +26,22 @@
{{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}} {{template "repo/commit_statuses" dict "Status" (index $.CommitLastStatus .PullRequest.ID) "Statuses" (index $.CommitStatuses .PullRequest.ID)}}
{{end}} {{end}}
{{end}} {{end}}
{{if gt $blockedByCount 0}}
<div class="ui label label-blocking">
<span data-tooltip-content="{{ctx.Locale.TrN $blockedByCount "repo.issues.dependency.blocked_by_count_1" "repo.issues.dependency.blocked_by_count_n" $blockedByCount}}" class="text red flex-text-block">
{{svg "octicon-blocked" 16}}
{{$blockedByCount}}
</span>
</div>
{{end}}
{{if and (gt $blockingCount 0) (not .IsClosed)}}
<div class="ui label label-blocking">
<span data-tooltip-content="{{ctx.Locale.TrN $blockingCount "repo.issues.dependency.blocking_count_1" "repo.issues.dependency.blocking_count_n" $blockingCount}}" class="text red flex-text-block">
{{svg "octicon-report" 16}}
{{$blockingCount}}
</span>
</div>
{{end}}
<span class="labels-list"> <span class="labels-list">
{{range .Labels}} {{range .Labels}}
<a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.RenderUtils.RenderLabel .}}</a> <a href="?q={{$.Keyword}}&type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}{{if ne $.listType "milestone"}}&milestone={{$.MilestoneID}}{{end}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.RenderUtils.RenderLabel .}}</a>

View File

@ -1,7 +1,7 @@
{ {
"include": [ "include": [
"${configDir}/.*",
"${configDir}/*", "${configDir}/*",
"${configDir}/.*",
"${configDir}/tests/e2e/**/*", "${configDir}/tests/e2e/**/*",
"${configDir}/tests/e2e/**/.*", "${configDir}/tests/e2e/**/.*",
"${configDir}/tools/**/*", "${configDir}/tools/**/*",
@ -17,27 +17,31 @@
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"allowJs": true, "allowJs": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"alwaysStrict": true, "alwaysStrict": true,
"erasableSyntaxOnly": true, "erasableSyntaxOnly": true,
"esModuleInterop": true, "esModuleInterop": true,
"exactOptionalPropertyTypes": false,
"isolatedModules": true, "isolatedModules": true,
"libReplacement": false, "libReplacement": false,
"noEmit": true, "noEmit": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noPropertyAccessFromIndexSignature": false,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"skipLibCheck": true, "skipLibCheck": true,
"verbatimModuleSyntax": true, "sourceMap": true,
"stripInternal": true,
"strict": false, "strict": false,
"strictBindCallApply": true, "strictBindCallApply": true,
"strictBuiltinIteratorReturn": true, "strictBuiltinIteratorReturn": true,
"strictFunctionTypes": true, "strictFunctionTypes": true,
"noImplicitAny": true, "stripInternal": true,
"noImplicitThis": true, "verbatimModuleSyntax": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noPropertyAccessFromIndexSignature": false,
"exactOptionalPropertyTypes": false,
"sourceMap": true,
"types": [ "types": [
"vitest/globals", "vitest/globals",
"./web_src/js/globals.d.ts", "./web_src/js/globals.d.ts",

View File

@ -56,3 +56,10 @@
top: 10px; top: 10px;
right: 5px; right: 5px;
} }
.label-blocking {
border: 1px solid var(--color-secondary) !important;
background: none transparent !important;
margin-left: 1px;
margin-right: 1px;
}

View File

@ -10,7 +10,7 @@ export function initTableSort() {
} }
function tableSort(normSort: string, revSort: string, isDefault: string) { function tableSort(normSort: string, revSort: string, isDefault: string) {
if (!normSort) return false; if (!normSort) return;
if (!revSort) revSort = ''; if (!revSort) revSort = '';
const url = new URL(window.location.href); const url = new URL(window.location.href);

View File

@ -43,7 +43,7 @@ type ToastOpts = {
type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast}; type ToastifyElement = HTMLElement & {_giteaToastifyInstance?: Toast};
/** See https://github.com/apvarun/toastify-js#api for options */ /** See https://github.com/apvarun/toastify-js#api for options */
function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast { function showToast(message: string, level: Intent, {gravity, position, duration, useHtmlBody, preventDuplicates = true, ...other}: ToastOpts = {}): Toast | null {
const body = useHtmlBody ? message : htmlEscape(message); const body = useHtmlBody ? message : htmlEscape(message);
const parent = document.querySelector('.ui.dimmer.active') ?? document.body; const parent = document.querySelector('.ui.dimmer.active') ?? document.body;
const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${body}` : preventDuplicates) : ''; const duplicateKey = preventDuplicates ? (preventDuplicates === true ? `${level}-${body}` : preventDuplicates) : '';
@ -56,7 +56,7 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
showElem(toastDupNumEl); showElem(toastDupNumEl);
toastDupNumEl.textContent = String(Number(toastDupNumEl.textContent) + 1); toastDupNumEl.textContent = String(Number(toastDupNumEl.textContent) + 1);
animateOnce(toastDupNumEl, 'pulse-1p5-200'); animateOnce(toastDupNumEl, 'pulse-1p5-200');
return; return null;
} }
} }
@ -83,15 +83,15 @@ function showToast(message: string, level: Intent, {gravity, position, duration,
return toast; return toast;
} }
export function showInfoToast(message: string, opts?: ToastOpts): Toast { export function showInfoToast(message: string, opts?: ToastOpts): Toast | null {
return showToast(message, 'info', opts); return showToast(message, 'info', opts);
} }
export function showWarningToast(message: string, opts?: ToastOpts): Toast { export function showWarningToast(message: string, opts?: ToastOpts): Toast | null {
return showToast(message, 'warning', opts); return showToast(message, 'warning', opts);
} }
export function showErrorToast(message: string, opts?: ToastOpts): Toast { export function showErrorToast(message: string, opts?: ToastOpts): Toast | null {
return showToast(message, 'error', opts); return showToast(message, 'error', opts);
} }