loading up the forgejo repo on tangled to test page performance

Merge pull request '[gitea] week 2024-44 cherry pick (gitea/main -> forgejo)' (#5714) from algernon/wcp/2024-44 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5714
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>

Changed files
+88 -55
cmd
custom
models
issues
modules
setting
release-notes
routers
api
v1
web
explore
user
setting
security
services
user
templates
explore
web_src
js
features
+1 -1
cmd/admin_auth_ldap.go
··· 386 386 return a.createAuthSource(ctx, authSource) 387 387 } 388 388 389 - // updateLdapBindDn updates a new LDAP (simple auth) authentication source. 389 + // updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source. 390 390 func (a *authService) updateLdapSimpleAuth(c *cli.Context) error { 391 391 ctx, cancel := installSignals() 392 392 defer cancel()
+18
custom/conf/app.example.ini
··· 925 925 ;; Valid site url schemes for user profiles 926 926 ;VALID_SITE_URL_SCHEMES=http,https 927 927 928 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 929 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 930 + ;[service.explore] 931 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 932 + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 933 + ;; 934 + ;; Only allow signed in users to view the explore pages. 935 + ;REQUIRE_SIGNIN_VIEW = false 936 + ;; 937 + ;; Disable the users explore page. 938 + ;DISABLE_USERS_PAGE = false 939 + ;; 940 + ;; Disable the organizations explore page. 941 + ;DISABLE_ORGANIZATIONS_PAGE = false 942 + ;; 943 + ;; Disable the code explore page. 944 + ;DISABLE_CODE_PAGE = false 945 + ;; 928 946 929 947 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 930 948 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+1 -1
models/issues/issue.go
··· 875 875 return issues, nil 876 876 } 877 877 878 - // IsNewPinnedAllowed returns if a new Issue or Pull request can be pinned 878 + // IsNewPinAllowed returns if a new Issue or Pull request can be pinned 879 879 func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) { 880 880 var maxPin int 881 881 _, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin)
+1 -1
models/issues/pull.go
··· 689 689 return pr, pr.LoadAttributes(ctx) 690 690 } 691 691 692 - // GetPullRequestsByBaseHeadInfo returns the pull request by given base and head 692 + // GetPullRequestByBaseHeadInfo returns the pull request by given base and head 693 693 func GetPullRequestByBaseHeadInfo(ctx context.Context, baseID, headID int64, base, head string) (*PullRequest, error) { 694 694 pr := &PullRequest{} 695 695 sess := db.GetEngine(ctx).
+4 -2
modules/setting/service.go
··· 91 91 92 92 // Explore page settings 93 93 Explore struct { 94 - RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"` 95 - DisableUsersPage bool `ini:"DISABLE_USERS_PAGE"` 94 + RequireSigninView bool `ini:"REQUIRE_SIGNIN_VIEW"` 95 + DisableUsersPage bool `ini:"DISABLE_USERS_PAGE"` 96 + DisableOrganizationsPage bool `ini:"DISABLE_ORGANIZATIONS_PAGE"` 97 + DisableCodePage bool `ini:"DISABLE_CODE_PAGE"` 96 98 } `ini:"service.explore"` 97 99 }{ 98 100 AllowedUserVisibilityModesSlice: []bool{true, true, true},
+3
release-notes/5714.md
··· 1 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/d13a4ab5632d6a9697bd0907f9c69ed57d949340) Fixed a bug related to disabling two-factor authentication 2 + chore: [commit](https://codeberg.org/forgejo/forgejo/commit/ab26d880932dbc116c43ea277029984c7a6d4e94) Emit a log message when failing to delete an inactive user 3 + feat: [commit](https://codeberg.org/forgejo/forgejo/commit/ab660c5944d59cdb4ecc071401445ac9f53cee45) Add `DISABLE_ORGANIZATIONS_PAGE` and `DISABLE_CODE_PAGE` settings for explore pages
+10 -2
routers/api/v1/api.go
··· 395 395 396 396 func reqExploreSignIn() func(ctx *context.APIContext) { 397 397 return func(ctx *context.APIContext) { 398 - if setting.Service.Explore.RequireSigninView && !ctx.IsSigned { 398 + if (setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView) && !ctx.IsSigned { 399 399 ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users") 400 + } 401 + } 402 + } 403 + 404 + func reqUsersExploreEnabled() func(ctx *context.APIContext) { 405 + return func(ctx *context.APIContext) { 406 + if setting.Service.Explore.DisableUsersPage { 407 + ctx.NotFound() 400 408 } 401 409 } 402 410 } ··· 887 895 888 896 // Users (requires user scope) 889 897 m.Group("/users", func() { 890 - m.Get("/search", reqExploreSignIn(), user.Search) 898 + m.Get("/search", reqExploreSignIn(), reqUsersExploreEnabled(), user.Search) 891 899 892 900 m.Group("/{username}", func() { 893 901 m.Get("", reqExploreSignIn(), user.GetInfo)
+3 -2
routers/web/explore/code.go
··· 21 21 22 22 // Code render explore code page 23 23 func Code(ctx *context.Context) { 24 - if !setting.Indexer.RepoIndexerEnabled { 24 + if !setting.Indexer.RepoIndexerEnabled || setting.Service.Explore.DisableCodePage { 25 25 ctx.Redirect(setting.AppSubURL + "/explore") 26 26 return 27 27 } 28 28 29 - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage 29 + ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage 30 + ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage 30 31 ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled 31 32 ctx.Data["Title"] = ctx.Tr("explore") 32 33 ctx.Data["PageIsExplore"] = true
+7 -1
routers/web/explore/org.go
··· 14 14 15 15 // Organizations render explore organizations page 16 16 func Organizations(ctx *context.Context) { 17 - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage 17 + if setting.Service.Explore.DisableOrganizationsPage { 18 + ctx.Redirect(setting.AppSubURL + "/explore") 19 + return 20 + } 21 + 22 + ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage 23 + ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage 18 24 ctx.Data["Title"] = ctx.Tr("explore") 19 25 ctx.Data["PageIsExplore"] = true 20 26 ctx.Data["PageIsExploreOrganizations"] = true
+3 -1
routers/web/explore/repo.go
··· 165 165 166 166 // Repos render explore repositories page 167 167 func Repos(ctx *context.Context) { 168 - ctx.Data["UsersIsDisabled"] = setting.Service.Explore.DisableUsersPage 168 + ctx.Data["UsersPageIsDisabled"] = setting.Service.Explore.DisableUsersPage 169 + ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage 170 + ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage 169 171 ctx.Data["Title"] = ctx.Tr("explore") 170 172 ctx.Data["PageIsExplore"] = true 171 173 ctx.Data["PageIsExploreRepositories"] = true
+3 -1
routers/web/explore/user.go
··· 131 131 // Users render explore users page 132 132 func Users(ctx *context.Context) { 133 133 if setting.Service.Explore.DisableUsersPage { 134 - ctx.Redirect(setting.AppSubURL + "/explore/repos") 134 + ctx.Redirect(setting.AppSubURL + "/explore") 135 135 return 136 136 } 137 + ctx.Data["OrganizationsPageIsDisabled"] = setting.Service.Explore.DisableOrganizationsPage 138 + ctx.Data["CodePageIsDisabled"] = setting.Service.Explore.DisableCodePage 137 139 ctx.Data["Title"] = ctx.Tr("explore") 138 140 ctx.Data["PageIsExplore"] = true 139 141 ctx.Data["PageIsExploreUsers"] = true
+9 -22
routers/web/user/search.go
··· 8 8 9 9 "code.gitea.io/gitea/models/db" 10 10 user_model "code.gitea.io/gitea/models/user" 11 + "code.gitea.io/gitea/modules/optional" 12 + "code.gitea.io/gitea/modules/setting" 11 13 "code.gitea.io/gitea/services/context" 12 14 "code.gitea.io/gitea/services/convert" 13 15 ) 14 16 15 - // Search search users 16 - func Search(ctx *context.Context) { 17 - listOptions := db.ListOptions{ 18 - Page: ctx.FormInt("page"), 19 - PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), 20 - } 21 - 22 - users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ 17 + // SearchCandidates searches candidate users for dropdown list 18 + func SearchCandidates(ctx *context.Context) { 19 + users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ 23 20 Actor: ctx.Doer, 24 21 Keyword: ctx.FormTrim("q"), 25 - UID: ctx.FormInt64("uid"), 26 22 Type: user_model.UserTypeIndividual, 27 - IsActive: ctx.FormOptionalBool("active"), 28 - ListOptions: listOptions, 23 + IsActive: optional.Some(true), 24 + ListOptions: db.ListOptions{PageSize: setting.UI.MembersPagingNum}, 29 25 }) 30 26 if err != nil { 31 - ctx.JSON(http.StatusInternalServerError, map[string]any{ 32 - "ok": false, 33 - "error": err.Error(), 34 - }) 27 + ctx.ServerError("Unable to search users", err) 35 28 return 36 29 } 37 - 38 - ctx.SetTotalCountHeader(maxResults) 39 - 40 - ctx.JSON(http.StatusOK, map[string]any{ 41 - "ok": true, 42 - "data": convert.ToUsers(ctx, ctx.Doer, users), 43 - }) 30 + ctx.JSON(http.StatusOK, map[string]any{"data": convert.ToUsers(ctx, ctx.Doer, users)}) 44 31 }
+6 -3
routers/web/user/setting/security/2fa.go
··· 34 34 if auth.IsErrTwoFactorNotEnrolled(err) { 35 35 ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) 36 36 ctx.Redirect(setting.AppSubURL + "/user/settings/security") 37 + } else { 38 + ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) 37 39 } 38 - ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) 39 40 return 40 41 } 41 42 ··· 64 65 if auth.IsErrTwoFactorNotEnrolled(err) { 65 66 ctx.Flash.Error(ctx.Tr("settings.twofa_not_enrolled")) 66 67 ctx.Redirect(setting.AppSubURL + "/user/settings/security") 68 + } else { 69 + ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) 67 70 } 68 - ctx.ServerError("SettingsTwoFactor: Failed to GetTwoFactorByUID", err) 69 71 return 70 72 } 71 73 ··· 74 76 // There is a potential DB race here - we must have been disabled by another request in the intervening period 75 77 ctx.Flash.Success(ctx.Tr("settings.twofa_disabled")) 76 78 ctx.Redirect(setting.AppSubURL + "/user/settings/security") 79 + } else { 80 + ctx.ServerError("SettingsTwoFactor: Failed to DeleteTwoFactorByID", err) 77 81 } 78 - ctx.ServerError("SettingsTwoFactor: Failed to DeleteTwoFactorByID", err) 79 82 return 80 83 } 81 84
+1 -1
routers/web/web.go
··· 642 642 m.Post("/logout", auth.SignOut) 643 643 m.Get("/task/{task}", reqSignIn, user.TaskStatus) 644 644 m.Get("/stopwatches", reqSignIn, user.GetStopwatches) 645 - m.Get("/search", ignExploreSignIn, user.Search) 645 + m.Get("/search_candidates", ignExploreSignIn, user.SearchCandidates) 646 646 m.Group("/oauth2", func() { 647 647 m.Get("/{provider}", auth.SignInOAuth) 648 648 m.Get("/{provider}/callback", auth.SignInOAuthCallback)
+1
services/user/user.go
··· 306 306 // Ignore users that were set inactive by admin. 307 307 if models.IsErrUserOwnRepos(err) || models.IsErrUserHasOrgs(err) || 308 308 models.IsErrUserOwnPackages(err) || models.IsErrDeleteLastAdminUser(err) { 309 + log.Warn("Inactive user %q has repositories, organizations or packages, skipping deletion: %v", u.Name, err) 309 310 continue 310 311 } 311 312 return err
+5 -2
templates/explore/navbar.tmpl
··· 3 3 <a class="{{if .PageIsExploreRepositories}}active {{end}}item" href="{{AppSubUrl}}/explore/repos"> 4 4 {{svg "octicon-repo"}} {{ctx.Locale.Tr "explore.repos"}} 5 5 </a> 6 - {{if not .UsersIsDisabled}} 6 + {{if not .UsersPageIsDisabled}} 7 7 <a class="{{if .PageIsExploreUsers}}active {{end}}item" href="{{AppSubUrl}}/explore/users"> 8 8 {{svg "octicon-person"}} {{ctx.Locale.Tr "explore.users"}} 9 9 </a> 10 10 {{end}} 11 + {{if not .OrganizationsPageIsDisabled}} 11 12 <a class="{{if .PageIsExploreOrganizations}}active {{end}}item" href="{{AppSubUrl}}/explore/organizations"> 12 13 {{svg "octicon-organization"}} {{ctx.Locale.Tr "explore.organizations"}} 13 14 </a> 14 - {{if and (not $.UnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled}} 15 + {{end}} 16 + 17 + {{if and (not $.UnitTypeCode.UnitGlobalDisabled) .IsRepoIndexerEnabled (not .CodePageIsDisabled)}} 15 18 <a class="{{if .PageIsExploreCode}}active {{end}}item" href="{{AppSubUrl}}/explore/code"> 16 19 {{svg "octicon-code"}} {{ctx.Locale.Tr "explore.code"}} 17 20 </a>
+12 -15
web_src/js/features/comp/SearchUserBox.js
··· 8 8 const searchUserBox = document.getElementById('search-user-box'); 9 9 if (!searchUserBox) return; 10 10 11 - const $searchUserBox = $(searchUserBox); 12 11 const allowEmailInput = searchUserBox.getAttribute('data-allow-email') === 'true'; 13 12 const allowEmailDescription = searchUserBox.getAttribute('data-allow-email-description') ?? undefined; 14 - $searchUserBox.search({ 13 + $(searchUserBox).search({ 15 14 minCharacters: 2, 16 15 apiSettings: { 17 - url: `${appSubUrl}/user/search?active=1&q={query}`, 16 + url: `${appSubUrl}/user/search_candidates?q={query}`, 18 17 onResponse(response) { 19 - const items = []; 20 - const searchQuery = $searchUserBox.find('input').val(); 18 + const resultItems = []; 19 + const searchQuery = searchUserBox.querySelector('input').value; 21 20 const searchQueryUppercase = searchQuery.toUpperCase(); 22 - $.each(response.data, (_i, item) => { 21 + for (const item of response.data) { 23 22 const resultItem = { 24 23 title: item.login, 25 24 image: item.avatar_url, 25 + description: htmlEscape(item.full_name), 26 26 }; 27 - if (item.full_name) { 28 - resultItem.description = htmlEscape(item.full_name); 29 - } 30 27 if (searchQueryUppercase === item.login.toUpperCase()) { 31 - items.unshift(resultItem); 28 + resultItems.unshift(resultItem); // add the exact match to the top 32 29 } else { 33 - items.push(resultItem); 30 + resultItems.push(resultItem); 34 31 } 35 - }); 32 + } 36 33 37 - if (allowEmailInput && !items.length && looksLikeEmailAddressCheck.test(searchQuery)) { 34 + if (allowEmailInput && !resultItems.length && looksLikeEmailAddressCheck.test(searchQuery)) { 38 35 const resultItem = { 39 36 title: searchQuery, 40 37 description: allowEmailDescription, 41 38 }; 42 - items.push(resultItem); 39 + resultItems.push(resultItem); 43 40 } 44 41 45 - return {results: items}; 42 + return {results: resultItems}; 46 43 }, 47 44 }, 48 45 searchFields: ['login', 'full_name'],