131 lines
2.4 KiB
Go
131 lines
2.4 KiB
Go
package http
|
|
|
|
import (
|
|
"bytes"
|
|
"embed"
|
|
"html/template"
|
|
"io"
|
|
"io/fs"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
_ "embed"
|
|
|
|
"github.com/dschmidt/go-layerfs"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
//go:embed templates
|
|
var embeddedTemplates embed.FS
|
|
|
|
//go:embed assets
|
|
var embeddedAssets embed.FS
|
|
|
|
type Server struct {
|
|
http *http.Server
|
|
opts *Options
|
|
templates template.Template
|
|
}
|
|
|
|
func (s *Server) serveHomepage(w http.ResponseWriter, r *http.Request) {
|
|
data := struct {
|
|
Title string
|
|
}{
|
|
Title: "Rebound",
|
|
}
|
|
|
|
s.renderTemplate(w, "index", data)
|
|
}
|
|
|
|
func (s *Server) Serve(l net.Listener) error {
|
|
templatesFilesystem, err := s.getCustomizedFilesystem(embeddedTemplates)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if err := s.parseTemplates(templatesFilesystem); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
assetsFilesystem, err := s.getCustomizedFilesystem(embeddedAssets)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.Handle("/assets/", http.FileServer(http.FS(assetsFilesystem)))
|
|
mux.HandleFunc("/", s.serveHomepage)
|
|
|
|
httpServer := &http.Server{
|
|
Handler: mux,
|
|
}
|
|
|
|
s.http = httpServer
|
|
|
|
if err := s.http.Serve(l); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) parseTemplates(fs fs.FS) error {
|
|
templates, err := template.ParseFS(fs, "templates/*.html")
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
s.templates = *templates
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) renderTemplate(w http.ResponseWriter, name string, data any) {
|
|
var buff bytes.Buffer
|
|
|
|
if err := s.templates.ExecuteTemplate(&buff, name, data); err != nil {
|
|
s.log("[ERROR] %+s", errors.WithStack(err))
|
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if _, err := io.Copy(w, &buff); err != nil {
|
|
s.log("[ERROR] %+s", errors.WithStack(err))
|
|
}
|
|
}
|
|
|
|
func (s *Server) log(message string, args ...any) {
|
|
s.opts.Logger(message, args...)
|
|
}
|
|
|
|
func (s *Server) getCustomizedFilesystem(base fs.FS) (fs.FS, error) {
|
|
filesystems := []fs.FS{}
|
|
|
|
if s.opts.CustomDir != "" {
|
|
absPath, err := filepath.Abs(s.opts.CustomDir)
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
filesystems = append(filesystems, os.DirFS(absPath))
|
|
}
|
|
|
|
filesystems = append(filesystems, base)
|
|
|
|
return layerfs.New(filesystems...), nil
|
|
}
|
|
|
|
func NewServer(funcs ...OptionFunc) *Server {
|
|
opts := DefaultOptions()
|
|
for _, fn := range funcs {
|
|
fn(opts)
|
|
}
|
|
|
|
return &Server{
|
|
opts: opts,
|
|
}
|
|
}
|