loading up the forgejo repo on tangled to test page performance

[gitea] week 2025-14 cherry pick (gitea/main -> forgejo) (#7486)

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

Gusted 442958df 7861d2ff

Changed files
+80 -23
cmd
docker
root
etc
s6
openssh
models
routers
web
user
setting
services
web_src
css
markup
+37 -9
cmd/admin_user_create.go
··· 6 6 import ( 7 7 "errors" 8 8 "fmt" 9 + "strings" 9 10 10 11 auth_model "forgejo.org/models/auth" 11 12 "forgejo.org/models/db" ··· 60 61 &cli.BoolFlag{ 61 62 Name: "access-token", 62 63 Usage: "Generate access token for the user", 64 + }, 65 + &cli.StringFlag{ 66 + Name: "access-token-name", 67 + Usage: `Name of the generated access token`, 68 + Value: "gitea-admin", 69 + }, 70 + &cli.StringFlag{ 71 + Name: "access-token-scopes", 72 + Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`, 73 + Value: "all", 63 74 }, 64 75 &cli.BoolFlag{ 65 76 Name: "restricted", ··· 157 168 IsRestricted: restricted, 158 169 } 159 170 171 + var accessTokenName string 172 + var accessTokenScope auth_model.AccessTokenScope 173 + if c.IsSet("access-token") { 174 + accessTokenName = strings.TrimSpace(c.String("access-token-name")) 175 + if accessTokenName == "" { 176 + return errors.New("access-token-name cannot be empty") 177 + } 178 + var err error 179 + accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize() 180 + if err != nil { 181 + return fmt.Errorf("invalid access token scope provided: %w", err) 182 + } 183 + if !accessTokenScope.HasPermissionScope() { 184 + return errors.New("access token does not have any permission") 185 + } 186 + } else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") { 187 + return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set") 188 + } 189 + 190 + // arguments should be prepared before creating the user & access token, in case there is anything wrong 191 + 192 + // create the user 160 193 if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil { 161 194 return fmt.Errorf("CreateUser: %w", err) 162 195 } 196 + fmt.Printf("New user '%s' has been successfully created!\n", username) 163 197 164 - if c.Bool("access-token") { 165 - t := &auth_model.AccessToken{ 166 - Name: "gitea-admin", 167 - UID: u.ID, 168 - } 169 - 198 + // create the access token 199 + if accessTokenScope != "" { 200 + t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope} 170 201 if err := auth_model.NewAccessToken(ctx, t); err != nil { 171 202 return err 172 203 } 173 - 174 204 fmt.Printf("Access token was successfully created... %s\n", t.Token) 175 205 } 176 - 177 - fmt.Printf("New user '%s' has been successfully created!\n", username) 178 206 return nil 179 207 }
+6 -3
cmd/admin_user_generate_access_token.go
··· 34 34 }, 35 35 &cli.StringFlag{ 36 36 Name: "scopes", 37 - Value: "", 38 - Usage: "Comma separated list of scopes to apply to access token", 37 + Value: "all", 38 + Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`, 39 39 }, 40 40 }, 41 41 Action: runGenerateAccessToken, ··· 43 43 44 44 func runGenerateAccessToken(c *cli.Context) error { 45 45 if !c.IsSet("username") { 46 - return errors.New("You must provide a username to generate a token for") 46 + return errors.New("you must provide a username to generate a token for") 47 47 } 48 48 49 49 ctx, cancel := installSignals() ··· 76 76 accessTokenScope, err := auth_model.AccessTokenScope(c.String("scopes")).Normalize() 77 77 if err != nil { 78 78 return fmt.Errorf("invalid access token scope provided: %w", err) 79 + } 80 + if !accessTokenScope.HasPermissionScope() { 81 + return errors.New("access token does not have any permission") 79 82 } 80 83 t.Scope = accessTokenScope 81 84
+12
docker/root/etc/s6/openssh/setup
··· 31 31 SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa_cert"} 32 32 fi 33 33 34 + if [ -e /data/ssh/ssh_host_ed25519-cert.pub ]; then 35 + SSH_ED25519_CERT=${SSH_ED25519_CERT:-"/data/ssh/ssh_host_ed25519-cert.pub"} 36 + fi 37 + 38 + if [ -e /data/ssh/ssh_host_rsa-cert.pub ]; then 39 + SSH_RSA_CERT=${SSH_RSA_CERT:-"/data/ssh/ssh_host_rsa-cert.pub"} 40 + fi 41 + 42 + if [ -e /data/ssh/ssh_host_ecdsa-cert.pub ]; then 43 + SSH_ECDSA_CERT=${SSH_ECDSA_CERT:-"/data/ssh/ssh_host_ecdsa-cert.pub"} 44 + fi 45 + 34 46 if [ -d /etc/ssh ]; then 35 47 SSH_PORT=${SSH_PORT:-"22"} \ 36 48 SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
+4
models/auth/access_token_scope.go
··· 283 283 return bitmap.toScope(), nil 284 284 } 285 285 286 + func (s AccessTokenScope) HasPermissionScope() bool { 287 + return s != "" && s != AccessTokenScopePublicOnly 288 + } 289 + 286 290 // PublicOnly checks if this token scope is limited to public resources 287 291 func (s AccessTokenScope) PublicOnly() (bool, error) { 288 292 bitmap, err := s.parse()
+1 -1
models/user/avatar.go
··· 62 62 63 63 // AvatarLinkWithSize returns a link to the user's avatar with size. size <= 0 means default size 64 64 func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string { 65 - if u.IsGhost() { 65 + if u.IsGhost() || u.ID <= 0 { 66 66 return avatars.DefaultAvatarLink() 67 67 } 68 68
+3
routers/web/user/setting/applications.go
··· 49 49 ctx.ServerError("GetScope", err) 50 50 return 51 51 } 52 + if !scope.HasPermissionScope() { 53 + ctx.Flash.Error(ctx.Tr("settings.at_least_one_permission"), true) 54 + } 52 55 t := &auth_model.AccessToken{ 53 56 UID: ctx.Doer.ID, 54 57 Name: form.Name,
+1 -2
services/packages/rpm/repository.go
··· 410 410 files = append(files, f) 411 411 } 412 412 } 413 - packageVersion := fmt.Sprintf("%s-%s", pd.FileMetadata.Version, pd.FileMetadata.Release) 414 413 packages = append(packages, &Package{ 415 414 Type: "rpm", 416 415 Name: pd.Package.Name, ··· 439 438 Archive: pd.FileMetadata.ArchiveSize, 440 439 }, 441 440 Location: Location{ 442 - Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, packageVersion, pd.FileMetadata.Architecture, pd.Package.Name, packageVersion, pd.FileMetadata.Architecture), 441 + Href: fmt.Sprintf("package/%s/%s/%s/%s-%s.%s.rpm", pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture, pd.Package.Name, pd.Version.Version, pd.FileMetadata.Architecture), 443 442 }, 444 443 Format: Format{ 445 444 License: pd.VersionMetadata.License,
+14 -6
services/webhook/discord.go
··· 16 16 "unicode/utf8" 17 17 18 18 webhook_model "forgejo.org/models/webhook" 19 + "forgejo.org/modules/base" 19 20 "forgejo.org/modules/git" 20 21 "forgejo.org/modules/json" 21 22 "forgejo.org/modules/log" ··· 151 152 redColor = color("ff3232") 152 153 ) 153 154 155 + // https://discord.com/developers/docs/resources/message#embed-object-embed-limits 156 + // Discord has some limits in place for the embeds. 157 + // According to some tests, there is no consistent limit for different character sets. 158 + // For example: 4096 ASCII letters are allowed, but only 2490 emoji characters are allowed. 159 + // To keep it simple, we currently truncate at 2000. 160 + const discordDescriptionCharactersLimit = 2000 161 + 162 + type discordConvertor struct { 163 + Username string 164 + AvatarURL string 165 + } 166 + 154 167 // Create implements PayloadConvertor Create method 155 168 func (d discordConvertor) Create(p *api.CreatePayload) (DiscordPayload, error) { 156 169 // created tag/branch ··· 312 325 return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil 313 326 } 314 327 315 - type discordConvertor struct { 316 - Username string 317 - AvatarURL string 318 - } 319 - 320 328 var _ shared.PayloadConvertor[DiscordPayload] = discordConvertor{} 321 329 322 330 func (discordHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t *webhook_model.HookTask) (*http.Request, []byte, error) { ··· 357 365 Embeds: []DiscordEmbed{ 358 366 { 359 367 Title: title, 360 - Description: text, 368 + Description: base.TruncateString(text, discordDescriptionCharactersLimit), 361 369 URL: url, 362 370 Color: color, 363 371 Author: DiscordEmbedAuthor{
+1 -1
services/webhook/discord_test.go
··· 175 175 require.NoError(t, err) 176 176 177 177 assert.Len(t, pl.Embeds, 1) 178 - assert.Len(t, pl.Embeds[0].Description, 4096) 178 + assert.Len(t, pl.Embeds[0].Description, 2000) 179 179 }) 180 180 181 181 t.Run("IssueComment", func(t *testing.T) {
+1 -1
web_src/css/markup/content.css
··· 2 2 overflow: hidden; 3 3 font-size: 16px; 4 4 line-height: 1.5 !important; 5 - word-wrap: break-word; 5 + overflow-wrap: anywhere; 6 6 } 7 7 8 8 .markup > *:first-child {