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