package matrix
|
|
|
|
import (
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"path/filepath"
|
|
|
|
"github.com/labstack/echo/v4/middleware"
|
|
|
|
"maze.io/matrix/driver"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
var defaultServerConfig = &ServerConfig{
|
|
Listen: ":8025",
|
|
Template: "template",
|
|
Static: "static",
|
|
}
|
|
|
|
type ServerConfig struct {
|
|
Listen string
|
|
TLS bool
|
|
Certificate string
|
|
Key string
|
|
Template string
|
|
Static string
|
|
}
|
|
|
|
type Server struct {
|
|
router *echo.Echo
|
|
config *ServerConfig
|
|
loader *Loader
|
|
matrix driver.Matrix
|
|
}
|
|
|
|
func NewServer(config *ServerConfig, options ...ServerOption) (*Server, error) {
|
|
if _, err := net.ResolveTCPAddr("tcp", config.Listen); err != nil {
|
|
return nil, fmt.Errorf("matrix: invalid server listen %q: %w", config.Listen, err)
|
|
}
|
|
|
|
s := &Server{
|
|
router: echo.New(),
|
|
config: config,
|
|
}
|
|
|
|
var err error
|
|
if s.router.Renderer, err = newTemplateRenderer(config.Template); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, option := range options {
|
|
if err := option(s); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
s.routes()
|
|
return s, nil
|
|
}
|
|
|
|
func (s *Server) Start() error {
|
|
if s.config.TLS {
|
|
if s.config.Certificate == "" {
|
|
return s.router.StartAutoTLS(s.config.Listen)
|
|
}
|
|
return s.router.StartTLS(s.config.Listen, s.config.Certificate, s.config.Key)
|
|
}
|
|
return s.router.Start(s.config.Listen)
|
|
}
|
|
|
|
func (s *Server) routes() {
|
|
s.router.HideBanner = true
|
|
s.router.HidePort = true
|
|
|
|
s.router.Use(middleware.Recover())
|
|
s.router.Use(middleware.Logger())
|
|
|
|
s.router.GET("/", s.handleIndex)
|
|
s.router.GET("/animation/set/:set", s.handleAnimationSetIndex)
|
|
s.router.GET("/animation/set/:set/:name", s.handleAnimationSetAnimation)
|
|
}
|
|
|
|
func (s *Server) handleIndex(c echo.Context) error {
|
|
return c.Render(http.StatusOK, "index.html", map[string]interface{}{
|
|
"Loader": s.loader,
|
|
"Matrix": s.matrix,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleAnimationSetIndex(c echo.Context) error {
|
|
set, ok := s.loader.BySet[c.Param("set")]
|
|
if !ok {
|
|
log.Printf("server: animation set %q not found", c.Param("set"))
|
|
return c.NoContent(http.StatusNotFound)
|
|
}
|
|
return c.Render(http.StatusOK, "animation_set.html", map[string]interface{}{
|
|
"Loader": s.loader,
|
|
"Matrix": s.matrix,
|
|
"Name": c.Param("set"),
|
|
"Animations": set,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleAnimationSetAnimation(context echo.Context) error {
|
|
return nil
|
|
}
|
|
|
|
type ServerOption func(*Server) error
|
|
|
|
func WithLoader(l *Loader) ServerOption {
|
|
return func(s *Server) error {
|
|
s.loader = l
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func WithOutput(m driver.Matrix) ServerOption {
|
|
return func(s *Server) error {
|
|
s.matrix = m
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type templateRenderer struct {
|
|
t *template.Template
|
|
}
|
|
|
|
func (r templateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
|
// Add global methods if data is a map
|
|
if viewContext, isMap := data.(map[string]interface{}); isMap {
|
|
viewContext["reverse"] = c.Echo().Reverse
|
|
}
|
|
return r.t.ExecuteTemplate(w, name, data)
|
|
}
|
|
|
|
func newTemplateRenderer(path string) (echo.Renderer, error) {
|
|
t, err := template.ParseGlob(filepath.Join(path, "*.html"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return templateRenderer{
|
|
t: t,
|
|
}, nil
|
|
}
|