Skip to content

Commit 923e703

Browse files
authored
Merge branch 'main' into avoid-deprecation-notice
2 parents 18cdc0f + 3349fd8 commit 923e703

File tree

24 files changed

+477
-23
lines changed

24 files changed

+477
-23
lines changed

docs/content/doc/features/webhooks.en-us.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ All event pushes are POST requests. The methods currently supported are:
2828
- Microsoft Teams
2929
- Feishu
3030
- Wechatwork
31+
- Packagist
3132

3233
### Event information
3334

docs/content/doc/features/webhooks.zh-cn.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ Gitea 的存储 webhook。这可以有存储库管路设定页 `/:username/:repo
2727
- Microsoft Teams
2828
- Feishu
2929
- Wechatwork
30+
- Packagist
3031

3132
## TBD

docs/content/doc/features/webhooks.zh-tw.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Gitea 的儲存庫事件支援 web hook。這可以有儲存庫管理員在設
2727
- Microsoft Teams
2828
- Feishu
2929
- Wechatwork
30+
- Packagist
3031

3132
### 事件資訊
3233

models/webhook/webhook.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ const (
161161
FEISHU HookType = "feishu"
162162
MATRIX HookType = "matrix"
163163
WECHATWORK HookType = "wechatwork"
164+
PACKAGIST HookType = "packagist"
164165
)
165166

166167
// HookStatus is the status of a web hook

modules/public/mime_types.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package public
6+
7+
import "strings"
8+
9+
// wellKnownMimeTypesLower comes from Golang's builtin mime package: `builtinTypesLower`, see the comment of detectWellKnownMimeType
10+
var wellKnownMimeTypesLower = map[string]string{
11+
".avif": "image/avif",
12+
".css": "text/css; charset=utf-8",
13+
".gif": "image/gif",
14+
".htm": "text/html; charset=utf-8",
15+
".html": "text/html; charset=utf-8",
16+
".jpeg": "image/jpeg",
17+
".jpg": "image/jpeg",
18+
".js": "text/javascript; charset=utf-8",
19+
".json": "application/json",
20+
".mjs": "text/javascript; charset=utf-8",
21+
".pdf": "application/pdf",
22+
".png": "image/png",
23+
".svg": "image/svg+xml",
24+
".wasm": "application/wasm",
25+
".webp": "image/webp",
26+
".xml": "text/xml; charset=utf-8",
27+
28+
// well, there are some types missing from the builtin list
29+
".txt": "text/plain; charset=utf-8",
30+
}
31+
32+
// detectWellKnownMimeType will return the mime-type for a well-known file ext name
33+
// The purpose of this function is to bypass the unstable behavior of Golang's mime.TypeByExtension
34+
// mime.TypeByExtension would use OS's mime-type config to overwrite the well-known types (see its document).
35+
// If the user's OS has incorrect mime-type config, it would make Gitea can not respond a correct Content-Type to browsers.
36+
// For example, if Gitea returns `text/plain` for a `.js` file, the browser couldn't run the JS due to security reasons.
37+
// detectWellKnownMimeType makes the Content-Type for well-known files stable.
38+
func detectWellKnownMimeType(ext string) string {
39+
ext = strings.ToLower(ext)
40+
return wellKnownMimeTypesLower[ext]
41+
}

modules/public/public.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ func parseAcceptEncoding(val string) map[string]bool {
9292
return types
9393
}
9494

95+
// setWellKnownContentType will set the Content-Type if the file is a well-known type.
96+
// See the comments of detectWellKnownMimeType
97+
func setWellKnownContentType(w http.ResponseWriter, file string) {
98+
mimeType := detectWellKnownMimeType(filepath.Ext(file))
99+
if mimeType != "" {
100+
w.Header().Set("Content-Type", mimeType)
101+
}
102+
}
103+
95104
func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool {
96105
// use clean to keep the file is a valid path with no . or ..
97106
f, err := fs.Open(path.Clean(file))
@@ -122,6 +131,8 @@ func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.Fi
122131
return true
123132
}
124133

134+
setWellKnownContentType(w, file)
135+
125136
serveContent(w, req, fi, fi.ModTime(), f)
126137
return true
127138
}
File renamed without changes.

modules/public/static.go renamed to modules/public/serve_static.go

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@ package public
99

1010
import (
1111
"bytes"
12-
"compress/gzip"
1312
"io"
14-
"mime"
1513
"net/http"
1614
"os"
1715
"path/filepath"
1816
"time"
1917

20-
"code.gitea.io/gitea/modules/log"
2118
"code.gitea.io/gitea/modules/timeutil"
2219
)
2320

@@ -66,24 +63,16 @@ func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modt
6663
encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding"))
6764
if encodings["gzip"] {
6865
if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok {
69-
rd := bytes.NewReader(cf.GzipBytes())
70-
w.Header().Set("Content-Encoding", "gzip")
71-
ctype := mime.TypeByExtension(filepath.Ext(fi.Name()))
72-
if ctype == "" {
73-
// read a chunk to decide between utf-8 text and binary
74-
var buf [512]byte
75-
grd, _ := gzip.NewReader(rd)
76-
n, _ := io.ReadFull(grd, buf[:])
77-
ctype = http.DetectContentType(buf[:n])
78-
_, err := rd.Seek(0, io.SeekStart) // rewind to output whole file
79-
if err != nil {
80-
log.Error("rd.Seek error: %v", err)
81-
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
82-
return
83-
}
66+
rdGzip := bytes.NewReader(cf.GzipBytes())
67+
// all static files are managed by Gitea, so we can make sure every file has the correct ext name
68+
// then we can get the correct Content-Type, we do not need to do http.DetectContentType on the decompressed data
69+
mimeType := detectWellKnownMimeType(filepath.Ext(fi.Name()))
70+
if mimeType == "" {
71+
mimeType = "application/octet-stream"
8472
}
85-
w.Header().Set("Content-Type", ctype)
86-
http.ServeContent(w, req, fi.Name(), modtime, rd)
73+
w.Header().Set("Content-Type", mimeType)
74+
w.Header().Set("Content-Encoding", "gzip")
75+
http.ServeContent(w, req, fi.Name(), modtime, rdGzip)
8776
return
8877
}
8978
}

modules/setting/webhook.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func newWebhookService() {
3636
Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
3737
Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
3838
Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString("")
39-
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork"}
39+
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist"}
4040
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
4141
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
4242
if Webhook.ProxyURL != "" {

modules/structs/hook.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ type CreateHookOptionConfig map[string]string
4040
// CreateHookOption options when create a hook
4141
type CreateHookOption struct {
4242
// required: true
43-
// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork
43+
// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork,packagist
4444
Type string `json:"type" binding:"Required"`
4545
// required: true
4646
Config CreateHookOptionConfig `json:"config" binding:"Required"`

options/locale/locale_en-US.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1947,6 +1947,10 @@ settings.add_matrix_hook_desc = Integrate <a href="%s">Matrix</a> into your repo
19471947
settings.add_msteams_hook_desc = Integrate <a href="%s">Microsoft Teams</a> into your repository.
19481948
settings.add_feishu_hook_desc = Integrate <a href="%s">Feishu</a> into your repository.
19491949
settings.add_Wechat_hook_desc = Integrate <a href="%s">Wechatwork</a> into your repository.
1950+
settings.add_packagist_hook_desc = Integrate <a href="%s">Packagist</a> into your repository.
1951+
settings.packagist_username = Packagist username
1952+
settings.packagist_api_token = API token
1953+
settings.packagist_package_url = Packagist package URL
19501954
settings.deploy_keys = Deploy Keys
19511955
settings.add_deploy_key = Add Deploy Key
19521956
settings.deploy_key_desc = Deploy keys have read-only pull access to the repository.

public/img/packagist.png

4.47 KB
Loading

routers/web/repo/webhook.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,59 @@ func WechatworkHooksNewPost(ctx *context.Context) {
682682
ctx.Redirect(orCtx.Link)
683683
}
684684

685+
// PackagistHooksNewPost response for creating packagist hook
686+
func PackagistHooksNewPost(ctx *context.Context) {
687+
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
688+
ctx.Data["Title"] = ctx.Tr("repo.settings")
689+
ctx.Data["PageIsSettingsHooks"] = true
690+
ctx.Data["PageIsSettingsHooksNew"] = true
691+
ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
692+
ctx.Data["HookType"] = webhook.PACKAGIST
693+
694+
orCtx, err := getOrgRepoCtx(ctx)
695+
if err != nil {
696+
ctx.ServerError("getOrgRepoCtx", err)
697+
return
698+
}
699+
700+
if ctx.HasError() {
701+
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
702+
return
703+
}
704+
705+
meta, err := json.Marshal(&webhook_service.PackagistMeta{
706+
Username: form.Username,
707+
APIToken: form.APIToken,
708+
PackageURL: form.PackageURL,
709+
})
710+
if err != nil {
711+
ctx.ServerError("Marshal", err)
712+
return
713+
}
714+
715+
w := &webhook.Webhook{
716+
RepoID: orCtx.RepoID,
717+
URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
718+
ContentType: webhook.ContentTypeJSON,
719+
HookEvent: ParseHookEvent(form.WebhookForm),
720+
IsActive: form.Active,
721+
Type: webhook.PACKAGIST,
722+
Meta: string(meta),
723+
OrgID: orCtx.OrgID,
724+
IsSystemWebhook: orCtx.IsSystemWebhook,
725+
}
726+
if err := w.UpdateEvent(); err != nil {
727+
ctx.ServerError("UpdateEvent", err)
728+
return
729+
} else if err := webhook.CreateWebhook(db.DefaultContext, w); err != nil {
730+
ctx.ServerError("CreateWebhook", err)
731+
return
732+
}
733+
734+
ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
735+
ctx.Redirect(orCtx.Link)
736+
}
737+
685738
func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
686739
ctx.Data["RequireHighlightJS"] = true
687740

@@ -719,6 +772,8 @@ func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
719772
ctx.Data["TelegramHook"] = webhook_service.GetTelegramHook(w)
720773
case webhook.MATRIX:
721774
ctx.Data["MatrixHook"] = webhook_service.GetMatrixHook(w)
775+
case webhook.PACKAGIST:
776+
ctx.Data["PackagistHook"] = webhook_service.GetPackagistHook(w)
722777
}
723778

724779
ctx.Data["History"], err = w.History(1)
@@ -1137,6 +1192,50 @@ func WechatworkHooksEditPost(ctx *context.Context) {
11371192
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
11381193
}
11391194

1195+
// PackagistHooksEditPost response for editing packagist hook
1196+
func PackagistHooksEditPost(ctx *context.Context) {
1197+
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
1198+
ctx.Data["Title"] = ctx.Tr("repo.settings")
1199+
ctx.Data["PageIsSettingsHooks"] = true
1200+
ctx.Data["PageIsSettingsHooksEdit"] = true
1201+
1202+
orCtx, w := checkWebhook(ctx)
1203+
if ctx.Written() {
1204+
return
1205+
}
1206+
ctx.Data["Webhook"] = w
1207+
1208+
if ctx.HasError() {
1209+
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
1210+
return
1211+
}
1212+
1213+
meta, err := json.Marshal(&webhook_service.PackagistMeta{
1214+
Username: form.Username,
1215+
APIToken: form.APIToken,
1216+
PackageURL: form.PackageURL,
1217+
})
1218+
if err != nil {
1219+
ctx.ServerError("Marshal", err)
1220+
return
1221+
}
1222+
1223+
w.Meta = string(meta)
1224+
w.URL = fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken))
1225+
w.HookEvent = ParseHookEvent(form.WebhookForm)
1226+
w.IsActive = form.Active
1227+
if err := w.UpdateEvent(); err != nil {
1228+
ctx.ServerError("UpdateEvent", err)
1229+
return
1230+
} else if err := webhook.UpdateWebhook(w); err != nil {
1231+
ctx.ServerError("UpdateWebhook", err)
1232+
return
1233+
}
1234+
1235+
ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
1236+
ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
1237+
}
1238+
11401239
// TestWebhook test if web hook is work fine
11411240
func TestWebhook(ctx *context.Context) {
11421241
hookID := ctx.ParamsInt64(":id")

routers/web/web.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ func RegisterRoutes(m *web.Route) {
448448
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
449449
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
450450
m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
451+
m.Post("/packagist/{id}", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
451452
}, webhooksEnabled)
452453

453454
m.Group("/{configType:default-hooks|system-hooks}", func() {
@@ -462,6 +463,7 @@ func RegisterRoutes(m *web.Route) {
462463
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
463464
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
464465
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
466+
m.Post("/packagist/new", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
465467
})
466468

467469
m.Group("/auths", func() {
@@ -657,6 +659,7 @@ func RegisterRoutes(m *web.Route) {
657659
m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost)
658660
m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost)
659661
m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost)
662+
m.Post("/packagist/new", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost)
660663
m.Group("/{id}", func() {
661664
m.Get("", repo.WebHooksEdit)
662665
m.Post("/test", repo.TestWebhook)
@@ -672,6 +675,7 @@ func RegisterRoutes(m *web.Route) {
672675
m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
673676
m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
674677
m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost)
678+
m.Post("/packagist/{id}", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost)
675679
}, webhooksEnabled)
676680

677681
m.Group("/keys", func() {

services/forms/repo_form.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,20 @@ func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors)
396396
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
397397
}
398398

399+
// NewPackagistHookForm form for creating packagist hook
400+
type NewPackagistHookForm struct {
401+
Username string `binding:"Required"`
402+
APIToken string `binding:"Required"`
403+
PackageURL string `binding:"Required;ValidUrl"`
404+
WebhookForm
405+
}
406+
407+
// Validate validates the fields
408+
func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
409+
ctx := context.GetContext(req)
410+
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
411+
}
412+
399413
// .___
400414
// | | ______ ________ __ ____
401415
// | |/ ___// ___/ | \_/ __ \

0 commit comments

Comments
 (0)