package git import ( "bytes" "io" "log/slog" "net/http" "os" "path/filepath" "git.gabrielgio.me/cerrado/pkg/ext" "git.gabrielgio.me/cerrado/pkg/service" "git.gabrielgio.me/cerrado/templates" "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/formatters/html" "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/object" "github.com/gomarkdown/markdown" markdownhtml "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" ) type ( GitHandler struct { gitService gitService readmePath string } gitService interface { ListRepositories() ([]*service.Repository, error) ListCommits(name string, ref string) ([]*object.Commit, error) GetHead(name string) (*plumbing.Reference, error) GetTree(name, ref, path string) (*object.Tree, error) GetFileContent(name, ref, path string) (string, error) ListTags(name string) ([]*object.Tag, error) ListBranches(name string) ([]*plumbing.Reference, error) } configurationRepository interface { GetRootReadme() string } ) func NewGitHandler(gitService gitService, confRepo configurationRepository) *GitHandler { return &GitHandler{ gitService: gitService, readmePath: confRepo.GetRootReadme(), } } func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) { repos, err := g.gitService.ListRepositories() if err != nil { slog.Error("Error listing repo", "error", err) return } f, err := os.Open(g.readmePath) if err != nil { slog.Error("Error loading readme file", "error", err) return } bs, err := io.ReadAll(f) if err != nil { slog.Error("Error reading readme file bytes", "error", err) return } extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock p := parser.NewWithExtensions(extensions) doc := p.Parse(bs) htmlFlag := markdownhtml.CommonFlags | markdownhtml.HrefTargetBlank opts := markdownhtml.RendererOptions{Flags: htmlFlag} renderer := markdownhtml.NewRenderer(opts) bs = markdown.Render(doc, renderer) gitList := &templates.GitListPage{ Respositories: repos, About: bs, } templates.WritePageTemplate(w, gitList) } func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) { ext.SetHTML(w) name := r.PathValue("name") ref, err := g.gitService.GetHead(name) if err != nil { slog.Error("Error loading head", "error", err) return } gitList := &templates.GitItemPage{ Name: name, Ref: ref.Name().Short(), GitItemBase: &templates.GitItemSummaryPage{}, } templates.WritePageTemplate(w, gitList) } func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) { ext.SetHTML(w) name := r.PathValue("name") ref, err := g.gitService.GetHead(name) if err != nil { slog.Error("Error loading head", "error", err) return } gitList := &templates.GitItemPage{ Name: name, Ref: ref.Name().Short(), GitItemBase: &templates.GitItemAboutPage{}, } templates.WritePageTemplate(w, gitList) } func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) { ext.SetHTML(w) name := r.PathValue("name") tags, err := g.gitService.ListTags(name) if err != nil { slog.Error("Error loading tags", "error", err) return } branches, err := g.gitService.ListBranches(name) if err != nil { slog.Error("Error loading branches", "error", err) return } ref, err := g.gitService.GetHead(name) if err != nil { slog.Error("Error loading head", "error", err) return } gitList := &templates.GitItemPage{ Name: name, Ref: ref.Name().Short(), GitItemBase: &templates.GitItemRefsPage{ Tags: tags, Branches: branches, }, } templates.WritePageTemplate(w, gitList) } func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) { ext.SetHTML(w) name := r.PathValue("name") ref := r.PathValue("ref") rest := r.PathValue("rest") tree, err := g.gitService.GetTree(name, ref, rest) if err != nil { slog.Error("Error loading tree", "error", err) return } gitList := &templates.GitItemPage{ Name: name, Ref: ref, GitItemBase: &templates.GitItemTreePage{ CurrentPath: rest, Tree: tree, Ref: ref, Name: name, }, } templates.WritePageTemplate(w, gitList) } func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) { ext.SetHTML(w) name := r.PathValue("name") ref := r.PathValue("ref") rest := r.PathValue("rest") file, err := g.gitService.GetFileContent(name, ref, rest) if err != nil { slog.Error("Error loading blob", "error", err) return } filename := filepath.Base(rest) lexer := GetLexers(filename) style := styles.Get("xcode") formatter := html.New( html.WithLineNumbers(true), ) iterator, err := lexer.Tokenise(nil, file) if err != nil { slog.Error("Error tokenise", "error", err) return } var code bytes.Buffer err = formatter.Format(&code, style, iterator) if err != nil { slog.Error("Error format", "error", err) return } gitList := &templates.GitItemPage{ Name: name, Ref: ref, GitItemBase: &templates.GitItemBlobPage{ File: rest, Content: code.Bytes(), }, } templates.WritePageTemplate(w, gitList) } func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) { ext.SetHTML(w) name := r.PathValue("name") ref := r.PathValue("ref") commits, err := g.gitService.ListCommits(name, ref) if err != nil { slog.Error("Error loading commits", "error", err) return } gitList := &templates.GitItemPage{ Name: name, Ref: ref, GitItemBase: &templates.GitItemLogPage{ Commits: commits, }, } templates.WritePageTemplate(w, gitList) } func GetLexers(filename string) chroma.Lexer { if filename == "APKBUILD" { return lexers.Get("sh") } lexer := lexers.Get(filename) if lexer == nil { lexer = lexers.Get("txt") } return lexer }