Manager refactor, part 1 (#4298)

* Move BackupDatabase and AnonymiseDatabase to internal/manager
* Rename config.Instance to config.Config
* Rename FFMPEG
* Rework manager and initialization process
* Fix Makefile
* Tweak phasher
* Fix config races
* Fix setup error not clearing
This commit is contained in:
DingDongSoLong4
2023-11-28 04:56:46 +02:00
committed by GitHub
parent fc1fc20df4
commit b78771dbcd
45 changed files with 1230 additions and 1213 deletions

View File

@@ -505,19 +505,10 @@ func (r *mutationResolver) ConfigureDlna(ctx context.Context, input ConfigDLNAIn
c.Set(config.DLNAVideoSortOrder, input.VideoSortOrder)
}
currentDLNAEnabled := c.GetDLNADefaultEnabled()
if input.Enabled != nil && *input.Enabled != currentDLNAEnabled {
refresh := false
if input.Enabled != nil {
c.Set(config.DLNADefaultEnabled, *input.Enabled)
// start/stop the DLNA service as needed
dlnaService := manager.GetInstance().DLNAService
if !*input.Enabled && dlnaService.IsRunning() {
dlnaService.Stop(nil)
} else if *input.Enabled && !dlnaService.IsRunning() {
if err := dlnaService.Start(nil); err != nil {
logger.Warnf("error starting DLNA service: %v", err)
}
}
refresh = true
}
if input.Interfaces != nil {
@@ -528,6 +519,10 @@ func (r *mutationResolver) ConfigureDlna(ctx context.Context, input ConfigDLNAIn
return makeConfigDLNAResult(), err
}
if refresh {
manager.GetInstance().RefreshDLNA()
}
return makeConfigDLNAResult(), nil
}

View File

@@ -3,8 +3,6 @@ package api
import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"sync"
"time"
@@ -12,7 +10,6 @@ import (
"github.com/stashapp/stash/internal/identify"
"github.com/stashapp/stash/internal/manager"
"github.com/stashapp/stash/internal/manager/config"
"github.com/stashapp/stash/pkg/fsutil"
"github.com/stashapp/stash/pkg/logger"
)
@@ -110,31 +107,10 @@ func (r *mutationResolver) BackupDatabase(ctx context.Context, input BackupDatab
// if download is true, then backup to temporary file and return a link
download := input.Download != nil && *input.Download
mgr := manager.GetInstance()
database := mgr.Database
var backupPath string
if download {
if err := fsutil.EnsureDir(mgr.Paths.Generated.Downloads); err != nil {
return nil, fmt.Errorf("could not create backup directory %v: %w", mgr.Paths.Generated.Downloads, err)
}
f, err := os.CreateTemp(mgr.Paths.Generated.Downloads, "backup*.sqlite")
if err != nil {
return nil, err
}
backupPath = f.Name()
f.Close()
} else {
backupDirectoryPath := mgr.Config.GetBackupDirectoryPathOrDefault()
if backupDirectoryPath != "" {
if err := fsutil.EnsureDir(backupDirectoryPath); err != nil {
return nil, fmt.Errorf("could not create backup directory %v: %w", backupDirectoryPath, err)
}
}
backupPath = database.DatabaseBackupPath(backupDirectoryPath)
}
err := database.Backup(backupPath)
backupPath, backupName, err := mgr.BackupDatabase(download)
if err != nil {
logger.Errorf("Error backing up database: %v", err)
return nil, err
}
@@ -147,8 +123,7 @@ func (r *mutationResolver) BackupDatabase(ctx context.Context, input BackupDatab
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
fn := filepath.Base(database.DatabaseBackupPath(""))
ret := baseURL + "/downloads/" + downloadHash + "/" + fn
ret := baseURL + "/downloads/" + downloadHash + "/" + backupName
return &ret, nil
} else {
logger.Infof("Successfully backed up database to: %s", backupPath)
@@ -158,33 +133,11 @@ func (r *mutationResolver) BackupDatabase(ctx context.Context, input BackupDatab
}
func (r *mutationResolver) AnonymiseDatabase(ctx context.Context, input AnonymiseDatabaseInput) (*string, error) {
// if download is true, then backup to temporary file and return a link
// if download is true, then save to temporary file and return a link
download := input.Download != nil && *input.Download
mgr := manager.GetInstance()
database := mgr.Database
var outPath string
if download {
if err := fsutil.EnsureDir(mgr.Paths.Generated.Downloads); err != nil {
return nil, fmt.Errorf("could not create backup directory %v: %w", mgr.Paths.Generated.Downloads, err)
}
f, err := os.CreateTemp(mgr.Paths.Generated.Downloads, "anonymous*.sqlite")
if err != nil {
return nil, err
}
outPath = f.Name()
f.Close()
} else {
backupDirectoryPath := mgr.Config.GetBackupDirectoryPathOrDefault()
if backupDirectoryPath != "" {
if err := fsutil.EnsureDir(backupDirectoryPath); err != nil {
return nil, fmt.Errorf("could not create backup directory %v: %w", backupDirectoryPath, err)
}
}
outPath = database.AnonymousDatabasePath(backupDirectoryPath)
}
err := database.Anonymise(outPath)
outPath, outName, err := mgr.AnonymiseDatabase(download)
if err != nil {
logger.Errorf("Error anonymising database: %v", err)
return nil, err
@@ -199,8 +152,7 @@ func (r *mutationResolver) AnonymiseDatabase(ctx context.Context, input Anonymis
baseURL, _ := ctx.Value(BaseURLCtxKey).(string)
fn := filepath.Base(database.DatabaseBackupPath(""))
ret := baseURL + "/downloads/" + downloadHash + "/" + fn
ret := baseURL + "/downloads/" + downloadHash + "/" + outName
return &ret, nil
} else {
logger.Infof("Successfully anonymised database to: %s", outPath)

View File

@@ -6,7 +6,6 @@ import (
"github.com/stashapp/stash/internal/manager"
"github.com/stashapp/stash/internal/manager/task"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
)
@@ -14,13 +13,9 @@ func refreshPackageType(typeArg PackageType) {
mgr := manager.GetInstance()
if typeArg == PackageTypePlugin {
if err := mgr.PluginCache.LoadPlugins(); err != nil {
logger.Errorf("Error reading plugin configs: %v", err)
}
mgr.RefreshPluginCache()
} else if typeArg == PackageTypeScraper {
if err := mgr.ScraperCache.ReloadScrapers(); err != nil {
logger.Errorf("Error reading scraper configs: %v", err)
}
mgr.RefreshScraperCache()
}
}

View File

@@ -5,7 +5,6 @@ import (
"github.com/stashapp/stash/internal/manager"
"github.com/stashapp/stash/internal/manager/config"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/plugin"
"github.com/stashapp/stash/pkg/sliceutil"
)
@@ -17,11 +16,7 @@ func (r *mutationResolver) RunPluginTask(ctx context.Context, pluginID string, t
}
func (r *mutationResolver) ReloadPlugins(ctx context.Context) (bool, error) {
err := manager.GetInstance().PluginCache.LoadPlugins()
if err != nil {
logger.Errorf("Error reading plugin configs: %v", err)
}
manager.GetInstance().RefreshPluginCache()
return true, nil
}

View File

@@ -7,11 +7,6 @@ import (
)
func (r *mutationResolver) ReloadScrapers(ctx context.Context) (bool, error) {
err := manager.GetInstance().ScraperCache.ReloadScrapers()
if err != nil {
return false, err
}
manager.GetInstance().RefreshScraperCache()
return true, nil
}

View File

@@ -11,10 +11,6 @@ import (
type downloadsRoutes struct{}
func getDownloadsRoutes() chi.Router {
return downloadsRoutes{}.Routes()
}
func (rs downloadsRoutes) Routes() chi.Router {
r := chi.NewRouter()

View File

@@ -31,14 +31,6 @@ type imageRoutes struct {
fileGetter models.FileGetter
}
func getImageRoutes(repo models.Repository) chi.Router {
return imageRoutes{
routes: routes{txnManager: repo.TxnManager},
imageFinder: repo.Image,
fileGetter: repo.File,
}.Routes()
}
func (rs imageRoutes) Routes() chi.Router {
r := chi.NewRouter()
@@ -76,7 +68,7 @@ func (rs imageRoutes) Thumbnail(w http.ResponseWriter, r *http.Request) {
Preset: manager.GetInstance().Config.GetPreviewPreset().String(),
}
encoder := image.NewThumbnailEncoder(manager.GetInstance().FFMPEG, manager.GetInstance().FFProbe, clipPreviewOptions)
encoder := image.NewThumbnailEncoder(manager.GetInstance().FFMpeg, manager.GetInstance().FFProbe, clipPreviewOptions)
data, err := encoder.GetThumbnail(f, models.DefaultGthumbWidth)
if err != nil {
// don't log for unsupported image format

View File

@@ -25,13 +25,6 @@ type movieRoutes struct {
movieFinder MovieFinder
}
func getMovieRoutes(repo models.Repository) chi.Router {
return movieRoutes{
routes: routes{txnManager: repo.TxnManager},
movieFinder: repo.Movie,
}.Routes()
}
func (rs movieRoutes) Routes() chi.Router {
r := chi.NewRouter()

View File

@@ -23,13 +23,6 @@ type performerRoutes struct {
performerFinder PerformerFinder
}
func getPerformerRoutes(repo models.Repository) chi.Router {
return performerRoutes{
routes: routes{txnManager: repo.TxnManager},
performerFinder: repo.Performer,
}.Routes()
}
func (rs performerRoutes) Routes() chi.Router {
r := chi.NewRouter()

View File

@@ -15,12 +15,6 @@ type pluginRoutes struct {
pluginCache *plugin.Cache
}
func getPluginRoutes(pluginCache *plugin.Cache) chi.Router {
return pluginRoutes{
pluginCache: pluginCache,
}.Routes()
}
func (rs pluginRoutes) Routes() chi.Router {
r := chi.NewRouter()

View File

@@ -51,17 +51,6 @@ type sceneRoutes struct {
tagFinder SceneMarkerTagFinder
}
func getSceneRoutes(repo models.Repository) chi.Router {
return sceneRoutes{
routes: routes{txnManager: repo.TxnManager},
sceneFinder: repo.Scene,
fileGetter: repo.File,
captionFinder: repo.File,
sceneMarkerFinder: repo.SceneMarker,
tagFinder: repo.Tag,
}.Routes()
}
func (rs sceneRoutes) Routes() chi.Router {
r := chi.NewRouter()

View File

@@ -24,13 +24,6 @@ type studioRoutes struct {
studioFinder StudioFinder
}
func getStudioRoutes(repo models.Repository) chi.Router {
return studioRoutes{
routes: routes{txnManager: repo.TxnManager},
studioFinder: repo.Studio,
}.Routes()
}
func (rs studioRoutes) Routes() chi.Router {
r := chi.NewRouter()

View File

@@ -24,13 +24,6 @@ type tagRoutes struct {
tagFinder TagFinder
}
func getTagRoutes(repo models.Repository) chi.Router {
return tagRoutes{
routes: routes{txnManager: repo.TxnManager},
tagFinder: repo.Tag,
}.Routes()
}
func (rs tagRoutes) Routes() chi.Router {
r := chi.NewRouter()

View File

@@ -46,25 +46,65 @@ const (
playgroundEndpoint = "/playground"
)
var uiBox = ui.UIBox
var loginUIBox = ui.LoginUIBox
type Server struct {
http.Server
displayAddress string
func Start() error {
c := config.GetInstance()
manager *manager.Manager
}
initCustomPerformerImages(c.GetCustomPerformerImageLocation())
// Called at startup
func Initialize() (*Server, error) {
mgr := manager.GetInstance()
cfg := mgr.Config
initCustomPerformerImages(cfg.GetCustomPerformerImageLocation())
displayHost := cfg.GetHost()
if displayHost == "0.0.0.0" {
displayHost = "localhost"
}
displayAddress := displayHost + ":" + strconv.Itoa(cfg.GetPort())
address := cfg.GetHost() + ":" + strconv.Itoa(cfg.GetPort())
tlsConfig, err := makeTLSConfig(cfg)
if err != nil {
// assume we don't want to start with a broken TLS configuration
return nil, fmt.Errorf("error loading TLS config: %v", err)
}
if tlsConfig != nil {
displayAddress = "https://" + displayAddress + "/"
} else {
displayAddress = "http://" + displayAddress + "/"
}
r := chi.NewRouter()
server := &Server{
Server: http.Server{
Addr: address,
Handler: r,
TLSConfig: tlsConfig,
// disable http/2 support by default
// when http/2 is enabled, we are unable to hijack and close
// the connection/request. This is necessary to stop running
// streams when deleting a scene file.
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
},
displayAddress: displayAddress,
manager: mgr,
}
r.Use(middleware.Heartbeat("/healthz"))
r.Use(cors.AllowAll().Handler)
r.Use(authenticateHandler())
visitedPluginHandler := manager.GetInstance().SessionStore.VisitedPluginHandler()
visitedPluginHandler := mgr.SessionStore.VisitedPluginHandler()
r.Use(visitedPluginHandler)
r.Use(middleware.Recoverer)
if c.GetLogAccess() {
if cfg.GetLogAccess() {
httpLogger := httplog.NewLogger("Stash", httplog.Options{
Concise: true,
})
@@ -83,7 +123,7 @@ func Start() error {
return errors.New(message)
}
repo := manager.GetInstance().Repository
repo := mgr.Repository
dataloaders := loaders.Middleware{
Repository: repo,
@@ -91,10 +131,10 @@ func Start() error {
r.Use(dataloaders.Middleware)
pluginCache := manager.GetInstance().PluginCache
sceneService := manager.GetInstance().SceneService
imageService := manager.GetInstance().ImageService
galleryService := manager.GetInstance().GalleryService
pluginCache := mgr.PluginCache
sceneService := mgr.SceneService
imageService := mgr.ImageService
galleryService := mgr.GalleryService
resolver := &Resolver{
repository: repo,
sceneService: sceneService,
@@ -117,7 +157,7 @@ func Start() error {
gqlSrv.AddTransport(gqlTransport.GET{})
gqlSrv.AddTransport(gqlTransport.POST{})
gqlSrv.AddTransport(gqlTransport.MultipartForm{
MaxUploadSize: c.GetMaxUploadSize(),
MaxUploadSize: cfg.GetMaxUploadSize(),
})
gqlSrv.SetQueryCache(gqlLru.New(1000))
@@ -134,7 +174,7 @@ func Start() error {
// chain the visited plugin handler
// also requires the dataloader middleware
gqlHandler := visitedPluginHandler(dataloaders.Middleware(http.HandlerFunc(gqlHandlerFunc)))
manager.GetInstance().PluginCache.RegisterGQLHandler(gqlHandler)
pluginCache.RegisterGQLHandler(gqlHandler)
r.HandleFunc(gqlEndpoint, gqlHandlerFunc)
r.HandleFunc(playgroundEndpoint, func(w http.ResponseWriter, r *http.Request) {
@@ -143,23 +183,23 @@ func Start() error {
gqlPlayground.Handler("GraphQL playground", endpoint)(w, r)
})
r.Mount("/performer", getPerformerRoutes(repo))
r.Mount("/scene", getSceneRoutes(repo))
r.Mount("/image", getImageRoutes(repo))
r.Mount("/studio", getStudioRoutes(repo))
r.Mount("/movie", getMovieRoutes(repo))
r.Mount("/tag", getTagRoutes(repo))
r.Mount("/downloads", getDownloadsRoutes())
r.Mount("/plugin", getPluginRoutes(pluginCache))
r.Mount("/performer", server.getPerformerRoutes())
r.Mount("/scene", server.getSceneRoutes())
r.Mount("/image", server.getImageRoutes())
r.Mount("/studio", server.getStudioRoutes())
r.Mount("/movie", server.getMovieRoutes())
r.Mount("/tag", server.getTagRoutes())
r.Mount("/downloads", server.getDownloadsRoutes())
r.Mount("/plugin", server.getPluginRoutes())
r.HandleFunc("/css", cssHandler(c))
r.HandleFunc("/javascript", javascriptHandler(c))
r.HandleFunc("/customlocales", customLocalesHandler(c))
r.HandleFunc("/css", cssHandler(cfg))
r.HandleFunc("/javascript", javascriptHandler(cfg))
r.HandleFunc("/customlocales", customLocalesHandler(cfg))
staticLoginUI := statigz.FileServer(loginUIBox.(fs.ReadDirFS))
staticLoginUI := statigz.FileServer(ui.LoginUIBox.(fs.ReadDirFS))
r.Get(loginEndpoint, handleLogin(loginUIBox))
r.Post(loginEndpoint, handleLoginPost(loginUIBox))
r.Get(loginEndpoint, handleLogin())
r.Post(loginEndpoint, handleLoginPost())
r.Get(logoutEndpoint, handleLogout())
r.HandleFunc(loginEndpoint+"/*", func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.TrimPrefix(r.URL.Path, loginEndpoint)
@@ -168,13 +208,13 @@ func Start() error {
})
// Serve static folders
customServedFolders := c.GetCustomServedFolders()
customServedFolders := cfg.GetCustomServedFolders()
if customServedFolders != nil {
r.Mount("/custom", getCustomRoutes(customServedFolders))
}
customUILocation := c.GetCustomUILocation()
staticUI := statigz.FileServer(uiBox.(fs.ReadDirFS))
customUILocation := cfg.GetCustomUILocation()
staticUI := statigz.FileServer(ui.UIBox.(fs.ReadDirFS))
// Serve the web app
r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
@@ -190,8 +230,8 @@ func Start() error {
}
if ext == ".html" || ext == "" {
themeColor := c.GetThemeColor()
data, err := fs.ReadFile(uiBox, "index.html")
themeColor := cfg.GetThemeColor()
data, err := fs.ReadFile(ui.UIBox, "index.html")
if err != nil {
panic(err)
}
@@ -217,51 +257,91 @@ func Start() error {
}
})
displayHost := c.GetHost()
if displayHost == "0.0.0.0" {
displayHost = "localhost"
}
displayAddress := displayHost + ":" + strconv.Itoa(c.GetPort())
address := c.GetHost() + ":" + strconv.Itoa(c.GetPort())
tlsConfig, err := makeTLSConfig(c)
if err != nil {
// assume we don't want to start with a broken TLS configuration
panic(fmt.Errorf("error loading TLS config: %v", err))
}
server := &http.Server{
Addr: address,
Handler: r,
TLSConfig: tlsConfig,
// disable http/2 support by default
// when http/2 is enabled, we are unable to hijack and close
// the connection/request. This is necessary to stop running
// streams when deleting a scene file.
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
}
logger.Infof("stash version: %s\n", build.VersionString())
logger.Infof("stash version: %s", build.VersionString())
go printLatestVersion(context.TODO())
logger.Infof("stash is listening on " + address)
if tlsConfig != nil {
displayAddress = "https://" + displayAddress + "/"
return server, nil
}
func (s *Server) Start() error {
logger.Infof("stash is listening on " + s.Addr)
logger.Infof("stash is running at " + s.displayAddress)
if s.TLSConfig != nil {
return s.ListenAndServeTLS("", "")
} else {
displayAddress = "http://" + displayAddress + "/"
return s.ListenAndServe()
}
}
logger.Infof("stash is running at " + displayAddress)
if tlsConfig != nil {
err = server.ListenAndServeTLS("", "")
} else {
err = server.ListenAndServe()
func (s *Server) Shutdown() {
err := s.Server.Shutdown(context.TODO())
if err != nil {
logger.Errorf("Error shutting down http server: %v", err)
}
}
if !errors.Is(err, http.ErrServerClosed) {
return err
}
func (s *Server) getPerformerRoutes() chi.Router {
repo := s.manager.Repository
return performerRoutes{
routes: routes{txnManager: repo.TxnManager},
performerFinder: repo.Performer,
}.Routes()
}
return nil
func (s *Server) getSceneRoutes() chi.Router {
repo := s.manager.Repository
return sceneRoutes{
routes: routes{txnManager: repo.TxnManager},
sceneFinder: repo.Scene,
fileGetter: repo.File,
captionFinder: repo.File,
sceneMarkerFinder: repo.SceneMarker,
tagFinder: repo.Tag,
}.Routes()
}
func (s *Server) getImageRoutes() chi.Router {
repo := s.manager.Repository
return imageRoutes{
routes: routes{txnManager: repo.TxnManager},
imageFinder: repo.Image,
fileGetter: repo.File,
}.Routes()
}
func (s *Server) getStudioRoutes() chi.Router {
repo := s.manager.Repository
return studioRoutes{
routes: routes{txnManager: repo.TxnManager},
studioFinder: repo.Studio,
}.Routes()
}
func (s *Server) getMovieRoutes() chi.Router {
repo := s.manager.Repository
return movieRoutes{
routes: routes{txnManager: repo.TxnManager},
movieFinder: repo.Movie,
}.Routes()
}
func (s *Server) getTagRoutes() chi.Router {
repo := s.manager.Repository
return tagRoutes{
routes: routes{txnManager: repo.TxnManager},
tagFinder: repo.Tag,
}.Routes()
}
func (s *Server) getDownloadsRoutes() chi.Router {
return downloadsRoutes{}.Routes()
}
func (s *Server) getPluginRoutes() chi.Router {
return pluginRoutes{
pluginCache: s.manager.PluginCache,
}.Routes()
}
func copyFile(w io.Writer, path string) error {
@@ -290,7 +370,7 @@ func serveFiles(w http.ResponseWriter, r *http.Request, paths []string) {
utils.ServeStaticContent(w, r, buffer.Bytes())
}
func cssHandler(c *config.Instance) func(w http.ResponseWriter, r *http.Request) {
func cssHandler(c *config.Config) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var paths []string
@@ -308,7 +388,7 @@ func cssHandler(c *config.Instance) func(w http.ResponseWriter, r *http.Request)
}
}
func javascriptHandler(c *config.Instance) func(w http.ResponseWriter, r *http.Request) {
func javascriptHandler(c *config.Config) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var paths []string
@@ -326,7 +406,7 @@ func javascriptHandler(c *config.Instance) func(w http.ResponseWriter, r *http.R
}
}
func customLocalesHandler(c *config.Instance) func(w http.ResponseWriter, r *http.Request) {
func customLocalesHandler(c *config.Config) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
buffer := bytes.Buffer{}
@@ -351,7 +431,7 @@ func customLocalesHandler(c *config.Instance) func(w http.ResponseWriter, r *htt
}
}
func makeTLSConfig(c *config.Instance) (*tls.Config, error) {
func makeTLSConfig(c *config.Config) (*tls.Config, error) {
c.InitTLS()
certFile, keyFile := c.GetTLSFiles()

View File

@@ -14,12 +14,13 @@ import (
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/session"
"github.com/stashapp/stash/pkg/utils"
"github.com/stashapp/stash/ui"
)
const returnURLParam = "returnURL"
func getLoginPage(loginUIBox fs.FS) []byte {
data, err := fs.ReadFile(loginUIBox, "login.html")
func getLoginPage() []byte {
data, err := fs.ReadFile(ui.LoginUIBox, "login.html")
if err != nil {
panic(err)
}
@@ -31,8 +32,8 @@ type loginTemplateData struct {
Error string
}
func serveLoginPage(loginUIBox fs.FS, w http.ResponseWriter, r *http.Request, returnURL string, loginError string) {
loginPage := string(getLoginPage(loginUIBox))
func serveLoginPage(w http.ResponseWriter, r *http.Request, returnURL string, loginError string) {
loginPage := string(getLoginPage())
prefix := getProxyPrefix(r)
loginPage = strings.ReplaceAll(loginPage, "/%BASE_URL%", prefix)
@@ -57,7 +58,7 @@ func serveLoginPage(loginUIBox fs.FS, w http.ResponseWriter, r *http.Request, re
utils.ServeStaticContent(w, r, buffer.Bytes())
}
func handleLogin(loginUIBox fs.FS) http.HandlerFunc {
func handleLogin() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
returnURL := r.URL.Query().Get(returnURLParam)
@@ -71,11 +72,11 @@ func handleLogin(loginUIBox fs.FS) http.HandlerFunc {
return
}
serveLoginPage(loginUIBox, w, r, returnURL, "")
serveLoginPage(w, r, returnURL, "")
}
}
func handleLoginPost(loginUIBox fs.FS) http.HandlerFunc {
func handleLoginPost() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
url := r.FormValue(returnURLParam)
if url == "" {
@@ -92,7 +93,7 @@ func handleLoginPost(loginUIBox fs.FS) http.HandlerFunc {
if errors.As(err, &invalidCredentialsError) {
// serve login page with an error
serveLoginPage(loginUIBox, w, r, url, "Username or password is invalid")
serveLoginPage(w, r, url, "Username or password is invalid")
return
}