package main import ( "context" "crypto/subtle" "fmt" "net/http" "git.agecem.com/bottin/bottin/v10/pkg/bottin" "git.agecem.com/bottin/presences/ui" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) func RunUIServer(ctx context.Context, cfg Config, bottinClient *bottin.APIClient, dbClient *DBClient) error { select { case <-ctx.Done(): return ctx.Err() default: if bottinClient == nil { return fmt.Errorf("nil bottin client") } if dbClient == nil { return fmt.Errorf("nil dbClient") } if err := dbClient.Init(ctx); err != nil { return err } e := echo.New() r := ui.NewRenderer() if r == nil { return fmt.Errorf("nil renderer") } e.Renderer = r e.Pre(middleware.AddTrailingSlash()) // basic auth if cfg.UI.Username == "" || cfg.UI.Password == "" { return fmt.Errorf("UI username and password cannot be empty, please set UI.Password and UI.Username (PRESENCES_UI_PASSWORD and PRESENCES_UI_USERNAME") } e.Pre(middleware.BasicAuth( func(username, password string, c echo.Context) (bool, error) { userOK := subtle.ConstantTimeCompare([]byte(username), []byte(cfg.UI.Username)) == 1 passOK := subtle.ConstantTimeCompare([]byte(password), []byte(cfg.UI.Password)) == 1 if userOK && passOK { return true, nil } return false, nil }), ) e.Group("/public/js/*").Use(middleware.StaticWithConfig(middleware.StaticConfig{ Root: "/js/", Filesystem: http.FS(ui.JSFS()), })) e.GET("/", UIIndex(ctx, cfg)) e.GET("/nothing/", func(c echo.Context) error { return c.NoContent(http.StatusOK) }) e.GET("/decompte/", UICountPresences(ctx, cfg, dbClient)) e.POST("/scan/", UICreatePresence(ctx, cfg, bottinClient, dbClient)) address := fmt.Sprintf(":%d", cfg.Port) return e.StartTLS(address, cfg.TLS.Cert, cfg.TLS.Key) } }