feat(pages): serve gallery images via asset endpoint
Add support for serving gallery images through /assets/gallery/ endpoint instead of /raw/ URLs. Maps gallery/ asset paths to .gallery/ folder in repo. Works for both repo-path mode and custom domain mode. Refactors asset serving into shared serveRepoFileAsset helper. Improves URL consistency and allows proper caching headers for gallery images on landing pages.
This commit is contained in:
@@ -70,6 +70,12 @@ func ServeLandingPage(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle asset requests (gallery images, custom assets)
|
||||
if assetPath, found := strings.CutPrefix(requestPath, "/assets/"); found && assetPath != "" {
|
||||
serveCustomDomainAsset(ctx, repo, assetPath)
|
||||
return
|
||||
}
|
||||
|
||||
// Check for blog paths on custom domain
|
||||
if config.Blog.Enabled && repo.BlogEnabled {
|
||||
if idStr, found := strings.CutPrefix(requestPath, "/blog/"); found {
|
||||
@@ -166,6 +172,16 @@ func setupLandingPageContext(ctx *context.Context, repo *repo_model.Repository,
|
||||
ctx.Data["HideMobileReleases"] = config.Advanced.HideMobileReleases
|
||||
}
|
||||
|
||||
// galleryAssetBaseURL returns the base URL for gallery image assets.
|
||||
// For repo-path mode (ctx.Repo is set), it returns /{owner}/{repo}/pages/assets/gallery/
|
||||
// For custom domain / subdomain mode, it returns /assets/gallery/
|
||||
func galleryAssetBaseURL(ctx *context.Context, repo *repo_model.Repository) string {
|
||||
if ctx.Repo != nil && ctx.Repo.Repository != nil {
|
||||
return repo.Link() + "/pages/assets/gallery/"
|
||||
}
|
||||
return "/assets/gallery/"
|
||||
}
|
||||
|
||||
// renderLandingPage renders the landing page based on the template
|
||||
func renderLandingPage(ctx *context.Context, repo *repo_model.Repository, config *pages_module.LandingConfig) {
|
||||
setupLandingPageContext(ctx, repo, config)
|
||||
@@ -200,7 +216,8 @@ func renderLandingPage(ctx *context.Context, repo *repo_model.Repository, config
|
||||
|
||||
// Load gallery images if gallery section is enabled
|
||||
if config.Gallery.Enabled {
|
||||
images := loadGalleryImagesForLanding(ctx, repo, config)
|
||||
baseURL := galleryAssetBaseURL(ctx, repo)
|
||||
images := loadGalleryImagesForLanding(ctx, repo, config, baseURL)
|
||||
if len(images) > 0 {
|
||||
ctx.Data["GalleryImages"] = images
|
||||
}
|
||||
@@ -333,7 +350,7 @@ type GalleryImageInfo struct {
|
||||
}
|
||||
|
||||
// loadGalleryImagesForLanding reads gallery images from the .gallery folder
|
||||
func loadGalleryImagesForLanding(ctx *context.Context, repo *repo_model.Repository, config *pages_module.LandingConfig) []GalleryImageInfo {
|
||||
func loadGalleryImagesForLanding(ctx *context.Context, repo *repo_model.Repository, config *pages_module.LandingConfig, baseURL string) []GalleryImageInfo {
|
||||
if repo.IsEmpty {
|
||||
return nil
|
||||
}
|
||||
@@ -403,7 +420,7 @@ func loadGalleryImagesForLanding(ctx *context.Context, repo *repo_model.Reposito
|
||||
images = append(images, GalleryImageInfo{
|
||||
Name: name,
|
||||
Caption: captionMap[name],
|
||||
URL: repo.Link() + "/raw/" + repo.DefaultBranch + "/.gallery/" + name,
|
||||
URL: baseURL + name,
|
||||
})
|
||||
if len(images) >= maxImages {
|
||||
break
|
||||
@@ -501,6 +518,29 @@ func selectTemplate(templateName string) templates.TplName {
|
||||
}
|
||||
}
|
||||
|
||||
// serveCustomDomainAsset serves asset files for custom domain / subdomain requests.
|
||||
func serveCustomDomainAsset(ctx *context.Context, repo *repo_model.Repository, assetPath string) {
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
branch := repo.DefaultBranch
|
||||
if branch == "" {
|
||||
branch = "main"
|
||||
}
|
||||
|
||||
commit, err := gitRepo.GetBranchCommit(branch)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
|
||||
serveRepoFileAsset(ctx, commit, assetPath)
|
||||
}
|
||||
|
||||
// ServePageAsset serves static assets for the landing page
|
||||
func ServePageAsset(ctx *context.Context) {
|
||||
repo, _, err := getRepoFromRequest(ctx)
|
||||
@@ -535,41 +575,7 @@ func ServePageAsset(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Try assets folder first
|
||||
fullPath := path.Join("assets", assetPath)
|
||||
entry, err := commit.GetTreeEntryByPath(fullPath)
|
||||
if err != nil {
|
||||
// Try .gitea/assets
|
||||
fullPath = path.Join(".gitea", "assets", assetPath)
|
||||
entry, err = commit.GetTreeEntryByPath(fullPath)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
reader, err := entry.Blob().DataAsync()
|
||||
if err != nil {
|
||||
ctx.ServerError("Failed to read asset", err)
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
// Set content type based on extension
|
||||
ext := path.Ext(assetPath)
|
||||
contentType := getContentType(ext)
|
||||
ctx.Resp.Header().Set("Content-Type", contentType)
|
||||
ctx.Resp.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
|
||||
// Stream content
|
||||
content := make([]byte, entry.Blob().Size())
|
||||
_, err = reader.Read(content)
|
||||
if err != nil && err.Error() != "EOF" {
|
||||
ctx.ServerError("Failed to read asset", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = ctx.Resp.Write(content)
|
||||
serveRepoFileAsset(ctx, commit, assetPath)
|
||||
}
|
||||
|
||||
// ServeRepoLandingPage serves the landing page for a repository via URL path
|
||||
@@ -686,17 +692,38 @@ func ServeRepoPageAsset(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Try assets folder first
|
||||
fullPath := path.Join("assets", assetPath)
|
||||
entry, err := commit.GetTreeEntryByPath(fullPath)
|
||||
if err != nil {
|
||||
// Try .gitea/assets
|
||||
fullPath = path.Join(".gitea", "assets", assetPath)
|
||||
serveRepoFileAsset(ctx, commit, assetPath)
|
||||
}
|
||||
|
||||
// serveRepoFileAsset resolves an asset path to a file in the repo and streams it.
|
||||
// For paths starting with "gallery/", it maps to .gallery/ in the repo.
|
||||
// Otherwise it looks in assets/ then .gitea/assets/.
|
||||
func serveRepoFileAsset(ctx *context.Context, commit *git.Commit, assetPath string) {
|
||||
var fullPath string
|
||||
var entry *git.TreeEntry
|
||||
var err error
|
||||
|
||||
if galleryName, found := strings.CutPrefix(assetPath, "gallery/"); found && galleryName != "" {
|
||||
// Serve from .gallery/ folder
|
||||
fullPath = ".gallery/" + galleryName
|
||||
entry, err = commit.GetTreeEntryByPath(fullPath)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Try assets folder first
|
||||
fullPath = path.Join("assets", assetPath)
|
||||
entry, err = commit.GetTreeEntryByPath(fullPath)
|
||||
if err != nil {
|
||||
// Try .gitea/assets
|
||||
fullPath = path.Join(".gitea", "assets", assetPath)
|
||||
entry, err = commit.GetTreeEntryByPath(fullPath)
|
||||
if err != nil {
|
||||
ctx.NotFound(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reader, err := entry.Blob().DataAsync()
|
||||
|
||||
Reference in New Issue
Block a user