// Copyright (C) 2021 Marius Schellenberger package server import ( "bytes" "context" "embed" "errors" "html/template" "net" "net/http" "time" "git.giftfish.de/ston1th/authdav" "git.giftfish.de/ston1th/docstore/pkg/core" "git.giftfish.de/ston1th/docstore/pkg/db" "git.giftfish.de/ston1th/docstore/pkg/fs" "git.giftfish.de/ston1th/docstore/pkg/scan" "git.giftfish.de/ston1th/godrop/v2" "git.giftfish.de/ston1th/jwt/v3" "github.com/go-logr/logr" "github.com/gorilla/mux" "golang.org/x/net/webdav" ) const ( cookieName = "docstore_session" ) type HTTPServer struct { Config *core.Config listener net.Listener srv *http.Server handler http.Handler DB *db.DB JWT *jwt.JWT Scanner *scan.Scanner FS *fs.Filesystem Log logr.Logger plain logr.Logger templ map[string]*template.Template res map[string][]byte } func NewHTTPServer(log logr.Logger, cfg *core.Config, l net.Listener, s *scan.Scanner) (srv *HTTPServer) { srv = &HTTPServer{ Config: cfg, listener: l, Scanner: s, Log: log.WithName("server"), plain: log, } srv.loadTemplates() srv.srv = &http.Server{} return } func (s *HTTPServer) Start() (err error) { log := s.Log s.FS, err = fs.NewFilesystem(s.plain.WithName("fs"), s.Config.DataDir) if err != nil { return errors.New("fs: " + err.Error()) } s.DB, err = db.New(s.plain.WithName("db"), s.Config) if err != nil { return errors.New("db: " + err.Error()) } err = godrop.UnveilBlock() if err != nil { return } if s.Config.Secret != "" { s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, bytes.NewBufferString(s.Config.Secret)) } else { s.JWT, err = jwt.New(jwt.DefaultExpiry, s.DB, nil) } if err != nil { return errors.New("server: " + err.Error()) } s.handler = s.buildRoutes() if !s.DB.UserExists() { log.V(2).Info("starting register handler") s.srv.Handler = s.register() } else { s.srv.Handler = s.handler } go func() { err = s.srv.Serve(s.listener) if err != nil { log.Error(err, "http server error") } }() return } func (s *HTTPServer) Stop() error { s.DB.Close() s.JWT.Stop() ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() return s.srv.Shutdown(ctx) } func (s *HTTPServer) register() http.Handler { r := mux.NewRouter() r.NotFoundHandler = ®isterNotFoundHandler{s} r.Handle("/static/{file}", http.StripPrefix("/", http.FileServer(http.FS(static)))) for _, v := range registerRoutes { r.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...) } return r } //go:embed static/* var static embed.FS func (s *HTTPServer) buildRoutes() http.Handler { r := mux.NewRouter() r.NotFoundHandler = ¬FoundHandler{s} if s.Config.WebDav { dav := authdav.NewWriteOnlyOnceFileSystem(webdav.Dir(s.Config.DataDir)) dav.Filters = []authdav.Filter{authdav.NewMacOSFilter()} h := authdav.NewWebdavBasicAuth( core.WebDavPrefix, dav, nil, webdavLogger(s.plain.WithName("webdav")), s.DB, "DocStore WebDav", ) r.PathPrefix(core.WebDavPrefix).Handler(h) } r.Handle("/static/{file}", http.StripPrefix("/", http.FileServer(http.FS(static)))) for _, v := range routes { r.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...) } for _, v := range prefixRoutes { r.PathPrefix(v.Path).HandlerFunc(s.contextWrapper(v.Handler)).Methods(v.Methods...) } return r } func (s *HTTPServer) contextWrapper(h ctxHandler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { h(newContext(w, r, s)) } }