forked from
tangled.org/core
this repo has no description
1package knotmirror
2
3import (
4 "database/sql"
5 "embed"
6 "html/template"
7 "log/slog"
8 "net/http"
9 "strconv"
10 "time"
11
12 "github.com/go-chi/chi/v5"
13 "tangled.org/core/appview/pagination"
14 "tangled.org/core/knotmirror/db"
15 "tangled.org/core/knotmirror/models"
16)
17
18//go:embed templates/*.html
19var templateFS embed.FS
20
21const repoPageSize = 20
22
23type AdminServer struct {
24 db *sql.DB
25}
26
27func NewAdminServer(database *sql.DB) *AdminServer {
28 return &AdminServer{db: database}
29}
30
31func (s *AdminServer) Router() http.Handler {
32 r := chi.NewRouter()
33 r.Get("/repos", s.handleRepos())
34 r.Get("/hosts", s.handleHosts())
35 return r
36}
37
38func funcmap() template.FuncMap {
39 return template.FuncMap{
40 "add": func(a, b int) int { return a + b },
41 "sub": func(a, b int) int { return a - b },
42 "readt": func(ts int64) string {
43 if ts == 0 {
44 return "n/a"
45 }
46 return time.Unix(ts, 0).Format("2006-01-02 15:04")
47 },
48 }
49}
50
51func (s *AdminServer) handleRepos() http.HandlerFunc {
52 tpl := template.Must(template.New("").Funcs(funcmap()).ParseFS(templateFS, "templates/base.html", "templates/repos.html"))
53 return func(w http.ResponseWriter, r *http.Request) {
54 pageNum, _ := strconv.Atoi(r.URL.Query().Get("page"))
55 if pageNum < 1 {
56 pageNum = 1
57 }
58 page := pagination.Page{
59 Offset: (pageNum - 1) * repoPageSize,
60 Limit: repoPageSize,
61 }
62
63 var (
64 did = r.URL.Query().Get("did")
65 knot = r.URL.Query().Get("knot")
66 state = r.URL.Query().Get("state")
67 )
68
69 repos, err := db.ListRepos(r.Context(), s.db, page, did, knot, state)
70 if err != nil {
71 http.Error(w, err.Error(), http.StatusInternalServerError)
72 }
73 counts, err := db.GetRepoCountsByState(r.Context(), s.db)
74 if err != nil {
75 http.Error(w, err.Error(), http.StatusInternalServerError)
76 }
77 err = tpl.ExecuteTemplate(w, "base", map[string]any{
78 "Repos": repos,
79 "RepoCounts": counts,
80 "Page": pageNum,
81 "FilterByDid": did,
82 "FilterByKnot": knot,
83 "FilterByState": models.RepoState(state),
84 })
85 if err != nil {
86 slog.Error("failed to render", "err", err)
87 }
88 }
89}
90
91func (s *AdminServer) handleHosts() http.HandlerFunc {
92 tpl := template.Must(template.New("").Funcs(funcmap()).ParseFS(templateFS, "templates/base.html", "templates/hosts.html"))
93 return func(w http.ResponseWriter, r *http.Request) {
94 var status = r.URL.Query().Get("status")
95
96 hosts, err := db.ListHosts(r.Context(), s.db, models.HostStatus(status))
97 if err != nil {
98 http.Error(w, err.Error(), http.StatusInternalServerError)
99 }
100 err = tpl.ExecuteTemplate(w, "base", map[string]any{
101 "Hosts": hosts,
102 "FilterByStatus": models.HostStatus(status),
103 })
104 if err != nil {
105 slog.Error("failed to render", "err", err)
106 }
107 }
108}