mirror of
https://github.com/stashapp/stash.git
synced 2025-12-16 20:07:05 +03:00
Log errors returned from graphql (#3562)
* Add func methods to logger * Log errors returned from the graphql interface * Log authentication * Log when credentials changed
This commit is contained in:
39
internal/api/error.go
Normal file
39
internal/api/error.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/99designs/gqlgen/graphql"
|
||||||
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||||
|
)
|
||||||
|
|
||||||
|
func gqlErrorHandler(ctx context.Context, e error) *gqlerror.Error {
|
||||||
|
// log all errors - for now just log the error message
|
||||||
|
// we can potentially add more context later
|
||||||
|
fc := graphql.GetFieldContext(ctx)
|
||||||
|
if fc != nil {
|
||||||
|
logger.Errorf("%s: %v", fc.Path(), e)
|
||||||
|
|
||||||
|
// log the args in debug level
|
||||||
|
logger.DebugFunc(func() (string, []interface{}) {
|
||||||
|
var args interface{}
|
||||||
|
args = fc.Args
|
||||||
|
|
||||||
|
s, _ := json.Marshal(args)
|
||||||
|
if len(s) > 0 {
|
||||||
|
args = string(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "%s: %v", []interface{}{
|
||||||
|
fc.Path(),
|
||||||
|
args,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// we may also want to transform the error message for the response
|
||||||
|
// for now just return the original error
|
||||||
|
return graphql.DefaultErrorPresenter(ctx, e)
|
||||||
|
}
|
||||||
@@ -228,8 +228,13 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input ConfigGen
|
|||||||
c.Set(config.GalleryCoverRegex, *input.GalleryCoverRegex)
|
c.Set(config.GalleryCoverRegex, *input.GalleryCoverRegex)
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.Username != nil {
|
if input.Username != nil && *input.Username != c.GetUsername() {
|
||||||
c.Set(config.Username, input.Username)
|
c.Set(config.Username, input.Username)
|
||||||
|
if *input.Password == "" {
|
||||||
|
logger.Info("Username cleared")
|
||||||
|
} else {
|
||||||
|
logger.Info("Username changed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.Password != nil {
|
if input.Password != nil {
|
||||||
@@ -238,6 +243,11 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input ConfigGen
|
|||||||
currentPWHash := c.GetPasswordHash()
|
currentPWHash := c.GetPasswordHash()
|
||||||
|
|
||||||
if *input.Password != currentPWHash {
|
if *input.Password != currentPWHash {
|
||||||
|
if *input.Password == "" {
|
||||||
|
logger.Info("Password cleared")
|
||||||
|
} else {
|
||||||
|
logger.Info("Password changed")
|
||||||
|
}
|
||||||
c.SetPassword(*input.Password)
|
c.SetPassword(*input.Password)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ func Start() error {
|
|||||||
gqlSrv.SetQueryCache(gqlLru.New(1000))
|
gqlSrv.SetQueryCache(gqlLru.New(1000))
|
||||||
gqlSrv.Use(gqlExtension.Introspection{})
|
gqlSrv.Use(gqlExtension.Introspection{})
|
||||||
|
|
||||||
|
gqlSrv.SetErrorPresenter(gqlErrorHandler)
|
||||||
|
|
||||||
gqlHandlerFunc := func(w http.ResponseWriter, r *http.Request) {
|
gqlHandlerFunc := func(w http.ResponseWriter, r *http.Request) {
|
||||||
gqlSrv.ServeHTTP(w, r)
|
gqlSrv.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stashapp/stash/internal/manager"
|
"github.com/stashapp/stash/internal/manager"
|
||||||
"github.com/stashapp/stash/internal/manager/config"
|
"github.com/stashapp/stash/internal/manager/config"
|
||||||
|
"github.com/stashapp/stash/pkg/logger"
|
||||||
"github.com/stashapp/stash/pkg/session"
|
"github.com/stashapp/stash/pkg/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -61,7 +62,14 @@ func handleLogin(loginUIBox embed.FS) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
err := manager.GetInstance().SessionStore.Login(w, r)
|
err := manager.GetInstance().SessionStore.Login(w, r)
|
||||||
if errors.Is(err, session.ErrInvalidCredentials) {
|
if err != nil {
|
||||||
|
// always log the error
|
||||||
|
logger.Errorf("Error logging in: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidCredentialsError *session.InvalidCredentialsError
|
||||||
|
|
||||||
|
if errors.As(err, &invalidCredentialsError) {
|
||||||
// redirect back to the login page with an error
|
// redirect back to the login page with an error
|
||||||
redirectToLogin(loginUIBox, w, url, "Username or password is invalid")
|
redirectToLogin(loginUIBox, w, url, "Username or password is invalid")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -235,6 +235,13 @@ func (log *Logger) Tracef(format string, args ...interface{}) {
|
|||||||
log.addLogItem(l)
|
log.addLogItem(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *Logger) TraceFunc(fn func() (string, []interface{})) {
|
||||||
|
if log.logger.Level >= logrus.TraceLevel {
|
||||||
|
msg, args := fn()
|
||||||
|
log.Tracef(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (log *Logger) Debug(args ...interface{}) {
|
func (log *Logger) Debug(args ...interface{}) {
|
||||||
log.logger.Debug(args...)
|
log.logger.Debug(args...)
|
||||||
l := &LogItem{
|
l := &LogItem{
|
||||||
@@ -253,6 +260,17 @@ func (log *Logger) Debugf(format string, args ...interface{}) {
|
|||||||
log.addLogItem(l)
|
log.addLogItem(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *Logger) logFunc(level logrus.Level, logFn func(format string, args ...interface{}), fn func() (string, []interface{})) {
|
||||||
|
if log.logger.Level >= level {
|
||||||
|
msg, args := fn()
|
||||||
|
logFn(msg, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (log *Logger) DebugFunc(fn func() (string, []interface{})) {
|
||||||
|
log.logFunc(logrus.DebugLevel, log.logger.Debugf, fn)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *Logger) Info(args ...interface{}) {
|
func (log *Logger) Info(args ...interface{}) {
|
||||||
log.logger.Info(args...)
|
log.logger.Info(args...)
|
||||||
l := &LogItem{
|
l := &LogItem{
|
||||||
@@ -271,6 +289,10 @@ func (log *Logger) Infof(format string, args ...interface{}) {
|
|||||||
log.addLogItem(l)
|
log.addLogItem(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *Logger) InfoFunc(fn func() (string, []interface{})) {
|
||||||
|
log.logFunc(logrus.InfoLevel, log.logger.Infof, fn)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *Logger) Warn(args ...interface{}) {
|
func (log *Logger) Warn(args ...interface{}) {
|
||||||
log.logger.Warn(args...)
|
log.logger.Warn(args...)
|
||||||
l := &LogItem{
|
l := &LogItem{
|
||||||
@@ -289,6 +311,10 @@ func (log *Logger) Warnf(format string, args ...interface{}) {
|
|||||||
log.addLogItem(l)
|
log.addLogItem(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *Logger) WarnFunc(fn func() (string, []interface{})) {
|
||||||
|
log.logFunc(logrus.WarnLevel, log.logger.Warnf, fn)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *Logger) Error(args ...interface{}) {
|
func (log *Logger) Error(args ...interface{}) {
|
||||||
log.logger.Error(args...)
|
log.logger.Error(args...)
|
||||||
l := &LogItem{
|
l := &LogItem{
|
||||||
@@ -307,6 +333,10 @@ func (log *Logger) Errorf(format string, args ...interface{}) {
|
|||||||
log.addLogItem(l)
|
log.addLogItem(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *Logger) ErrorFunc(fn func() (string, []interface{})) {
|
||||||
|
log.logFunc(logrus.ErrorLevel, log.logger.Errorf, fn)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *Logger) Fatal(args ...interface{}) {
|
func (log *Logger) Fatal(args ...interface{}) {
|
||||||
log.logger.Fatal(args...)
|
log.logger.Fatal(args...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ func (log *BasicLogger) Tracef(format string, args ...interface{}) {
|
|||||||
log.printf("Trace", format, args...)
|
log.printf("Trace", format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *BasicLogger) TraceFunc(fn func() (string, []interface{})) {
|
||||||
|
format, args := fn()
|
||||||
|
log.printf("Trace", format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *BasicLogger) Debug(args ...interface{}) {
|
func (log *BasicLogger) Debug(args ...interface{}) {
|
||||||
log.print("Debug", args...)
|
log.print("Debug", args...)
|
||||||
}
|
}
|
||||||
@@ -39,6 +44,11 @@ func (log *BasicLogger) Debugf(format string, args ...interface{}) {
|
|||||||
log.printf("Debug", format, args...)
|
log.printf("Debug", format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *BasicLogger) DebugFunc(fn func() (string, []interface{})) {
|
||||||
|
format, args := fn()
|
||||||
|
log.printf("Debug", format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *BasicLogger) Info(args ...interface{}) {
|
func (log *BasicLogger) Info(args ...interface{}) {
|
||||||
log.print("Info", args...)
|
log.print("Info", args...)
|
||||||
}
|
}
|
||||||
@@ -47,6 +57,11 @@ func (log *BasicLogger) Infof(format string, args ...interface{}) {
|
|||||||
log.printf("Info", format, args...)
|
log.printf("Info", format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *BasicLogger) InfoFunc(fn func() (string, []interface{})) {
|
||||||
|
format, args := fn()
|
||||||
|
log.printf("Info", format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *BasicLogger) Warn(args ...interface{}) {
|
func (log *BasicLogger) Warn(args ...interface{}) {
|
||||||
log.print("Warn", args...)
|
log.print("Warn", args...)
|
||||||
}
|
}
|
||||||
@@ -55,6 +70,11 @@ func (log *BasicLogger) Warnf(format string, args ...interface{}) {
|
|||||||
log.printf("Warn", format, args...)
|
log.printf("Warn", format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *BasicLogger) WarnFunc(fn func() (string, []interface{})) {
|
||||||
|
format, args := fn()
|
||||||
|
log.printf("Warn", format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *BasicLogger) Error(args ...interface{}) {
|
func (log *BasicLogger) Error(args ...interface{}) {
|
||||||
log.print("Error", args...)
|
log.print("Error", args...)
|
||||||
}
|
}
|
||||||
@@ -63,6 +83,11 @@ func (log *BasicLogger) Errorf(format string, args ...interface{}) {
|
|||||||
log.printf("Error", format, args...)
|
log.printf("Error", format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (log *BasicLogger) ErrorFunc(fn func() (string, []interface{})) {
|
||||||
|
format, args := fn()
|
||||||
|
log.printf("Error", format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
func (log *BasicLogger) Fatal(args ...interface{}) {
|
func (log *BasicLogger) Fatal(args ...interface{}) {
|
||||||
log.print("Fatal", args...)
|
log.print("Fatal", args...)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -16,18 +16,23 @@ type LoggerImpl interface {
|
|||||||
|
|
||||||
Trace(args ...interface{})
|
Trace(args ...interface{})
|
||||||
Tracef(format string, args ...interface{})
|
Tracef(format string, args ...interface{})
|
||||||
|
TraceFunc(fn func() (string, []interface{}))
|
||||||
|
|
||||||
Debug(args ...interface{})
|
Debug(args ...interface{})
|
||||||
Debugf(format string, args ...interface{})
|
Debugf(format string, args ...interface{})
|
||||||
|
DebugFunc(fn func() (string, []interface{}))
|
||||||
|
|
||||||
Info(args ...interface{})
|
Info(args ...interface{})
|
||||||
Infof(format string, args ...interface{})
|
Infof(format string, args ...interface{})
|
||||||
|
InfoFunc(fn func() (string, []interface{}))
|
||||||
|
|
||||||
Warn(args ...interface{})
|
Warn(args ...interface{})
|
||||||
Warnf(format string, args ...interface{})
|
Warnf(format string, args ...interface{})
|
||||||
|
WarnFunc(fn func() (string, []interface{}))
|
||||||
|
|
||||||
Error(args ...interface{})
|
Error(args ...interface{})
|
||||||
Errorf(format string, args ...interface{})
|
Errorf(format string, args ...interface{})
|
||||||
|
ErrorFunc(fn func() (string, []interface{}))
|
||||||
|
|
||||||
Fatal(args ...interface{})
|
Fatal(args ...interface{})
|
||||||
Fatalf(format string, args ...interface{})
|
Fatalf(format string, args ...interface{})
|
||||||
@@ -61,6 +66,14 @@ func Tracef(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraceFunc calls TraceFunc with the Logger registered using RegisterLogger.
|
||||||
|
// If no logger has been registered, then this function is a no-op.
|
||||||
|
func TraceFunc(fn func() (string, []interface{})) {
|
||||||
|
if Logger != nil {
|
||||||
|
Logger.TraceFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Debug calls Debug with the Logger registered using RegisterLogger.
|
// Debug calls Debug with the Logger registered using RegisterLogger.
|
||||||
// If no logger has been registered, then this function is a no-op.
|
// If no logger has been registered, then this function is a no-op.
|
||||||
func Debug(args ...interface{}) {
|
func Debug(args ...interface{}) {
|
||||||
@@ -77,6 +90,14 @@ func Debugf(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DebugFunc calls DebugFunc with the Logger registered using RegisterLogger.
|
||||||
|
// If no logger has been registered, then this function is a no-op.
|
||||||
|
func DebugFunc(fn func() (string, []interface{})) {
|
||||||
|
if Logger != nil {
|
||||||
|
Logger.DebugFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Info calls Info with the Logger registered using RegisterLogger.
|
// Info calls Info with the Logger registered using RegisterLogger.
|
||||||
// If no logger has been registered, then this function is a no-op.
|
// If no logger has been registered, then this function is a no-op.
|
||||||
func Info(args ...interface{}) {
|
func Info(args ...interface{}) {
|
||||||
@@ -93,6 +114,14 @@ func Infof(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InfoFunc calls InfoFunc with the Logger registered using RegisterLogger.
|
||||||
|
// If no logger has been registered, then this function is a no-op.
|
||||||
|
func InfoFunc(fn func() (string, []interface{})) {
|
||||||
|
if Logger != nil {
|
||||||
|
Logger.InfoFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Warn calls Warn with the Logger registered using RegisterLogger.
|
// Warn calls Warn with the Logger registered using RegisterLogger.
|
||||||
// If no logger has been registered, then this function is a no-op.
|
// If no logger has been registered, then this function is a no-op.
|
||||||
func Warn(args ...interface{}) {
|
func Warn(args ...interface{}) {
|
||||||
@@ -109,6 +138,14 @@ func Warnf(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WarnFunc calls WarnFunc with the Logger registered using RegisterLogger.
|
||||||
|
// If no logger has been registered, then this function is a no-op.
|
||||||
|
func WarnFunc(fn func() (string, []interface{})) {
|
||||||
|
if Logger != nil {
|
||||||
|
Logger.WarnFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Error calls Error with the Logger registered using RegisterLogger.
|
// Error calls Error with the Logger registered using RegisterLogger.
|
||||||
// If no logger has been registered, then this function is a no-op.
|
// If no logger has been registered, then this function is a no-op.
|
||||||
func Error(args ...interface{}) {
|
func Error(args ...interface{}) {
|
||||||
@@ -125,6 +162,14 @@ func Errorf(format string, args ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrorFunc calls ErrorFunc with the Logger registered using RegisterLogger.
|
||||||
|
// If no logger has been registered, then this function is a no-op.
|
||||||
|
func ErrorFunc(fn func() (string, []interface{})) {
|
||||||
|
if Logger != nil {
|
||||||
|
Logger.ErrorFunc(fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fatal calls Fatal with the Logger registered using RegisterLogger.
|
// Fatal calls Fatal with the Logger registered using RegisterLogger.
|
||||||
// If no logger has been registered, then this function is a no-op.
|
// If no logger has been registered, then this function is a no-op.
|
||||||
func Fatal(args ...interface{}) {
|
func Fatal(args ...interface{}) {
|
||||||
|
|||||||
@@ -34,7 +34,15 @@ const (
|
|||||||
passwordFormKey = "password"
|
passwordFormKey = "password"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrInvalidCredentials = errors.New("invalid username or password")
|
type InvalidCredentialsError struct {
|
||||||
|
Username string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e InvalidCredentialsError) Error() string {
|
||||||
|
// don't leak the username
|
||||||
|
return "invalid credentials"
|
||||||
|
}
|
||||||
|
|
||||||
var ErrUnauthorized = errors.New("unauthorized")
|
var ErrUnauthorized = errors.New("unauthorized")
|
||||||
|
|
||||||
type Store struct {
|
type Store struct {
|
||||||
@@ -63,9 +71,12 @@ func (s *Store) Login(w http.ResponseWriter, r *http.Request) error {
|
|||||||
|
|
||||||
// authenticate the user
|
// authenticate the user
|
||||||
if !s.config.ValidateCredentials(username, password) {
|
if !s.config.ValidateCredentials(username, password) {
|
||||||
return ErrInvalidCredentials
|
return &InvalidCredentialsError{Username: username}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// since we only have one user, don't leak the name
|
||||||
|
logger.Info("User logged in")
|
||||||
|
|
||||||
newSession.Values[userIDKey] = username
|
newSession.Values[userIDKey] = username
|
||||||
|
|
||||||
err := newSession.Save(r, w)
|
err := newSession.Save(r, w)
|
||||||
@@ -90,6 +101,9 @@ func (s *Store) Logout(w http.ResponseWriter, r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// since we only have one user, don't leak the name
|
||||||
|
logger.Infof("User logged out")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user