mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
92 lines
2.8 KiB
Go
92 lines
2.8 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"image/png"
|
|
"io"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/hanzoai/git/models/db"
|
|
"github.com/hanzoai/git/modules/avatar"
|
|
"github.com/hanzoai/git/modules/httplib"
|
|
"github.com/hanzoai/git/modules/log"
|
|
"github.com/hanzoai/git/modules/setting"
|
|
"github.com/hanzoai/git/modules/storage"
|
|
)
|
|
|
|
// CustomAvatarRelativePath returns repository custom avatar file path.
|
|
func (repo *Repository) CustomAvatarRelativePath() string {
|
|
return repo.Avatar
|
|
}
|
|
|
|
// ExistsWithAvatarAtStoragePath returns true if there is a user with this Avatar
|
|
func ExistsWithAvatarAtStoragePath(ctx context.Context, storagePath string) (bool, error) {
|
|
// See func (repo *Repository) CustomAvatarRelativePath()
|
|
// repo.Avatar is used directly as the storage path - therefore we can check for existence directly using the path
|
|
return db.GetEngine(ctx).Where("`avatar`=?", storagePath).Exist(new(Repository))
|
|
}
|
|
|
|
// RelAvatarLink returns a relative link to the repository's avatar.
|
|
func (repo *Repository) RelAvatarLink(ctx context.Context) string {
|
|
return repo.relAvatarLink(ctx)
|
|
}
|
|
|
|
// generateRandomAvatar generates a random avatar for repository.
|
|
func generateRandomAvatar(ctx context.Context, repo *Repository) error {
|
|
idToString := strconv.FormatInt(repo.ID, 10)
|
|
|
|
seed := idToString
|
|
img := avatar.RandomImageDefaultSize([]byte(seed))
|
|
|
|
repo.Avatar = idToString
|
|
|
|
if err := storage.SaveFrom(storage.RepoAvatars, repo.CustomAvatarRelativePath(), func(w io.Writer) error {
|
|
return png.Encode(w, img)
|
|
}); err != nil {
|
|
return fmt.Errorf("failed to create dir %s: %w", repo.CustomAvatarRelativePath(), err)
|
|
}
|
|
|
|
log.Info("New random avatar created for repository: %d", repo.ID)
|
|
|
|
if _, err := db.GetEngine(ctx).ID(repo.ID).Cols("avatar").NoAutoTime().Update(repo); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (repo *Repository) relAvatarLink(ctx context.Context) string {
|
|
// If no avatar - path is empty
|
|
avatarPath := repo.CustomAvatarRelativePath()
|
|
if len(avatarPath) == 0 {
|
|
switch mode := setting.RepoAvatar.Fallback; mode {
|
|
case "image":
|
|
return setting.RepoAvatar.FallbackImage
|
|
case "random":
|
|
if err := generateRandomAvatar(ctx, repo); err != nil {
|
|
log.Error("generateRandomAvatar: %v", err)
|
|
}
|
|
default:
|
|
// default behaviour: do not display avatar
|
|
return ""
|
|
}
|
|
}
|
|
return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar)
|
|
}
|
|
|
|
// AvatarLink returns the full avatar url with http host or the empty string if the repo doesn't have an avatar.
|
|
//
|
|
// TODO: refactor it to a relative URL, but it is still used in API response at the moment
|
|
func (repo *Repository) AvatarLink(ctx context.Context) string {
|
|
relLink := repo.relAvatarLink(ctx)
|
|
if relLink != "" {
|
|
return httplib.MakeAbsoluteURL(ctx, relLink)
|
|
}
|
|
return ""
|
|
}
|