package control import ( "context" "html/template" "net/http" "strings" "time" _ "embed" "github.com/FAU-CDI/wisski-distillery/internal/component" "github.com/FAU-CDI/wisski-distillery/internal/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/config" "github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/tkw1536/goprogram/stream" "golang.org/x/sync/errgroup" ) type Info struct { component.ComponentBase Instances *instances.Instances } func (Info) Name() string { return "control-info" } func (*Info) Routes() []string { return []string{"/dis/"} } func (info *Info) Handler(route string, context context.Context, io stream.IOStream) (http.Handler, error) { mux := http.NewServeMux() // handle everything under /dis/! mux.HandleFunc("/dis/", func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/dis/" { http.Redirect(w, r, "/dis/index", http.StatusTemporaryRedirect) return } http.NotFound(w, r) }) // render everything mux.Handle("/dis/index", httpx.HTMLHandler[disIndex]{ Handler: info.disIndex, Template: indexTemplate, }) mux.Handle("/dis/instance/", httpx.HTMLHandler[disInstance]{ Handler: info.disInstance, Template: instanceTemplate, }) // api -- for future usage mux.Handle("/dis/api/v1/instance/get/", httpx.JSON(info.getinstance)) mux.Handle("/dis/api/v1/instance/all", httpx.JSON(info.allinstances)) // ensure that everyone is logged in! return httpx.BasicAuth(mux, "WissKI Distillery Admin", func(user, pass string) bool { return user == info.Config.DisAdminUser && pass == info.Config.DisAdminPassword }), nil } // disIndex is the context of the "/dis/index" page type disIndex struct { Time time.Time Config *config.Config Instances []instances.WissKIInfo TotalCount int RunningCount int StoppedCount int Backups []models.Snapshot } func (info *Info) disIndex(r *http.Request) (idx disIndex, err error) { var group errgroup.Group group.Go(func() error { // load instances idx.Instances, err = info.allinstances(r) if err != nil { return err } // count how many are running and how many are stopped for _, i := range idx.Instances { if i.Running { idx.RunningCount++ } else { idx.StoppedCount++ } } idx.TotalCount = len(idx.Instances) return nil }) // get the log entries group.Go(func() (err error) { idx.Backups, err = info.Instances.SnapshotLogFor("") return }) // get the static properties idx.Config = info.Config // current time idx.Time = time.Now().UTC() // wait for everything! group.Wait() return } // disInstance is the context of the "/dis/instance/*" page type disInstance struct { Time time.Time Instance models.Instance Info instances.WissKIInfo } func (info *Info) disInstance(r *http.Request) (is disInstance, err error) { // find the slug as the last component of path! slug := strings.TrimSuffix(r.URL.Path, "/") slug = slug[strings.LastIndex(slug, "/")+1:] // find the instance itself! instance, err := info.Instances.WissKI(slug) if err == instances.ErrWissKINotFound { return is, httpx.ErrNotFound } if err != nil { return is, err } is.Instance = instance.Instance // get some more info about the wisski is.Info, err = instance.Info(false) if err != nil { return is, err } // current time is.Time = time.Now().UTC() return } //go:embed "html/index.html" var indexTemplateStr string var indexTemplate = template.Must(template.New("index.html").Parse(indexTemplateStr)) //go:embed "html/instance.html" var instanceTemplateString string var instanceTemplate = template.Must(template.New("instance.html").Parse(instanceTemplateString)) func (info *Info) getinstance(r *http.Request) (iinfo instances.WissKIInfo, err error) { // find the slug as the last component of path! slug := strings.TrimSuffix(r.URL.Path, "/") slug = slug[strings.LastIndex(slug, "/")+1:] // load the wisski instance! wisski, err := info.Instances.WissKI(strings.TrimSuffix(slug, "/")) if err == instances.ErrWissKINotFound { return iinfo, httpx.ErrNotFound } if err != nil { return iinfo, err } // get info about it! return wisski.Info(false) } func (info *Info) allinstances(*http.Request) (infos []instances.WissKIInfo, err error) { var errgroup errgroup.Group // list all the instances all, err := info.Instances.All() if err != nil { return nil, err } // get all of their info! infos = make([]instances.WissKIInfo, len(all)) for i, instance := range all { { i := i instance := instance errgroup.Go(func() (err error) { infos[i], err = instance.Info(true) return err }) } } // wait for the results, and return err = errgroup.Wait() return }