teafolio/page_repository.go

138 lines
4.1 KiB
Go

package main
import (
"bytes"
"errors"
"fmt"
"html"
"log"
"net/http"
"net/url"
"strings"
"teafolio/gitea"
"github.com/yuin/goldmark"
)
func (this *Application) Repopage(w http.ResponseWriter, r *http.Request, repoName string) {
ctx := r.Context()
repoURL := this.cfg.Gitea.URL + url.PathEscape(this.cfg.Gitea.Org) + `/` + url.PathEscape(repoName)
extraHead := ""
readme, err := this.gitea.RepoFile(ctx, repoName, `README.md`)
if err != nil {
log.Printf("%s %s: %s", r.Method, r.URL.Path, err)
http.Redirect(w, r, repoURL, http.StatusTemporaryRedirect)
return
}
lines := strings.Split(string(readme), "\n")
// Check if this repo has the 'article' tag
hasArticleTag := false
this.reposMut.RLock()
for _, rr := range this.reposCache {
if rr.Name != repoName {
continue
}
for _, topic := range rr.Topics {
if topic == "article" {
hasArticleTag = true
break
}
}
}
this.reposMut.RUnlock()
// We add some extra badges based on special text entries
extraBadgesMd := ``
if !hasArticleTag {
extraBadgesMd += ` %%REPLACEME__BADGE%%`
}
extraBadgesMd += ` [![](https://img.shields.io/badge/vcs-git-green?logo=git)](` + repoURL + `)`
// Inject more badges to 3rd line; or, create badges on 3rd line if there are none already
if len(lines) >= 3 && strings.Contains(lines[2], `shields.io`) {
lines[2] += ` ` + extraBadgesMd
} else {
// Push other lines down
lines = append([]string{lines[0], lines[1], extraBadgesMd, ""}, lines[2:]...)
}
readmeHtml, err := this.gitea.RenderMarkdown(ctx, repoName, strings.Join(lines, "\n"))
if err != nil {
if !errors.Is(err, gitea.ErrMarkdownCooldown) {
log.Printf("%s %s: %s", r.Method, r.URL.Path, fmt.Errorf("rendering markdown: %w", err))
}
// Failed to use Gitea's markdown renderer
// HTTP 401 (Unauthorized) started happening with this API sometime
// between Gitea 1.18 -> 1.21, and no authorization token seems
// sufficient to make it work again
// Use our own one instead as a fallback
buff := bytes.Buffer{}
err = goldmark.Convert([]byte(strings.Join(lines, "\n")), &buff)
if err != nil {
// Built-in markdown renderer didn't work either
log.Printf("%s %s: %s", r.Method, r.URL.Path, fmt.Errorf("rendering markdown: %w", err))
http.Redirect(w, r, repoURL, http.StatusTemporaryRedirect)
return
}
// OK
readmeHtml = buff.Bytes()
err = nil
}
readmeHtml = []byte(strings.Replace(string(readmeHtml), `%%REPLACEME__BADGE%%`, `<img src="/static/build_success_brightgreen.svg" style="width:90px;height:20px;">`, 1))
images, err := this.gitea.ImageFilesForRepo(ctx, repoName)
if err != nil {
log.Printf("%s %s: %s", r.Method, r.URL.Path, fmt.Errorf("listing images: %w", err))
http.Redirect(w, r, repoURL, http.StatusTemporaryRedirect)
return
}
// If the Git repository contains a top-level go.mod file, allow vanity imports
if goMod, err := this.gitea.RepoFile(ctx, repoName, `go.mod`); err == nil {
// Check the first line should be `module MODULENAME\n`
firstLine := bytes.SplitN(goMod, []byte("\n"), 2)[0]
if bytes.HasPrefix(firstLine, []byte("module ")) {
moduleName := firstLine[7:]
extraHead = `<meta name="go-import" content="` + html.EscapeString(string(moduleName)) + ` git ` + repoURL + `.git">`
}
}
// De-escalate all headers in rendered markdown to match our style
repl := strings.NewReplacer(`<h1`, `<h2`, `<h2`, `<h3`, `<h3`, `<h4`,
`</h1>`, `</h2>`, `</h2>`, `</h3>`, `</h3>`, `</h4>`)
// Ready for template
this.Templatepage(w, r, repoName, extraHead, func() {
projBodyclass := `projbody`
if len(images) > 0 {
projBodyclass += ` projbody_halfw`
}
fmt.Fprint(w, `<div class="projinfo"><div class="`+projBodyclass+`">`)
repl.WriteString(w, string(readmeHtml))
fmt.Fprint(w, `</div>`)
if len(images) > 0 {
fmt.Fprint(w, `<div class="projimg">`)
for _, img := range images {
fmt.Fprint(w, `<a href="`+html.EscapeString(img.RawURL)+`"><img alt="" class="thumbimage" src="`+html.EscapeString(img.RawURL)+`" /></a>`)
}
fmt.Fprint(w, `</div>`)
}
fmt.Fprint(w, `<div style="clear:both;"></div>`)
fmt.Fprint(w, `</div>`) // projbody
})
}