package knotmirror import ( "database/sql" "embed" "html/template" "log/slog" "net/http" "strconv" "time" "github.com/go-chi/chi/v5" "tangled.org/core/appview/pagination" "tangled.org/core/knotmirror/db" "tangled.org/core/knotmirror/models" ) //go:embed templates/*.html var templateFS embed.FS const repoPageSize = 20 type AdminServer struct { db *sql.DB } func NewAdminServer(database *sql.DB) *AdminServer { return &AdminServer{db: database} } func (s *AdminServer) Router() http.Handler { r := chi.NewRouter() r.Get("/repos", s.handleRepos()) r.Get("/hosts", s.handleHosts()) return r } func funcmap() template.FuncMap { return template.FuncMap{ "add": func(a, b int) int { return a + b }, "sub": func(a, b int) int { return a - b }, "readt": func(ts int64) string { if ts == 0 { return "n/a" } return time.Unix(ts, 0).Format("2006-01-02 15:04") }, "const": func() map[string]any { return map[string]any{ "AllRepoStates": models.AllRepoStates, "AllHostStatuses": models.AllHostStatuses, } }, } } func (s *AdminServer) handleRepos() http.HandlerFunc { tpl := template.Must(template.New("").Funcs(funcmap()).ParseFS(templateFS, "templates/base.html", "templates/repos.html")) return func(w http.ResponseWriter, r *http.Request) { pageNum, _ := strconv.Atoi(r.URL.Query().Get("page")) if pageNum < 1 { pageNum = 1 } page := pagination.Page{ Offset: (pageNum - 1) * repoPageSize, Limit: repoPageSize, } var ( did = r.URL.Query().Get("did") knot = r.URL.Query().Get("knot") state = r.URL.Query().Get("state") ) repos, err := db.ListRepos(r.Context(), s.db, page, did, knot, state) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } counts, err := db.GetRepoCountsByState(r.Context(), s.db) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } err = tpl.ExecuteTemplate(w, "base", map[string]any{ "Repos": repos, "RepoCounts": counts, "Page": pageNum, "FilterByDid": did, "FilterByKnot": knot, "FilterByState": models.RepoState(state), }) if err != nil { slog.Error("failed to render", "err", err) } } } func (s *AdminServer) handleHosts() http.HandlerFunc { tpl := template.Must(template.New("").Funcs(funcmap()).ParseFS(templateFS, "templates/base.html", "templates/hosts.html")) return func(w http.ResponseWriter, r *http.Request) { var status = r.URL.Query().Get("status") hosts, err := db.ListHosts(r.Context(), s.db, models.HostStatus(status)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } err = tpl.ExecuteTemplate(w, "base", map[string]any{ "Hosts": hosts, "FilterByStatus": models.HostStatus(status), }) if err != nil { slog.Error("failed to render", "err", err) } } }