From 4a2cc70b52cc5fed8514639a6c2d8134eae93029 Mon Sep 17 00:00:00 2001 From: yusing Date: Fri, 28 Mar 2025 07:14:34 +0800 Subject: [PATCH] refactor: rename module 'err' to 'gperr' and use gphttp error handling --- cmd/main.go | 8 ++++---- internal/api/v1/auth/auth.go | 6 ++---- internal/api/v1/auth/oidc.go | 29 ++++++++++++++--------------- internal/api/v1/auth/userpass.go | 17 ++++++++--------- internal/api/v1/auth/utils.go | 8 ++++---- internal/api/v1/favicon/favicon.go | 24 ++++++++++++------------ internal/api/v1/query/query.go | 30 +++++++++++++++--------------- 7 files changed, 59 insertions(+), 63 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 37563e2..b176bba 100755 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,7 +17,7 @@ import ( "github.com/yusing/go-proxy/internal/api/v1/query" "github.com/yusing/go-proxy/internal/common" "github.com/yusing/go-proxy/internal/config" - E "github.com/yusing/go-proxy/internal/error" + "github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/homepage" "github.com/yusing/go-proxy/internal/logging" "github.com/yusing/go-proxy/internal/net/http/middleware" @@ -47,7 +47,7 @@ func main() { switch args.Command { case common.CommandReload: if err := query.ReloadServer(); err != nil { - E.LogFatal("server reload error", err) + gperr.LogFatal("server reload error", err) } rawLogger.Println("ok") return @@ -107,9 +107,9 @@ func main() { middleware.LoadComposeFiles() var cfg *config.Config - var err E.Error + var err gperr.Error if cfg, err = config.Load(); err != nil { - E.LogWarn("errors in config", err) + gperr.LogWarn("errors in config", err) } switch args.Command { diff --git a/internal/api/v1/auth/auth.go b/internal/api/v1/auth/auth.go index ed0060e..c579d4a 100644 --- a/internal/api/v1/auth/auth.go +++ b/internal/api/v1/auth/auth.go @@ -3,9 +3,8 @@ package auth import ( "net/http" - U "github.com/yusing/go-proxy/internal/api/v1/utils" "github.com/yusing/go-proxy/internal/common" - "github.com/yusing/go-proxy/internal/logging" + "github.com/yusing/go-proxy/internal/net/gphttp" ) var defaultAuth Provider @@ -13,7 +12,6 @@ var defaultAuth Provider // Initialize sets up authentication providers. func Initialize() error { if !IsEnabled() { - logging.Warn().Msg("authentication is disabled, please set API_JWT_SECRET or OIDC_* to enable authentication") return nil } @@ -44,7 +42,7 @@ func RequireAuth(next http.HandlerFunc) http.HandlerFunc { if IsEnabled() { return func(w http.ResponseWriter, r *http.Request) { if err := defaultAuth.CheckToken(r); err != nil { - U.RespondError(w, err, http.StatusUnauthorized) + gphttp.ClientError(w, err, http.StatusUnauthorized) } else { next(w, r) } diff --git a/internal/api/v1/auth/oidc.go b/internal/api/v1/auth/oidc.go index d08d674..bb386e0 100644 --- a/internal/api/v1/auth/oidc.go +++ b/internal/api/v1/auth/oidc.go @@ -12,10 +12,9 @@ import ( "time" "github.com/coreos/go-oidc/v3/oidc" - U "github.com/yusing/go-proxy/internal/api/v1/utils" "github.com/yusing/go-proxy/internal/common" - E "github.com/yusing/go-proxy/internal/error" - CE "github.com/yusing/go-proxy/internal/utils" + "github.com/yusing/go-proxy/internal/net/gphttp" + "github.com/yusing/go-proxy/internal/utils" "github.com/yusing/go-proxy/internal/utils/strutils" "golang.org/x/oauth2" ) @@ -131,9 +130,9 @@ func (auth *OIDCProvider) CheckToken(r *http.Request) error { // Logical AND between allowed users and groups. allowedUser := slices.Contains(auth.allowedUsers, claims.Username) - allowedGroup := len(CE.Intersect(claims.Groups, auth.allowedGroups)) > 0 + allowedGroup := len(utils.Intersect(claims.Groups, auth.allowedGroups)) > 0 if !allowedUser && !allowedGroup { - return ErrUserNotAllowed.Subject(claims.Username) + return ErrUserNotAllowed } return nil } @@ -154,7 +153,7 @@ func generateState() (string, error) { func (auth *OIDCProvider) RedirectLoginPage(w http.ResponseWriter, r *http.Request) { state, err := generateState() if err != nil { - U.HandleErr(w, r, err, http.StatusInternalServerError) + gphttp.ServerError(w, r, err) return } http.SetCookie(w, &http.Cookie{ @@ -171,7 +170,7 @@ func (auth *OIDCProvider) RedirectLoginPage(w http.ResponseWriter, r *http.Reque if auth.isMiddleware { u, err := r.URL.Parse(redirURL) if err != nil { - U.HandleErr(w, r, err, http.StatusInternalServerError) + gphttp.ServerError(w, r, err) return } q := u.Query() @@ -201,31 +200,31 @@ func (auth *OIDCProvider) LoginCallbackHandler(w http.ResponseWriter, r *http.Re state, err := r.Cookie(CookieOauthState) if err != nil { - U.HandleErr(w, r, E.New("missing state cookie"), http.StatusBadRequest) + gphttp.BadRequest(w, "missing state cookie") return } query := r.URL.Query() if query.Get("state") != state.Value { - U.HandleErr(w, r, E.New("invalid oauth state"), http.StatusBadRequest) + gphttp.BadRequest(w, "invalid oauth state") return } oauth2Token, err := auth.exchange(r) if err != nil { - U.HandleErr(w, r, fmt.Errorf("failed to exchange token: %w", err), http.StatusInternalServerError) + gphttp.ServerError(w, r, fmt.Errorf("failed to exchange token: %w", err)) return } rawIDToken, ok := oauth2Token.Extra("id_token").(string) if !ok { - U.HandleErr(w, r, E.New("missing id_token"), http.StatusInternalServerError) + gphttp.BadRequest(w, "missing id_token") return } idToken, err := auth.oidcVerifier.Verify(r.Context(), rawIDToken) if err != nil { - U.HandleErr(w, r, fmt.Errorf("failed to verify ID token: %w", err), http.StatusInternalServerError) + gphttp.ServerError(w, r, fmt.Errorf("failed to verify ID token: %w", err)) return } @@ -243,7 +242,7 @@ func (auth *OIDCProvider) LogoutCallbackHandler(w http.ResponseWriter, r *http.R token, err := r.Cookie(auth.TokenCookieName()) if err != nil { - U.HandleErr(w, r, E.New("missing token cookie"), http.StatusBadRequest) + gphttp.BadRequest(w, "missing token cookie") return } clearTokenCookie(w, r, auth.TokenCookieName()) @@ -258,12 +257,12 @@ func (auth *OIDCProvider) LogoutCallbackHandler(w http.ResponseWriter, r *http.R func (auth *OIDCProvider) handleTestCallback(w http.ResponseWriter, r *http.Request) { state, err := r.Cookie(CookieOauthState) if err != nil { - U.HandleErr(w, r, E.New("missing state cookie"), http.StatusBadRequest) + gphttp.BadRequest(w, "missing state cookie") return } if r.URL.Query().Get("state") != state.Value { - U.HandleErr(w, r, E.New("invalid oauth state"), http.StatusBadRequest) + gphttp.BadRequest(w, "invalid oauth state") return } diff --git a/internal/api/v1/auth/userpass.go b/internal/api/v1/auth/userpass.go index 7c6512c..239a4cc 100644 --- a/internal/api/v1/auth/userpass.go +++ b/internal/api/v1/auth/userpass.go @@ -7,16 +7,16 @@ import ( "time" "github.com/golang-jwt/jwt/v5" - U "github.com/yusing/go-proxy/internal/api/v1/utils" "github.com/yusing/go-proxy/internal/common" - E "github.com/yusing/go-proxy/internal/error" + "github.com/yusing/go-proxy/internal/gperr" + "github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/utils/strutils" "golang.org/x/crypto/bcrypt" ) var ( - ErrInvalidUsername = E.New("invalid username") - ErrInvalidPassword = E.New("invalid password") + ErrInvalidUsername = gperr.New("invalid username") + ErrInvalidPassword = gperr.New("invalid password") ) type ( @@ -94,7 +94,7 @@ func (auth *UserPassAuth) CheckToken(r *http.Request) error { case claims.Username != auth.username: return ErrUserNotAllowed.Subject(claims.Username) case claims.ExpiresAt.Before(time.Now()): - return E.Errorf("token expired on %s", strutils.FormatTime(claims.ExpiresAt.Time)) + return gperr.Errorf("token expired on %s", strutils.FormatTime(claims.ExpiresAt.Time)) } return nil @@ -111,17 +111,16 @@ func (auth *UserPassAuth) LoginCallbackHandler(w http.ResponseWriter, r *http.Re } err := json.NewDecoder(r.Body).Decode(&creds) if err != nil { - U.HandleErr(w, r, err, http.StatusBadRequest) + gphttp.Unauthorized(w, "invalid credentials") return } if err := auth.validatePassword(creds.User, creds.Pass); err != nil { - U.LogError(r).Err(err).Msg("auth: invalid credentials") - U.RespondError(w, E.New("invalid credentials"), http.StatusUnauthorized) + gphttp.Unauthorized(w, "invalid credentials") return } token, err := auth.NewToken() if err != nil { - U.HandleErr(w, r, err, http.StatusInternalServerError) + gphttp.ServerError(w, r, err) return } setTokenCookie(w, r, auth.TokenCookieName(), token, auth.tokenTTL) diff --git a/internal/api/v1/auth/utils.go b/internal/api/v1/auth/utils.go index 05a1558..91eec3e 100644 --- a/internal/api/v1/auth/utils.go +++ b/internal/api/v1/auth/utils.go @@ -5,14 +5,14 @@ import ( "net/http" "time" - E "github.com/yusing/go-proxy/internal/error" + "github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/utils/strutils" ) var ( - ErrMissingToken = E.New("missing token") - ErrInvalidToken = E.New("invalid token") - ErrUserNotAllowed = E.New("user not allowed") + ErrMissingToken = gperr.New("missing token") + ErrInvalidToken = gperr.New("invalid token") + ErrUserNotAllowed = gperr.New("user not allowed") ) // cookieFQDN returns the fully qualified domain name of the request host diff --git a/internal/api/v1/favicon/favicon.go b/internal/api/v1/favicon/favicon.go index 00525dd..ccbb9a3 100644 --- a/internal/api/v1/favicon/favicon.go +++ b/internal/api/v1/favicon/favicon.go @@ -13,10 +13,10 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/vincent-petithory/dataurl" - U "github.com/yusing/go-proxy/internal/api/v1/utils" + "github.com/yusing/go-proxy/internal/gperr" "github.com/yusing/go-proxy/internal/homepage" "github.com/yusing/go-proxy/internal/logging" - gphttp "github.com/yusing/go-proxy/internal/net/http" + gphttp "github.com/yusing/go-proxy/internal/net/gphttp" "github.com/yusing/go-proxy/internal/route/routes" route "github.com/yusing/go-proxy/internal/route/types" "github.com/yusing/go-proxy/internal/utils/strutils" @@ -54,11 +54,11 @@ func (res *fetchResult) ContentType() string { func GetFavIcon(w http.ResponseWriter, req *http.Request) { url, alias := req.FormValue("url"), req.FormValue("alias") if url == "" && alias == "" { - U.RespondError(w, U.ErrMissingKey("url or alias"), http.StatusBadRequest) + gphttp.ClientError(w, gphttp.ErrMissingKey("url or alias"), http.StatusBadRequest) return } if url != "" && alias != "" { - U.RespondError(w, U.ErrInvalidKey("url and alias are mutually exclusive"), http.StatusBadRequest) + gphttp.ClientError(w, gperr.New("url and alias are mutually exclusive"), http.StatusBadRequest) return } @@ -66,7 +66,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) { if url != "" { var iconURL homepage.IconURL if err := iconURL.Parse(url); err != nil { - U.RespondError(w, err, http.StatusBadRequest) + gphttp.ClientError(w, err, http.StatusBadRequest) return } fetchResult := getFavIconFromURL(&iconURL) @@ -75,20 +75,20 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) { return } w.Header().Set("Content-Type", fetchResult.ContentType()) - U.WriteBody(w, fetchResult.icon) + gphttp.WriteBody(w, fetchResult.icon) return } // try with route.Homepage.Icon r, ok := routes.GetHTTPRoute(alias) if !ok { - U.RespondError(w, errors.New("no such route"), http.StatusNotFound) + gphttp.ClientError(w, errors.New("no such route"), http.StatusNotFound) return } var result *fetchResult - hp := r.HomepageConfig().GetOverride() - if !hp.IsEmpty() && hp.Icon != nil { + hp := r.HomepageItem() + if hp.Icon != nil { if hp.Icon.IconSource == homepage.IconSourceRelative { result = findIcon(r, req, hp.Icon.Value) } else { @@ -106,7 +106,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) { return } w.Header().Set("Content-Type", result.ContentType()) - U.WriteBody(w, result.icon) + gphttp.WriteBody(w, result.icon) } func getFavIconFromURL(iconURL *homepage.IconURL) *fetchResult { @@ -126,7 +126,7 @@ func fetchIconAbsolute(url string) *fetchResult { return result } - resp, err := U.Get(url) + resp, err := gphttp.Get(url) if err != nil || resp.StatusCode != http.StatusOK { if err == nil { err = errors.New(resp.Status) @@ -191,7 +191,7 @@ func findIcon(r route.HTTPRoute, req *http.Request, uri string) *fetchResult { result := fetchIcon("png", sanitizeName(r.TargetName())) cont := r.ContainerInfo() if !result.OK() && cont != nil { - result = fetchIcon("png", sanitizeName(cont.ImageName)) + result = fetchIcon("png", sanitizeName(cont.Image.Name)) } if !result.OK() { // fallback to parse html diff --git a/internal/api/v1/query/query.go b/internal/api/v1/query/query.go index 7b59772..0a1d576 100644 --- a/internal/api/v1/query/query.go +++ b/internal/api/v1/query/query.go @@ -7,20 +7,20 @@ import ( "net/http" v1 "github.com/yusing/go-proxy/internal/api/v1" - U "github.com/yusing/go-proxy/internal/api/v1/utils" "github.com/yusing/go-proxy/internal/common" - E "github.com/yusing/go-proxy/internal/error" - "github.com/yusing/go-proxy/internal/net/http/middleware" + "github.com/yusing/go-proxy/internal/gperr" + "github.com/yusing/go-proxy/internal/net/gphttp" + "github.com/yusing/go-proxy/internal/net/gphttp/middleware" ) -func ReloadServer() E.Error { - resp, err := U.Post(common.APIHTTPURL+"/v1/reload", "", nil) +func ReloadServer() gperr.Error { + resp, err := gphttp.Post(common.APIHTTPURL+"/v1/reload", "", nil) if err != nil { - return E.From(err) + return gperr.Wrap(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - failure := E.Errorf("server reload status %v", resp.StatusCode) + failure := gperr.Errorf("server reload status %v", resp.StatusCode) body, err := io.ReadAll(resp.Body) if err != nil { return failure.With(err) @@ -31,34 +31,34 @@ func ReloadServer() E.Error { return nil } -func List[T any](what string) (_ T, outErr E.Error) { - resp, err := U.Get(fmt.Sprintf("%s/v1/list/%s", common.APIHTTPURL, what)) +func List[T any](what string) (_ T, outErr gperr.Error) { + resp, err := gphttp.Get(fmt.Sprintf("%s/v1/list/%s", common.APIHTTPURL, what)) if err != nil { - outErr = E.From(err) + outErr = gperr.Wrap(err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - outErr = E.Errorf("list %s: failed, status %v", what, resp.StatusCode) + outErr = gperr.Errorf("list %s: failed, status %v", what, resp.StatusCode) return } var res T err = json.NewDecoder(resp.Body).Decode(&res) if err != nil { - outErr = E.From(err) + outErr = gperr.Wrap(err) return } return res, nil } -func ListRoutes() (map[string]map[string]any, E.Error) { +func ListRoutes() (map[string]map[string]any, gperr.Error) { return List[map[string]map[string]any](v1.ListRoutes) } -func ListMiddlewareTraces() (middleware.Traces, E.Error) { +func ListMiddlewareTraces() (middleware.Traces, gperr.Error) { return List[middleware.Traces](v1.ListMiddlewareTraces) } -func DebugListTasks() (map[string]any, E.Error) { +func DebugListTasks() (map[string]any, gperr.Error) { return List[map[string]any](v1.ListTasks) }