1package git
2
3import (
4 "bytes"
5 "io"
6 "log/slog"
7 "net/http"
8 "os"
9 "path/filepath"
10
11 "git.gabrielgio.me/cerrado/pkg/ext"
12 "git.gabrielgio.me/cerrado/pkg/service"
13 "git.gabrielgio.me/cerrado/templates"
14 "github.com/alecthomas/chroma/v2"
15 "github.com/alecthomas/chroma/v2/formatters/html"
16 "github.com/alecthomas/chroma/v2/lexers"
17 "github.com/alecthomas/chroma/v2/styles"
18 "github.com/go-git/go-git/v5/plumbing"
19 "github.com/go-git/go-git/v5/plumbing/object"
20 "github.com/gomarkdown/markdown"
21 markdownhtml "github.com/gomarkdown/markdown/html"
22 "github.com/gomarkdown/markdown/parser"
23)
24
25type (
26 GitHandler struct {
27 gitService gitService
28 readmePath string
29 }
30
31 gitService interface {
32 ListRepositories() ([]*service.Repository, error)
33 ListCommits(name string, ref string) ([]*object.Commit, error)
34 GetHead(name string) (*plumbing.Reference, error)
35 GetTree(name, ref, path string) (*object.Tree, error)
36 GetFileContent(name, ref, path string) (string, error)
37 ListTags(name string) ([]*object.Tag, error)
38 ListBranches(name string) ([]*plumbing.Reference, error)
39 }
40
41 configurationRepository interface {
42 GetRootReadme() string
43 }
44)
45
46func NewGitHandler(gitService gitService, confRepo configurationRepository) *GitHandler {
47 return &GitHandler{
48 gitService: gitService,
49 readmePath: confRepo.GetRootReadme(),
50 }
51}
52
53func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) {
54 repos, err := g.gitService.ListRepositories()
55 if err != nil {
56 slog.Error("Error listing repo", "error", err)
57 return
58 }
59
60 f, err := os.Open(g.readmePath)
61 if err != nil {
62 slog.Error("Error loading readme file", "error", err)
63 return
64 }
65
66 bs, err := io.ReadAll(f)
67 if err != nil {
68 slog.Error("Error reading readme file bytes", "error", err)
69 return
70 }
71
72 extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
73 p := parser.NewWithExtensions(extensions)
74 doc := p.Parse(bs)
75
76 htmlFlag := markdownhtml.CommonFlags | markdownhtml.HrefTargetBlank
77 opts := markdownhtml.RendererOptions{Flags: htmlFlag}
78 renderer := markdownhtml.NewRenderer(opts)
79
80 bs = markdown.Render(doc, renderer)
81
82 gitList := &templates.GitListPage{
83 Respositories: repos,
84 About: bs,
85 }
86 templates.WritePageTemplate(w, gitList)
87}
88
89func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) {
90 ext.SetHTML(w)
91 name := r.PathValue("name")
92 ref, err := g.gitService.GetHead(name)
93 if err != nil {
94 slog.Error("Error loading head", "error", err)
95 return
96 }
97
98 gitList := &templates.GitItemPage{
99 Name: name,
100 Ref: ref.Name().Short(),
101 GitItemBase: &templates.GitItemSummaryPage{},
102 }
103 templates.WritePageTemplate(w, gitList)
104}
105
106func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) {
107 ext.SetHTML(w)
108 name := r.PathValue("name")
109 ref, err := g.gitService.GetHead(name)
110 if err != nil {
111 slog.Error("Error loading head", "error", err)
112 return
113 }
114 gitList := &templates.GitItemPage{
115 Name: name,
116 Ref: ref.Name().Short(),
117 GitItemBase: &templates.GitItemAboutPage{},
118 }
119 templates.WritePageTemplate(w, gitList)
120}
121
122func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) {
123 ext.SetHTML(w)
124 name := r.PathValue("name")
125
126 tags, err := g.gitService.ListTags(name)
127 if err != nil {
128 slog.Error("Error loading tags", "error", err)
129 return
130 }
131
132 branches, err := g.gitService.ListBranches(name)
133 if err != nil {
134 slog.Error("Error loading branches", "error", err)
135 return
136 }
137
138 ref, err := g.gitService.GetHead(name)
139 if err != nil {
140 slog.Error("Error loading head", "error", err)
141 return
142 }
143
144 gitList := &templates.GitItemPage{
145 Name: name,
146 Ref: ref.Name().Short(),
147 GitItemBase: &templates.GitItemRefsPage{
148 Tags: tags,
149 Branches: branches,
150 },
151 }
152 templates.WritePageTemplate(w, gitList)
153}
154
155func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) {
156 ext.SetHTML(w)
157 name := r.PathValue("name")
158 ref := r.PathValue("ref")
159 rest := r.PathValue("rest")
160
161 tree, err := g.gitService.GetTree(name, ref, rest)
162 if err != nil {
163 slog.Error("Error loading tree", "error", err)
164 return
165 }
166
167 gitList := &templates.GitItemPage{
168 Name: name,
169 Ref: ref,
170 GitItemBase: &templates.GitItemTreePage{
171 CurrentPath: rest,
172 Tree: tree,
173 Ref: ref,
174 Name: name,
175 },
176 }
177 templates.WritePageTemplate(w, gitList)
178}
179
180func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) {
181 ext.SetHTML(w)
182 name := r.PathValue("name")
183 ref := r.PathValue("ref")
184 rest := r.PathValue("rest")
185
186 file, err := g.gitService.GetFileContent(name, ref, rest)
187 if err != nil {
188 slog.Error("Error loading blob", "error", err)
189 return
190 }
191
192 filename := filepath.Base(rest)
193 lexer := GetLexers(filename)
194 style := styles.Get("xcode")
195 formatter := html.New(
196 html.WithLineNumbers(true),
197 )
198 iterator, err := lexer.Tokenise(nil, file)
199 if err != nil {
200 slog.Error("Error tokenise", "error", err)
201 return
202 }
203
204 var code bytes.Buffer
205 err = formatter.Format(&code, style, iterator)
206 if err != nil {
207 slog.Error("Error format", "error", err)
208 return
209 }
210
211 gitList := &templates.GitItemPage{
212 Name: name,
213 Ref: ref,
214 GitItemBase: &templates.GitItemBlobPage{
215 File: rest,
216 Content: code.Bytes(),
217 },
218 }
219 templates.WritePageTemplate(w, gitList)
220}
221
222func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) {
223 ext.SetHTML(w)
224 name := r.PathValue("name")
225 ref := r.PathValue("ref")
226
227 commits, err := g.gitService.ListCommits(name, ref)
228 if err != nil {
229 slog.Error("Error loading commits", "error", err)
230 return
231 }
232
233 gitList := &templates.GitItemPage{
234 Name: name,
235 Ref: ref,
236 GitItemBase: &templates.GitItemLogPage{
237 Commits: commits,
238 },
239 }
240 templates.WritePageTemplate(w, gitList)
241}
242
243func GetLexers(filename string) chroma.Lexer {
244 if filename == "APKBUILD" {
245 return lexers.Get("sh")
246 }
247
248 lexer := lexers.Get(filename)
249
250 if lexer == nil {
251 lexer = lexers.Get("txt")
252 }
253 return lexer
254}