1diff --git a/main.go b/main.go
2index d2452e8a5766b22174b55f2ffbdcd2bc2149a954..0ba2dbc9553dcab2cbb3b5c0b3172c79c7dda650 100644
3--- a/main.go
4+++ b/main.go
5@@ -1,25 +1,17 @@
6 package main
7
8 import (
9- "bytes"
10 "context"
11- "encoding/json"
12 "flag"
13- "io/fs"
14 "log/slog"
15 "net/http"
16 "os"
17 "os/signal"
18 "time"
19
20- "github.com/alecthomas/chroma/v2/formatters/html"
21- "github.com/alecthomas/chroma/v2/lexers"
22- "github.com/alecthomas/chroma/v2/styles"
23-
24- "git.gabrielgio.me/cerrado/pkg/config"
25+ "git.gabrielgio.me/cerrado/pkg/handler"
26+ "git.gabrielgio.me/cerrado/pkg/service"
27 "git.gabrielgio.me/cerrado/pkg/worker"
28- "git.gabrielgio.me/cerrado/static"
29- "git.gabrielgio.me/cerrado/templates"
30 )
31
32 func main() {
33@@ -41,57 +33,20 @@ flag.Parse()
34
35 mux := http.NewServeMux()
36
37- staticFs, err := fs.Sub(static.Static, ".")
38+ staticHandler, err := handler.NewStaticHander("/static/")
39 if err != nil {
40 return err
41 }
42
43- mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFs))))
44- mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
45- slog.Info("Handling index")
46+ // services
47+ gitService := service.NewGitService()
48
49- f, err := os.Open(*configPath)
50- if err != nil {
51- slog.Error("Error openning config file json", "error", err, "path", *configPath)
52- return
53- }
54+ //handlers
55+ gitHandler := handler.NewGitHandler(gitService)
56
57- c, err := config.Parse(f)
58- if err != nil {
59- slog.Error("Error parsing config", "error", err, "path", *configPath)
60- return
61- }
62-
63- b, err := json.MarshalIndent(c, "", " ")
64- if err != nil {
65- slog.Error("Error parsing json", "error", err)
66- return
67- }
68-
69- lexer := lexers.Get("json")
70- style := styles.Get("monokailight")
71- formatter := html.New(
72- html.WithLineNumbers(true),
73- )
74- iterator, err := lexer.Tokenise(nil, string(b))
75- if err != nil {
76- slog.Error("Error tokenise", "error", err)
77- return
78- }
79-
80- var code bytes.Buffer
81- err = formatter.Format(&code, style, iterator)
82- if err != nil {
83- slog.Error("Error format", "error", err)
84- return
85- }
86-
87- hello := &templates.HelloPage{
88- Body: code.String(),
89- }
90-
91- templates.WritePageTemplate(w, hello)
92- })
93+ mux.Handle("/static/", staticHandler)
94+ mux.HandleFunc("/config", handler.ConfigFile(*configPath))
95+ mux.HandleFunc("/", gitHandler.List)
96 serverTask := worker.NewServerTask(&http.Server{Handler: mux, Addr: "0.0.0.0:8080"})
97
98 pool := worker.NewTaskPool()
99diff --git a/pkg/handler/git.go b/pkg/handler/git.go
100new file mode 100644
101index 0000000000000000000000000000000000000000..5b0912187f278158f0ab3ff7287a1a2f06e0e6df
102--- /dev/null
103+++ b/pkg/handler/git.go
104@@ -0,0 +1,21 @@
105+package handler
106+
107+import (
108+ "net/http"
109+
110+ "git.gabrielgio.me/cerrado/pkg/service"
111+ "git.gabrielgio.me/cerrado/templates"
112+)
113+
114+type GitHandler struct {
115+ gitService *service.GitService
116+}
117+
118+func NewGitHandler(gitService *service.GitService) *GitHandler {
119+ return &GitHandler{gitService}
120+}
121+
122+func (g *GitHandler) List(w http.ResponseWriter, _ *http.Request) {
123+ gitList := &templates.GitListPage{g.gitService.ListRepositories()}
124+ templates.WritePageTemplate(w, gitList)
125+}
126diff --git a/pkg/handler/static.go b/pkg/handler/static.go
127new file mode 100644
128index 0000000000000000000000000000000000000000..9f312f41f09ef14dad4649bfaa0b76d61f8abb5e
129--- /dev/null
130+++ b/pkg/handler/static.go
131@@ -0,0 +1,18 @@
132+package handler
133+
134+import (
135+ "io/fs"
136+ "net/http"
137+
138+ "git.gabrielgio.me/cerrado/static"
139+)
140+
141+func NewStaticHander(prefix string) (http.Handler, error) {
142+ staticFs, err := fs.Sub(static.Static, ".")
143+ if err != nil {
144+ return nil, err
145+ }
146+
147+ handler := http.StripPrefix(prefix, http.FileServer(http.FS(staticFs)))
148+ return handler, nil
149+}
150diff --git a/pkg/handler/status.go b/pkg/handler/status.go
151new file mode 100644
152index 0000000000000000000000000000000000000000..2a84a7e0e00ce79a7236eda6e444c6c3220209b9
153--- /dev/null
154+++ b/pkg/handler/status.go
155@@ -0,0 +1,62 @@
156+package handler
157+
158+import (
159+ "bytes"
160+ "encoding/json"
161+ "log/slog"
162+ "net/http"
163+ "os"
164+
165+ "github.com/alecthomas/chroma/v2/formatters/html"
166+ "github.com/alecthomas/chroma/v2/lexers"
167+ "github.com/alecthomas/chroma/v2/styles"
168+
169+ "git.gabrielgio.me/cerrado/pkg/config"
170+ "git.gabrielgio.me/cerrado/templates"
171+)
172+
173+func ConfigFile(configPath string) func(http.ResponseWriter, *http.Request) {
174+ return func(w http.ResponseWriter, _ *http.Request) {
175+ f, err := os.Open(configPath)
176+ if err != nil {
177+ slog.Error("Error openning config file json", "error", err, "path", configPath)
178+ return
179+ }
180+
181+ c, err := config.Parse(f)
182+ if err != nil {
183+ slog.Error("Error parsing config", "error", err, "path", configPath)
184+ return
185+ }
186+
187+ b, err := json.MarshalIndent(c, "", " ")
188+ if err != nil {
189+ slog.Error("Error parsing json", "error", err)
190+ return
191+ }
192+
193+ lexer := lexers.Get("json")
194+ style := styles.Get("monokailight")
195+ formatter := html.New(
196+ html.WithLineNumbers(true),
197+ )
198+ iterator, err := lexer.Tokenise(nil, string(b))
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+ hello := &templates.HelloPage{
212+ Body: code.String(),
213+ }
214+
215+ templates.WritePageTemplate(w, hello)
216+ }
217+}
218diff --git a/pkg/service/git.go b/pkg/service/git.go
219new file mode 100644
220index 0000000000000000000000000000000000000000..0415ceeee803e69b7e73d69b5875edd6321cfffd
221--- /dev/null
222+++ b/pkg/service/git.go
223@@ -0,0 +1,30 @@
224+package service
225+
226+import "fmt"
227+
228+type (
229+ GitService struct{}
230+ Repository struct {
231+ Name string
232+ Title string
233+ Description string
234+ }
235+)
236+
237+func NewGitService() *GitService {
238+ return &GitService{}
239+}
240+
241+func (g *GitService) ListRepositories() []*Repository {
242+ repos := make([]*Repository, 10)
243+
244+ for i := range 10 {
245+ repos[i] = &Repository{
246+ Name: fmt.Sprintf("repository-%d", i),
247+ Title: fmt.Sprintf("Repository %d", i),
248+ Description: fmt.Sprintf("This is a description for repository %d", i),
249+ }
250+ }
251+
252+ return repos
253+}
254diff --git a/scss/main.scss b/scss/main.scss
255index 7273c9f36be42a2a85fa8774639400a13bb7668a..eb8a6fe7dc52e09b62850258544a966c374925d2 100644
256--- a/scss/main.scss
257+++ b/scss/main.scss
258@@ -1,4 +1,9 @@
259+//$card-border-width: 0;
260+$card-border-radius: 0;
261+$card-cap-padding-y: 0;
262+$card-cap-padding-x: 0;
263
264+// basic functionality
265 @import "bootstrap/scss/_functions.scss";
266 @import "bootstrap/scss/_variables.scss";
267 @import "bootstrap/scss/_variables-dark.scss";
268@@ -6,14 +11,16 @@ @import "bootstrap/scss/_maps.scss";
269 @import "bootstrap/scss/_mixins.scss";
270 @import "bootstrap/scss/_utilities.scss";
271
272-
273+// added component
274 @import "bootstrap/scss/_root.scss";
275 @import "bootstrap/scss/_containers.scss";
276 @import "bootstrap/scss/_nav.scss";
277 @import "bootstrap/scss/_navbar.scss";
278+@import "bootstrap/scss/_card.scss";
279+@import "bootstrap/scss/_grid.scss";
280
281
282 body {
283 font-family: $font-family-monospace;
284-
285+ margin: 0;
286 }
287diff --git a/templates/gitlist.qtpl b/templates/gitlist.qtpl
288new file mode 100644
289index 0000000000000000000000000000000000000000..10a89da1a91734e006f2ef7e191ae51bc269ec01
290--- /dev/null
291+++ b/templates/gitlist.qtpl
292@@ -0,0 +1,34 @@
293+{% import "git.gabrielgio.me/cerrado/pkg/service" %}
294+{% import "git.gabrielgio.me/cerrado/pkg/u" %}
295+
296+
297+{% code
298+type GitListPage struct {
299+ Respositories []*service.Repository
300+}
301+%}
302+
303+{% func (p *GitListPage) Title() %}Git | List{% endfunc %}
304+
305+{% func (p *GitListPage) Content() %}
306+{% for _, c := range u.ChunkBy(p.Respositories, 3) %}
307+<div class="row">
308+ {% for _, r := range c %}
309+ <div class="col-md-4 g-0">
310+ <div class="card">
311+ <div class="card-header">
312+ {%s r.Title %}
313+ </div>
314+ <div class="card-body">
315+ <p class="card-text">{%s r.Description %}</p>
316+ <a href="/{%s r.Name %}" class="btn btn-primary">go to repository</a>
317+ </div>
318+ </div>
319+ </div>
320+ {% endfor %}
321+</div>
322+{% endfor %}
323+{% endfunc %}
324+
325+{% func (p *GitListPage) Script() %}
326+{% endfunc %}
327diff --git a/templates/gitlist.qtpl.go b/templates/gitlist.qtpl.go
328new file mode 100644
329index 0000000000000000000000000000000000000000..b02eeadf929a41ad4b3621deb6f5b7cb34be5454
330--- /dev/null
331+++ b/templates/gitlist.qtpl.go
332@@ -0,0 +1,175 @@
333+// Code generated by qtc from "gitlist.qtpl". DO NOT EDIT.
334+// See https://github.com/valyala/quicktemplate for details.
335+
336+//line gitlist.qtpl:1
337+package templates
338+
339+//line gitlist.qtpl:1
340+import "git.gabrielgio.me/cerrado/pkg/service"
341+
342+//line gitlist.qtpl:2
343+import "git.gabrielgio.me/cerrado/pkg/u"
344+
345+//line gitlist.qtpl:5
346+import (
347+ qtio422016 "io"
348+
349+ qt422016 "github.com/valyala/quicktemplate"
350+)
351+
352+//line gitlist.qtpl:5
353+var (
354+ _ = qtio422016.Copy
355+ _ = qt422016.AcquireByteBuffer
356+)
357+
358+//line gitlist.qtpl:6
359+type GitListPage struct {
360+ Respositories []*service.Repository
361+}
362+
363+//line gitlist.qtpl:11
364+func (p *GitListPage) StreamTitle(qw422016 *qt422016.Writer) {
365+//line gitlist.qtpl:11
366+ qw422016.N().S(`Git | List`)
367+//line gitlist.qtpl:11
368+}
369+
370+//line gitlist.qtpl:11
371+func (p *GitListPage) WriteTitle(qq422016 qtio422016.Writer) {
372+//line gitlist.qtpl:11
373+ qw422016 := qt422016.AcquireWriter(qq422016)
374+//line gitlist.qtpl:11
375+ p.StreamTitle(qw422016)
376+//line gitlist.qtpl:11
377+ qt422016.ReleaseWriter(qw422016)
378+//line gitlist.qtpl:11
379+}
380+
381+//line gitlist.qtpl:11
382+func (p *GitListPage) Title() string {
383+//line gitlist.qtpl:11
384+ qb422016 := qt422016.AcquireByteBuffer()
385+//line gitlist.qtpl:11
386+ p.WriteTitle(qb422016)
387+//line gitlist.qtpl:11
388+ qs422016 := string(qb422016.B)
389+//line gitlist.qtpl:11
390+ qt422016.ReleaseByteBuffer(qb422016)
391+//line gitlist.qtpl:11
392+ return qs422016
393+//line gitlist.qtpl:11
394+}
395+
396+//line gitlist.qtpl:13
397+func (p *GitListPage) StreamContent(qw422016 *qt422016.Writer) {
398+//line gitlist.qtpl:13
399+ qw422016.N().S(`
400+`)
401+//line gitlist.qtpl:14
402+ for _, c := range u.ChunkBy(p.Respositories, 3) {
403+//line gitlist.qtpl:14
404+ qw422016.N().S(`
405+<div class="row">
406+ `)
407+//line gitlist.qtpl:16
408+ for _, r := range c {
409+//line gitlist.qtpl:16
410+ qw422016.N().S(`
411+ <div class="col-md-4 g-0">
412+ <div class="card">
413+ <div class="card-header">
414+ `)
415+//line gitlist.qtpl:20
416+ qw422016.E().S(r.Title)
417+//line gitlist.qtpl:20
418+ qw422016.N().S(`
419+ </div>
420+ <div class="card-body">
421+ <p class="card-text">`)
422+//line gitlist.qtpl:23
423+ qw422016.E().S(r.Description)
424+//line gitlist.qtpl:23
425+ qw422016.N().S(`</p>
426+ <a href="/`)
427+//line gitlist.qtpl:24
428+ qw422016.E().S(r.Name)
429+//line gitlist.qtpl:24
430+ qw422016.N().S(`" class="btn btn-primary">go to repository</a>
431+ </div>
432+ </div>
433+ </div>
434+ `)
435+//line gitlist.qtpl:28
436+ }
437+//line gitlist.qtpl:28
438+ qw422016.N().S(`
439+</div>
440+`)
441+//line gitlist.qtpl:30
442+ }
443+//line gitlist.qtpl:30
444+ qw422016.N().S(`
445+`)
446+//line gitlist.qtpl:31
447+}
448+
449+//line gitlist.qtpl:31
450+func (p *GitListPage) WriteContent(qq422016 qtio422016.Writer) {
451+//line gitlist.qtpl:31
452+ qw422016 := qt422016.AcquireWriter(qq422016)
453+//line gitlist.qtpl:31
454+ p.StreamContent(qw422016)
455+//line gitlist.qtpl:31
456+ qt422016.ReleaseWriter(qw422016)
457+//line gitlist.qtpl:31
458+}
459+
460+//line gitlist.qtpl:31
461+func (p *GitListPage) Content() string {
462+//line gitlist.qtpl:31
463+ qb422016 := qt422016.AcquireByteBuffer()
464+//line gitlist.qtpl:31
465+ p.WriteContent(qb422016)
466+//line gitlist.qtpl:31
467+ qs422016 := string(qb422016.B)
468+//line gitlist.qtpl:31
469+ qt422016.ReleaseByteBuffer(qb422016)
470+//line gitlist.qtpl:31
471+ return qs422016
472+//line gitlist.qtpl:31
473+}
474+
475+//line gitlist.qtpl:33
476+func (p *GitListPage) StreamScript(qw422016 *qt422016.Writer) {
477+//line gitlist.qtpl:33
478+ qw422016.N().S(`
479+`)
480+//line gitlist.qtpl:34
481+}
482+
483+//line gitlist.qtpl:34
484+func (p *GitListPage) WriteScript(qq422016 qtio422016.Writer) {
485+//line gitlist.qtpl:34
486+ qw422016 := qt422016.AcquireWriter(qq422016)
487+//line gitlist.qtpl:34
488+ p.StreamScript(qw422016)
489+//line gitlist.qtpl:34
490+ qt422016.ReleaseWriter(qw422016)
491+//line gitlist.qtpl:34
492+}
493+
494+//line gitlist.qtpl:34
495+func (p *GitListPage) Script() string {
496+//line gitlist.qtpl:34
497+ qb422016 := qt422016.AcquireByteBuffer()
498+//line gitlist.qtpl:34
499+ p.WriteScript(qb422016)
500+//line gitlist.qtpl:34
501+ qs422016 := string(qb422016.B)
502+//line gitlist.qtpl:34
503+ qt422016.ReleaseByteBuffer(qb422016)
504+//line gitlist.qtpl:34
505+ return qs422016
506+//line gitlist.qtpl:34
507+}
508diff --git a/templates/helloworld.qtpl b/templates/helloworld.qtpl
509index 66287eac78fadb51d34da5420d2700ff4b4bb511..45c5595820b5fc751efd9f83275cb86da2d11ddc 100644
510--- a/templates/helloworld.qtpl
511+++ b/templates/helloworld.qtpl
512@@ -7,8 +7,6 @@
513 {% func (p *HelloPage) Title() %}Hello{% endfunc %}
514
515 {% func (p *HelloPage) Content() %}
516-HelloWorld
517-
518 {%s= p.Body %}
519 {% endfunc %}
520
521diff --git a/templates/helloworld.qtpl.go b/templates/helloworld.qtpl.go
522index a12455fe6c09e8027a97fd8b4a1fe30179553928..61c8f75a74d772c57fd5124d9966cfdbcba1f45c 100644
523--- a/templates/helloworld.qtpl.go
524+++ b/templates/helloworld.qtpl.go
525@@ -59,73 +59,71 @@ //line helloworld.qtpl:9
526 func (p *HelloPage) StreamContent(qw422016 *qt422016.Writer) {
527 //line helloworld.qtpl:9
528 qw422016.N().S(`
529-HelloWorld
530-
531 `)
532-//line helloworld.qtpl:12
533+//line helloworld.qtpl:10
534 qw422016.N().S(p.Body)
535-//line helloworld.qtpl:12
536+//line helloworld.qtpl:10
537 qw422016.N().S(`
538 `)
539-//line helloworld.qtpl:13
540+//line helloworld.qtpl:11
541 }
542
543-//line helloworld.qtpl:13
544+//line helloworld.qtpl:11
545 func (p *HelloPage) WriteContent(qq422016 qtio422016.Writer) {
546-//line helloworld.qtpl:13
547+//line helloworld.qtpl:11
548 qw422016 := qt422016.AcquireWriter(qq422016)
549-//line helloworld.qtpl:13
550+//line helloworld.qtpl:11
551 p.StreamContent(qw422016)
552-//line helloworld.qtpl:13
553+//line helloworld.qtpl:11
554 qt422016.ReleaseWriter(qw422016)
555-//line helloworld.qtpl:13
556+//line helloworld.qtpl:11
557 }
558
559-//line helloworld.qtpl:13
560+//line helloworld.qtpl:11
561 func (p *HelloPage) Content() string {
562-//line helloworld.qtpl:13
563+//line helloworld.qtpl:11
564 qb422016 := qt422016.AcquireByteBuffer()
565-//line helloworld.qtpl:13
566+//line helloworld.qtpl:11
567 p.WriteContent(qb422016)
568-//line helloworld.qtpl:13
569+//line helloworld.qtpl:11
570 qs422016 := string(qb422016.B)
571-//line helloworld.qtpl:13
572+//line helloworld.qtpl:11
573 qt422016.ReleaseByteBuffer(qb422016)
574-//line helloworld.qtpl:13
575+//line helloworld.qtpl:11
576 return qs422016
577-//line helloworld.qtpl:13
578+//line helloworld.qtpl:11
579 }
580
581-//line helloworld.qtpl:15
582+//line helloworld.qtpl:13
583 func (p *HelloPage) StreamScript(qw422016 *qt422016.Writer) {
584-//line helloworld.qtpl:15
585+//line helloworld.qtpl:13
586 qw422016.N().S(`
587 `)
588-//line helloworld.qtpl:16
589+//line helloworld.qtpl:14
590 }
591
592-//line helloworld.qtpl:16
593+//line helloworld.qtpl:14
594 func (p *HelloPage) WriteScript(qq422016 qtio422016.Writer) {
595-//line helloworld.qtpl:16
596+//line helloworld.qtpl:14
597 qw422016 := qt422016.AcquireWriter(qq422016)
598-//line helloworld.qtpl:16
599+//line helloworld.qtpl:14
600 p.StreamScript(qw422016)
601-//line helloworld.qtpl:16
602+//line helloworld.qtpl:14
603 qt422016.ReleaseWriter(qw422016)
604-//line helloworld.qtpl:16
605+//line helloworld.qtpl:14
606 }
607
608-//line helloworld.qtpl:16
609+//line helloworld.qtpl:14
610 func (p *HelloPage) Script() string {
611-//line helloworld.qtpl:16
612+//line helloworld.qtpl:14
613 qb422016 := qt422016.AcquireByteBuffer()
614-//line helloworld.qtpl:16
615+//line helloworld.qtpl:14
616 p.WriteScript(qb422016)
617-//line helloworld.qtpl:16
618+//line helloworld.qtpl:14
619 qs422016 := string(qb422016.B)
620-//line helloworld.qtpl:16
621+//line helloworld.qtpl:14
622 qt422016.ReleaseByteBuffer(qb422016)
623-//line helloworld.qtpl:16
624+//line helloworld.qtpl:14
625 return qs422016
626-//line helloworld.qtpl:16
627+//line helloworld.qtpl:14
628 }