diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index f39c501cbc763..ef64c57246155 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -852,7 +852,7 @@ Default templates for project boards: - `SCHEDULE` accept formats - Full crontab specs, e.g. `* * * * * ?` - Descriptors, e.g. `@midnight`, `@every 1h30m` ... - - See more: [cron decument](https://pkg.go.dev/github.com/gogs/cron@v0.0.0-20171120032916-9f6c956d3e14) + - See more: [cron documentation](https://pkg.go.dev/github.com/gogs/cron@v0.0.0-20171120032916-9f6c956d3e14) ### Basic cron tasks - enabled by default diff --git a/docs/content/doc/packages/npm.en-us.md b/docs/content/doc/packages/npm.en-us.md index 122f306ee5e3a..decb3f743c419 100644 --- a/docs/content/doc/packages/npm.en-us.md +++ b/docs/content/doc/packages/npm.en-us.md @@ -127,6 +127,10 @@ npm dist-tag add test_package@1.0.2 release The tag name must not be a valid version. All tag names which are parsable as a version are rejected. +## Search packages + +The registry supports [searching](https://docs.npmjs.com/cli/v7/commands/npm-search/) but does not support special search qualifiers like `author:gitea`. + ## Supported commands ``` @@ -136,4 +140,5 @@ npm publish npm unpublish npm dist-tag npm view +npm search ``` diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 88ce55ecdbef8..2ed4d262481d1 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -96,6 +96,34 @@ type PackageDistribution struct { NpmSignature string `json:"npm-signature,omitempty"` } +type PackageSearch struct { + Objects []*PackageSearchObject `json:"objects"` + Total int64 `json:"total"` +} + +type PackageSearchObject struct { + Package *PackageSearchPackage `json:"package"` +} + +type PackageSearchPackage struct { + Scope string `json:"scope"` + Name string `json:"name"` + Version string `json:"version"` + Date time.Time `json:"date"` + Description string `json:"description"` + Author User `json:"author"` + Publisher User `json:"publisher"` + Maintainers []User `json:"maintainers"` + Keywords []string `json:"keywords,omitempty"` + Links *PackageSearchPackageLinks `json:"links"` +} + +type PackageSearchPackageLinks struct { + Registry string `json:"npm"` + Homepage string `json:"homepage,omitempty"` + Repository string `json:"repository,omitempty"` +} + // User https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#package type User struct { Username string `json:"username,omitempty"` diff --git a/modules/storage/local.go b/modules/storage/local.go index 701b0b1a9f3cc..5d5b06b648d7d 100644 --- a/modules/storage/local.go +++ b/modules/storage/local.go @@ -102,6 +102,10 @@ func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) if err := util.Rename(tmp.Name(), p); err != nil { return 0, err } + // Golang's tmp file (os.CreateTemp) always have 0o600 mode, so we need to change the file to follow the umask (as what Create/MkDir does) + if err := util.ApplyUmask(p, os.ModePerm); err != nil { + return 0, err + } tmpRemoved = true diff --git a/modules/translation/i18n/i18n.go b/modules/translation/i18n/i18n.go index 23b4e23c76446..d8ed43a1cd2f6 100644 --- a/modules/translation/i18n/i18n.go +++ b/modules/translation/i18n/i18n.go @@ -34,7 +34,7 @@ type LocaleStore interface { // HasLang returns whether a given language is present in the store HasLang(langName string) bool // AddLocaleByIni adds a new language to the store - AddLocaleByIni(langName, langDesc string, source interface{}) error + AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error } // ResetDefaultLocales resets the current default locales diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index 7940e59c940a3..0f4605c1bbb53 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_Tr(t *testing.T) { +func TestLocaleStore(t *testing.T) { testData1 := []byte(` .dot.name = Dot Name fmt = %[1]s %[2]s @@ -28,8 +28,8 @@ sub = Changed Sub String `) ls := NewLocaleStore() - assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1)) - assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2)) + assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, nil)) + assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2, nil)) ls.SetDefaultLang("lang1") result := ls.Tr("lang1", "fmt", "a", "b") @@ -58,3 +58,21 @@ sub = Changed Sub String assert.False(t, found) assert.NoError(t, ls.Close()) } + +func TestLocaleStoreMoreSource(t *testing.T) { + testData1 := []byte(` +a=11 +b=12 +`) + + testData2 := []byte(` +b=21 +c=22 +`) + + ls := NewLocaleStore() + assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, testData2)) + assert.Equal(t, "11", ls.Tr("lang1", "a")) + assert.Equal(t, "21", ls.Tr("lang1", "b")) + assert.Equal(t, "22", ls.Tr("lang1", "c")) +} diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index e3b88ad96eba6..853519e22416b 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -37,9 +37,7 @@ func NewLocaleStore() LocaleStore { } // AddLocaleByIni adds locale by ini into the store -// if source is a string, then the file is loaded -// if source is a []byte, then the content is used -func (store *localeStore) AddLocaleByIni(langName, langDesc string, source interface{}) error { +func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error { if _, ok := store.localeMap[langName]; ok { return ErrLocaleAlreadyExist } @@ -53,7 +51,7 @@ func (store *localeStore) AddLocaleByIni(langName, langDesc string, source inter iniFile, err := ini.LoadSources(ini.LoadOptions{ IgnoreInlineComment: true, UnescapeValueCommentSymbols: true, - }, source) + }, source, moreSource) if err != nil { return fmt.Errorf("unable to load ini: %w", err) } diff --git a/modules/translation/translation.go b/modules/translation/translation.go index e40a9357faefe..fc311aa61b4ca 100644 --- a/modules/translation/translation.go +++ b/modules/translation/translation.go @@ -60,9 +60,9 @@ func InitLocales(ctx context.Context) { log.Fatal("Failed to list locale files: %v", err) } - localFiles := make(map[string]interface{}, len(localeNames)) + localeData := make(map[string][]byte, len(localeNames)) for _, name := range localeNames { - localFiles[name], err = options.Locale(name) + localeData[name], err = options.Locale(name) if err != nil { log.Fatal("Failed to load %s locale file. %v", name, err) } @@ -75,9 +75,17 @@ func InitLocales(ctx context.Context) { matcher = language.NewMatcher(supportedTags) for i := range setting.Names { - key := "locale_" + setting.Langs[i] + ".ini" + var localeDataBase []byte + if i == 0 && setting.Langs[0] != "en-US" { + // Only en-US has complete translations. When use other language as default, the en-US should still be used as fallback. + localeDataBase = localeData["locale_en-US.ini"] + if localeDataBase == nil { + log.Fatal("Failed to load locale_en-US.ini file.") + } + } - if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil { + key := "locale_" + setting.Langs[i] + ".ini" + if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localeDataBase, localeData[key]); err != nil { log.Error("Failed to set messages to %s: %v", setting.Langs[i], err) } } diff --git a/modules/util/file_unix.go b/modules/util/file_unix.go new file mode 100644 index 0000000000000..ec9d4ec167a9a --- /dev/null +++ b/modules/util/file_unix.go @@ -0,0 +1,28 @@ +// Copyright 2022 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. + +//go:build !windows + +package util + +import ( + "os" + + "golang.org/x/sys/unix" +) + +var defaultUmask int + +func init() { + // at the moment, the umask could only be gotten by calling unix.Umask(newUmask) + // use 0o077 as temp new umask to reduce the risks if this umask is used anywhere else before the correct umask is recovered + tempUmask := 0o077 + defaultUmask = unix.Umask(tempUmask) + unix.Umask(defaultUmask) +} + +func ApplyUmask(f string, newMode os.FileMode) error { + mod := newMode & ^os.FileMode(defaultUmask) + return os.Chmod(f, mod) +} diff --git a/modules/util/file_unix_test.go b/modules/util/file_unix_test.go new file mode 100644 index 0000000000000..41311aa13f3c9 --- /dev/null +++ b/modules/util/file_unix_test.go @@ -0,0 +1,36 @@ +// Copyright 2022 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. + +//go:build !windows + +package util + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestApplyUmask(t *testing.T) { + f, err := os.CreateTemp(t.TempDir(), "test-filemode-") + assert.NoError(t, err) + + err = os.Chmod(f.Name(), 0o777) + assert.NoError(t, err) + st, err := os.Stat(f.Name()) + assert.NoError(t, err) + assert.EqualValues(t, 0o777, st.Mode().Perm()&0o777) + + oldDefaultUmask := defaultUmask + defaultUmask = 0o037 + defer func() { + defaultUmask = oldDefaultUmask + }() + err = ApplyUmask(f.Name(), os.ModePerm) + assert.NoError(t, err) + st, err = os.Stat(f.Name()) + assert.NoError(t, err) + assert.EqualValues(t, 0o740, st.Mode().Perm()&0o777) +} diff --git a/modules/util/file_windows.go b/modules/util/file_windows.go new file mode 100644 index 0000000000000..6ad3e88ba5eee --- /dev/null +++ b/modules/util/file_windows.go @@ -0,0 +1,16 @@ +// Copyright 2022 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. + +//go:build windows + +package util + +import ( + "os" +) + +func ApplyUmask(f string, newMode os.FileMode) error { + // do nothing for Windows, because Windows doesn't use umask + return nil +} diff --git a/options/license/checkmk b/options/license/checkmk new file mode 100644 index 0000000000000..46c6b74278c29 --- /dev/null +++ b/options/license/checkmk @@ -0,0 +1,9 @@ +# Copyright (c) 2006, 2010 Micah Cowan +# +# Redistribution of this program in any form, with or without +# modifications, is permitted, provided that the above copyright is +# retained in distributions of this program in source form. +# +# (This is a free, non-copyleft license compatible with pretty much any +# other free or proprietary license, including the GPL. It's essentially +# a scaled-down version of the "modified" BSD license.) diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 42dac7cca97c5..aa10a56c14257 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -3092,6 +3092,7 @@ container.details.platform=プラットフォーム container.details.repository_site=リポジトリサイト container.details.documentation_site=ドキュメントサイト container.pull=コマンドラインでイメージを取得します: +container.digest=ダイジェスト: container.documentation=Container レジストリの詳細については、 ドキュメント を参照してください。 container.multi_arch=OS / アーキテクチャ container.layers=イメージレイヤー diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index d291e7f1b67cf..b8117f65c9907 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -277,6 +277,7 @@ org_no_results=Nenhuma organização correspondente foi encontrada. code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado. code_search_results=Resultados da pesquisa por: '%s' code_last_indexed_at=Última indexação %s +relevant_repositories_tooltip=Repositórios que são forks ou que não possuem tópico, nem ícone e nem descrição estão ocultos. relevant_repositories=Apenas repositórios relevantes estão sendo mostrados, mostrar resultados não filtrados. @@ -1038,6 +1039,12 @@ file_view_raw=Ver original file_permalink=Link permanente file_too_large=O arquivo é muito grande para ser mostrado. invisible_runes_header=`Este arquivo contém caracteres Unicode invisíveis!` +invisible_runes_description=`Este arquivo contém caracteres Unicode invisíveis que podem ser processados de forma diferente do que aparece abaixo. Se seu caso de uso for intencional e legítimo, você pode ignorar com segurança esse aviso. Use o botão Escapar para revelar caracteres ocultos.` +ambiguous_runes_header=`Esse arquivo contém caracteres Unicode ambíguos!` +ambiguous_runes_description=`Este arquivo contém caracteres ambíguos Unicode que podem ser confundidos com outros no seu idioma atual. Se o seu caso de uso for intencional e legítimo, você pode ignorar com segurança este aviso. Use o botão Escapar para destacar esses caracteres.` +invisible_runes_line=`Esta linha tem caracteres unicode invisíveis` +ambiguous_runes_line=`Esta linha tem caracteres unicode ambíguos` +ambiguous_character=`%[1]c [U+%04[1]X] é confundível com o %[2]c [U+%04[2]X]` escape_control_characters=Escapar unescape_control_characters=Desescapar @@ -1225,6 +1232,7 @@ issues.choose.get_started=Primeiros passos issues.choose.blank=Padrão issues.choose.blank_about=Criar uma issue a partir do modelo padrão. issues.choose.ignore_invalid_templates=Modelos inválidos foram ignorados +issues.choose.invalid_templates=%v modelo(s) inválido(s) encontrado(s) issues.no_ref=Nenhum branch/tag especificado issues.create=Criar issue issues.new_label=Nova etiqueta @@ -1533,6 +1541,7 @@ pulls.remove_prefix=Remover o prefixo %s pulls.data_broken=Este pull request está quebrado devido a falta de informação do fork. pulls.files_conflicted=Este pull request tem alterações conflitantes com o branch de destino. pulls.is_checking=Verificação de conflitos do merge está em andamento. Tente novamente em alguns momentos. +pulls.is_ancestor=Este branch já está incluído no branch de destino. Não há nada para mesclar. pulls.is_empty=As alterações neste branch já estão na branch de destino. Este será um commit vazio. pulls.required_status_check_failed=Algumas verificações necessárias não foram bem sucedidas. pulls.required_status_check_missing=Estão faltando algumas verificações necessárias. @@ -1603,11 +1612,13 @@ pulls.merge_instruction_step2_desc=Faça merge das alterações e atualize no Gi pulls.auto_merge_button_when_succeed=(Quando a verificação for bem-sucedida) pulls.auto_merge_when_succeed=Mesclar automaticamente quando todas as verificações forem bem sucedidas pulls.auto_merge_newly_scheduled=O merge do pull request foi agendado para quando todas as verificações forem bem-sucedidas. +pulls.auto_merge_has_pending_schedule=%[1]s agendou este pull request para merge automático quando todas as verificações tiverem sucesso %[2]s. pulls.auto_merge_cancel_schedule=Cancelar merge automático pulls.auto_merge_not_scheduled=Este pull request não está programado para ser automaticamente mesclado. pulls.auto_merge_canceled_schedule=O merge automático foi cancelado para este pull request. +pulls.auto_merge_newly_scheduled_comment=`agendou este pull request para merge automático quando todas as verificações tiverem sucesso %[1]s` pulls.auto_merge_canceled_schedule_comment=`cancelou o merge automático deste pull request quando todos as verificações tiverem sucesso %[1]s` pulls.delete.title=Excluir este pull request? @@ -1805,6 +1816,7 @@ settings.tracker_issue_style.numeric=Numérico settings.tracker_issue_style.alphanumeric=Alfanumérico settings.tracker_issue_style.regexp=Expressão Regular settings.tracker_issue_style.regexp_pattern=Padrão de expressão regular +settings.tracker_issue_style.regexp_pattern_desc=O primeiro grupo capturado será usado no lugar de {index}. settings.tracker_url_format_desc=Use os espaços reservados {user}, {repo} e {index} para o nome de usuário, nome do repositório e o índice de problemas. settings.enable_timetracker=Habilitar Cronômetro settings.allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo @@ -1944,6 +1956,8 @@ settings.event_delete=Excluir settings.event_delete_desc=Branch ou tag deletado. settings.event_fork=Fork settings.event_fork_desc=Feito fork do repositório. +settings.event_wiki=Wiki +settings.event_wiki_desc=Página da wiki criada, renomeada, editada ou excluída. settings.event_release=Versão settings.event_release_desc=Versão publicada, atualizada ou excluída em um repositório. settings.event_push=Push @@ -2423,6 +2437,7 @@ dashboard.new_version_hint=Gitea %s está disponível, você está executando %s dashboard.statistic=Resumo dashboard.operations=Operações de manutenção dashboard.system_status=Status do sistema +dashboard.statistic_info=O banco de dados do Gitea contém %d usuários, %d organizações, %d chaves públicas, %d repositórios, %d observadores, %d favoritos, ~%d ações, %d acessos, %d issues, %d comentários, %d contas sociais, %d seguidores, %d espelhos, %d versões, %d fontes de autenticação, %d webhooks, %d marcos, %d etiquetas, %d tarefas hook, %d equipes, %d tarefas de atualização, %d anexos. dashboard.operation_name=Nome da operação dashboard.operation_switch=Trocar dashboard.operation_run=Executar @@ -2631,6 +2646,8 @@ auths.use_paged_search=Use a pesquisa paginada auths.search_page_size=Tamanho da página auths.filter=Filtro de usuário auths.admin_filter=Filtro de administrador +auths.restricted_filter=Filtro de restrição +auths.restricted_filter_helper=Deixe em branco para não definir nenhum usuário como restrito. Use um asterisco ('*') para definir todos os usuários que não correspondem ao Filtro de administrador como restritos. auths.group_attribute_list_users=Atributo do Grupo que Contém a Lista de Usuários auths.enable_ldap_groups=Habilitar grupos do LDAP auths.ms_ad_sa=Atributos de pesquisa do MS AD @@ -2871,6 +2888,7 @@ monitor.queue.nopool.title=Nenhum conjunto de executores monitor.queue.nopool.desc=Essa fila agrupa outras filas e não possui um conjunto de executores. monitor.queue.wrapped.desc=Uma fila agrupada envolve uma fila inicial lenta, armazenando as solicitações da fila em um canal. Ela não possui um conjunto de executores em si. monitor.queue.persistable-channel.desc=Um canal persistente envolve duas filas, uma fila de canais que tem seu próprio conjunto de executores e uma fila de nível para solicitações persistentes de encerramentos anteriores. Ela não tem um conjunto de executores em si. +monitor.queue.flush=Liberar executor monitor.queue.pool.timeout=Tempo de espera monitor.queue.pool.addworkers.title=Adicionar executores monitor.queue.pool.addworkers.submit=Adicionar executores @@ -3023,6 +3041,7 @@ title=Pacotes desc=Gerenciar pacotes do repositório. empty=Não há pacotes ainda. empty.documentation=Para obter mais informações sobre o registro de pacote, consulte a documentação. +empty.repo=Você enviou um pacote, mas ele não está aqui? Vá para configurações do pacote e vincule-o a este repositório. filter.type=Tipo filter.type.all=Todos filter.no_result=Seu filtro não produziu resultados. @@ -3059,6 +3078,7 @@ container.details.platform=Plataforma container.details.repository_site=Site do Repositório container.details.documentation_site=Site da Documentação container.pull=Puxe a imagem pela linha de comando: +container.digest=Digest: container.documentation=Para obter mais informações sobre o registro de Container, consulte a documentação. container.multi_arch=S.O. / Arquitetura container.layers=Camadas da Imagem @@ -3088,6 +3108,8 @@ npm.dependencies.development=Dependências de Desenvolvimento npm.dependencies.peer=Dependências Peer npm.dependencies.optional=Dependências Opcionais npm.details.tag=Tag +pub.install=Para instalar o pacote usando Dart, execute o seguinte comando: +pub.documentation=Para obter mais informações sobre o registro Pub, consulte a documentação. pub.details.repository_site=Site do Repositório pub.details.documentation_site=Site da Documentação pypi.requires=Requer Python @@ -3100,6 +3122,8 @@ rubygems.dependencies.development=Dependências de Desenvolvimento rubygems.required.ruby=Requer o Ruby versão rubygems.required.rubygems=Requer o RubyGem versão rubygems.documentation=Para obter mais informações sobre o registro do RubyGems, consulte a documentação. +vagrant.install=Para adicionar uma Vagrant box, execute o seguinte comando: +vagrant.documentation=Para obter mais informações sobre o registro do Vagrant, consulte a documentação. settings.link=Vincular este pacote a um repositório settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório. settings.link.select=Selecionar Repositório diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index c19899529af6f..dfaefb7d629fc 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -3092,6 +3092,7 @@ container.details.platform=Platform container.details.repository_site=Depo Sitesi container.details.documentation_site=Belge Sitesi container.pull=Görüntüyü komut satırını kullanarak çekin: +container.digest=Özet: container.documentation=Taşıyıcı kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz. container.multi_arch=İşletim Sistemi / Mimari container.layers=Görüntü Katmanları diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 93f9afca7b283..3354fe12d43ca 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -69,7 +69,7 @@ func Routes(ctx gocontext.Context) *web.Route { r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata) r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile) r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/conan", func() { r.Group("/v1", func() { r.Get("/ping", conan.Ping) @@ -157,7 +157,7 @@ func Routes(ctx gocontext.Context) *web.Route { }, conan.ExtractPathParameters) }) }) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/generic", func() { r.Group("/{packagename}/{packageversion}", func() { r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage) @@ -169,33 +169,35 @@ func Routes(ctx gocontext.Context) *web.Route { }, reqPackageAccess(perm.AccessModeWrite)) }) }) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/helm", func() { r.Get("/index.yaml", helm.Index) r.Get("/{filename}", helm.DownloadPackageFile) r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/maven", func() { r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile) r.Get("/*", maven.DownloadPackageFile) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/nuget", func() { - r.Get("/index.json", nuget.ServiceIndex) - r.Get("/query", nuget.SearchService) - r.Group("/registration/{id}", func() { - r.Get("/index.json", nuget.RegistrationIndex) - r.Get("/{version}", nuget.RegistrationLeaf) - }) - r.Group("/package/{id}", func() { - r.Get("/index.json", nuget.EnumeratePackageVersions) - r.Get("/{version}/{filename}", nuget.DownloadPackageFile) - }) + r.Get("/index.json", nuget.ServiceIndex) // Needs to be unauthenticated for the NuGet client. r.Group("", func() { - r.Put("/", nuget.UploadPackage) - r.Put("/symbolpackage", nuget.UploadSymbolPackage) - r.Delete("/{id}/{version}", nuget.DeletePackage) - }, reqPackageAccess(perm.AccessModeWrite)) - r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile) + r.Get("/query", nuget.SearchService) + r.Group("/registration/{id}", func() { + r.Get("/index.json", nuget.RegistrationIndex) + r.Get("/{version}", nuget.RegistrationLeaf) + }) + r.Group("/package/{id}", func() { + r.Get("/index.json", nuget.EnumeratePackageVersions) + r.Get("/{version}/{filename}", nuget.DownloadPackageFile) + }) + r.Group("", func() { + r.Put("/", nuget.UploadPackage) + r.Put("/symbolpackage", nuget.UploadSymbolPackage) + r.Delete("/{id}/{version}", nuget.DeletePackage) + }, reqPackageAccess(perm.AccessModeWrite)) + r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile) + }, reqPackageAccess(perm.AccessModeRead)) }) r.Group("/npm", func() { r.Group("/@{scope}/{id}", func() { @@ -236,7 +238,10 @@ func Routes(ctx gocontext.Context) *web.Route { r.Delete("", npm.DeletePackageTag) }, reqPackageAccess(perm.AccessModeWrite)) }) - }) + r.Group("/-/v1/search", func() { + r.Get("", npm.PackageSearch) + }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/pub", func() { r.Group("/api/packages", func() { r.Group("/versions/new", func() { @@ -250,12 +255,12 @@ func Routes(ctx gocontext.Context) *web.Route { r.Get("/{version}", pub.PackageVersionMetadata) }) }) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/pypi", func() { r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile) r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile) r.Get("/simple/{id}", pypi.PackageMetadata) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/rubygems", func() { r.Get("/specs.4.8.gz", rubygems.EnumeratePackages) r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest) @@ -266,7 +271,7 @@ func Routes(ctx gocontext.Context) *web.Route { r.Post("/", rubygems.UploadPackageFile) r.Delete("/yank", rubygems.DeletePackage) }, reqPackageAccess(perm.AccessModeWrite)) - }) + }, reqPackageAccess(perm.AccessModeRead)) r.Group("/vagrant", func() { r.Group("/authenticate", func() { r.Get("", vagrant.CheckAuthenticate) @@ -279,8 +284,8 @@ func Routes(ctx gocontext.Context) *web.Route { r.Put("", reqPackageAccess(perm.AccessModeWrite), vagrant.UploadPackageFile) }) }) - }) - }, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) + }, reqPackageAccess(perm.AccessModeRead)) + }, context_service.UserAssignmentWeb(), context.PackageAssignment()) return r } diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go index 763c595152ec1..d1027763a836a 100644 --- a/routers/api/packages/npm/api.go +++ b/routers/api/packages/npm/api.go @@ -74,3 +74,38 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package }, } } + +func createPackageSearchResponse(pds []*packages_model.PackageDescriptor, total int64) *npm_module.PackageSearch { + objects := make([]*npm_module.PackageSearchObject, 0, len(pds)) + for _, pd := range pds { + metadata := pd.Metadata.(*npm_module.Metadata) + + scope := metadata.Scope + if scope == "" { + scope = "unscoped" + } + + objects = append(objects, &npm_module.PackageSearchObject{ + Package: &npm_module.PackageSearchPackage{ + Scope: scope, + Name: metadata.Name, + Version: pd.Version.Version, + Date: pd.Version.CreatedUnix.AsLocalTime(), + Description: metadata.Description, + Author: npm_module.User{Name: metadata.Author}, + Publisher: npm_module.User{Name: pd.Owner.Name}, + Maintainers: []npm_module.User{}, // npm cli needs this field + Keywords: metadata.Keywords, + Links: &npm_module.PackageSearchPackageLinks{ + Registry: pd.FullWebLink(), + Homepage: metadata.ProjectURL, + }, + }, + }) + } + + return &npm_module.PackageSearch{ + Objects: objects, + Total: total, + } +} diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 66b999d47ee50..2989ce6e7f6bd 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -350,3 +350,35 @@ func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly boo return committer.Commit() } + +func PackageSearch(ctx *context.Context) { + pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{ + OwnerID: ctx.Package.Owner.ID, + Type: packages_model.TypeNpm, + Name: packages_model.SearchValue{ + ExactMatch: false, + Value: ctx.FormTrim("text"), + }, + Paginator: db.NewAbsoluteListOptions( + ctx.FormInt("from"), + ctx.FormInt("size"), + ), + }) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + pds, err := packages_model.GetPackageDescriptors(ctx, pvs) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + resp := createPackageSearchResponse( + pds, + total, + ) + + ctx.JSON(http.StatusOK, resp) +} diff --git a/templates/repo/diff/whitespace_dropdown.tmpl b/templates/repo/diff/whitespace_dropdown.tmpl index 9df79dab739c9..47a5ab5533c34 100644 --- a/templates/repo/diff/whitespace_dropdown.tmpl +++ b/templates/repo/diff/whitespace_dropdown.tmpl @@ -3,20 +3,28 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}} diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 8e8c256c59199..5b25d664e6a0a 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -45,6 +45,10 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}