docstore/pkg/server/server.go
2025-10-15 22:51:08 +02:00

159 lines
3.7 KiB
Go

// 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
SQ *ScanQueue
Log logr.Logger
plain logr.Logger
cancel func()
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) {
ctx, cancel := context.WithCancel(context.Background())
srv = &HTTPServer{
Config: cfg,
listener: l,
Scanner: s,
Log: log.WithName("server"),
plain: log,
cancel: cancel,
}
srv.SQ = NewScanQueue(ctx, srv, 100)
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
}
opts := []jwt.JWTOption{jwt.WithNonce(), jwt.WithBlacklist(s.DB)}
if s.Config.Secret != "" {
opts = append(opts, jwt.WithSecret(bytes.NewBufferString(s.Config.Secret)))
}
s.JWT, err = jwt.New(opts...)
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 s.SQ.Scan()
go func() {
err = s.srv.Serve(s.listener)
if err != nil && err != http.ErrServerClosed {
log.Error(err, "http server error")
}
}()
return
}
func (s *HTTPServer) Stop() error {
s.cancel()
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 = &registerNotFoundHandler{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 = &notFoundHandler{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))
}
}