mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-10 18:56:55 +02:00
refactor and organize code
This commit is contained in:
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
||||
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,18 +6,19 @@ import (
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
)
|
||||
|
||||
func ListAgents(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
U.PeriodicWS(w, r, 10*time.Second, func(conn *websocket.Conn) error {
|
||||
gpwebsocket.Periodic(w, r, 10*time.Second, func(conn *websocket.Conn) error {
|
||||
wsjson.Write(r.Context(), conn, cfg.ListAgents())
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
U.RespondJSON(w, r, cfg.ListAgents())
|
||||
gphttp.RespondJSON(w, r, cfg.ListAgents())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ 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
|
||||
@@ -44,7 +44,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)
|
||||
}
|
||||
|
||||
@@ -12,9 +12,8 @@ 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"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
CE "github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
@@ -53,11 +53,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
|
||||
}
|
||||
|
||||
@@ -65,7 +65,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)
|
||||
@@ -74,14 +74,14 @@ 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
|
||||
}
|
||||
|
||||
@@ -105,7 +105,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 {
|
||||
@@ -125,7 +125,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)
|
||||
|
||||
@@ -7,11 +7,11 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
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"
|
||||
"github.com/yusing/go-proxy/internal/route/provider"
|
||||
)
|
||||
|
||||
@@ -51,12 +51,12 @@ func (t FileType) GetPath(filename string) string {
|
||||
func getArgs(r *http.Request) (fileType FileType, filename string, err error) {
|
||||
fileType = FileType(r.PathValue("type"))
|
||||
if !fileType.IsValid() {
|
||||
err = U.ErrInvalidKey("type")
|
||||
err = gphttp.ErrInvalidKey("type")
|
||||
return
|
||||
}
|
||||
filename = r.PathValue("filename")
|
||||
if filename == "" {
|
||||
err = U.ErrMissingKey("filename")
|
||||
err = gphttp.ErrMissingKey("filename")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -64,23 +64,23 @@ func getArgs(r *http.Request) (fileType FileType, filename string, err error) {
|
||||
func GetFileContent(w http.ResponseWriter, r *http.Request) {
|
||||
fileType, filename, err := getArgs(r)
|
||||
if err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
content, err := os.ReadFile(fileType.GetPath(filename))
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
U.WriteBody(w, content)
|
||||
gphttp.WriteBody(w, content)
|
||||
}
|
||||
|
||||
func validateFile(fileType FileType, content []byte) error {
|
||||
func validateFile(fileType FileType, content []byte) gperr.Error {
|
||||
switch fileType {
|
||||
case FileTypeConfig:
|
||||
return config.Validate(content)
|
||||
case FileTypeMiddleware:
|
||||
errs := E.NewBuilder("middleware errors")
|
||||
errs := gperr.NewBuilder("middleware errors")
|
||||
middleware.BuildMiddlewaresFromYAML("", content, errs)
|
||||
return errs.Error()
|
||||
}
|
||||
@@ -90,18 +90,17 @@ func validateFile(fileType FileType, content []byte) error {
|
||||
func ValidateFile(w http.ResponseWriter, r *http.Request) {
|
||||
fileType := FileType(r.PathValue("type"))
|
||||
if !fileType.IsValid() {
|
||||
U.RespondError(w, U.ErrInvalidKey("type"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "invalid file type")
|
||||
return
|
||||
}
|
||||
content, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
r.Body.Close()
|
||||
err = validateFile(fileType, content)
|
||||
if err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
if valErr := validateFile(fileType, content); valErr != nil {
|
||||
gphttp.JSONError(w, valErr, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
@@ -110,23 +109,23 @@ func ValidateFile(w http.ResponseWriter, r *http.Request) {
|
||||
func SetFileContent(w http.ResponseWriter, r *http.Request) {
|
||||
fileType, filename, err := getArgs(r)
|
||||
if err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
content, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if valErr := validateFile(fileType, content); valErr != nil {
|
||||
U.RespondError(w, valErr, http.StatusBadRequest)
|
||||
gphttp.JSONError(w, valErr, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(fileType.GetPath(filename), content, 0o644)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
@@ -6,17 +6,18 @@ import (
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/route/routes/routequery"
|
||||
)
|
||||
|
||||
func Health(w http.ResponseWriter, r *http.Request) {
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
U.PeriodicWS(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
gpwebsocket.Periodic(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
return wsjson.Write(r.Context(), conn, routequery.HealthMap())
|
||||
})
|
||||
} else {
|
||||
U.RespondJSON(w, r, routequery.HealthMap())
|
||||
gphttp.RespondJSON(w, r, routequery.HealthMap())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -37,13 +37,13 @@ type (
|
||||
func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
||||
what := r.FormValue("what")
|
||||
if what == "" {
|
||||
http.Error(w, "missing what or which", http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "missing what or which")
|
||||
return
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
r.Body.Close()
|
||||
@@ -53,21 +53,21 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
||||
case HomepageOverrideItem:
|
||||
var params HomepageOverrideItemParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
overrides.OverrideItem(params.Which, ¶ms.Value)
|
||||
case HomepageOverrideItemsBatch:
|
||||
var params HomepageOverrideItemsBatchParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
overrides.OverrideItems(params.Value)
|
||||
case HomepageOverrideItemVisible: // POST /v1/item_visible [a,b,c], false => hide a, b, c
|
||||
var params HomepageOverrideItemVisibleParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if params.Value {
|
||||
@@ -78,7 +78,7 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
||||
case HomepageOverrideCategoryOrder:
|
||||
var params HomepageOverrideCategoryOrderParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
overrides.SetCategoryOrder(params.Which, params.Value)
|
||||
|
||||
@@ -3,9 +3,9 @@ package v1
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
)
|
||||
|
||||
func Index(w http.ResponseWriter, r *http.Request) {
|
||||
WriteBody(w, []byte("API ready"))
|
||||
gphttp.WriteBody(w, []byte("API ready"))
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/route/routes/routequery"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
@@ -41,26 +42,25 @@ func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
case ListRoute:
|
||||
if route := listRoute(which); route == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
} else {
|
||||
U.RespondJSON(w, r, route)
|
||||
gphttp.RespondJSON(w, r, route)
|
||||
}
|
||||
case ListRoutes:
|
||||
U.RespondJSON(w, r, routequery.RoutesByAlias(route.RouteType(r.FormValue("type"))))
|
||||
gphttp.RespondJSON(w, r, routequery.RoutesByAlias(route.RouteType(r.FormValue("type"))))
|
||||
case ListFiles:
|
||||
listFiles(w, r)
|
||||
case ListMiddlewares:
|
||||
U.RespondJSON(w, r, middleware.All())
|
||||
gphttp.RespondJSON(w, r, middleware.All())
|
||||
case ListMiddlewareTraces:
|
||||
U.RespondJSON(w, r, middleware.GetAllTrace())
|
||||
gphttp.RespondJSON(w, r, middleware.GetAllTrace())
|
||||
case ListMatchDomains:
|
||||
U.RespondJSON(w, r, cfg.Value().MatchDomains)
|
||||
gphttp.RespondJSON(w, r, cfg.Value().MatchDomains)
|
||||
case ListHomepageConfig:
|
||||
U.RespondJSON(w, r, routequery.HomepageConfig(cfg.Value().Homepage.UseDefaultCategories, r.FormValue("category"), r.FormValue("provider")))
|
||||
gphttp.RespondJSON(w, r, routequery.HomepageConfig(cfg.Value().Homepage.UseDefaultCategories, r.FormValue("category"), r.FormValue("provider")))
|
||||
case ListRouteProviders:
|
||||
U.RespondJSON(w, r, cfg.RouteProviderList())
|
||||
gphttp.RespondJSON(w, r, cfg.RouteProviderList())
|
||||
case ListHomepageCategories:
|
||||
U.RespondJSON(w, r, routequery.HomepageCategories())
|
||||
gphttp.RespondJSON(w, r, routequery.HomepageCategories())
|
||||
case ListIcons:
|
||||
limit, err := strconv.Atoi(r.FormValue("limit"))
|
||||
if err != nil {
|
||||
@@ -68,17 +68,17 @@ func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
icons, err := internal.SearchIcons(r.FormValue("keyword"), limit)
|
||||
if err != nil {
|
||||
U.RespondError(w, err)
|
||||
gphttp.ClientError(w, err)
|
||||
return
|
||||
}
|
||||
if icons == nil {
|
||||
icons = []string{}
|
||||
}
|
||||
U.RespondJSON(w, r, icons)
|
||||
gphttp.RespondJSON(w, r, icons)
|
||||
case ListTasks:
|
||||
U.RespondJSON(w, r, task.DebugTaskList())
|
||||
gphttp.RespondJSON(w, r, task.DebugTaskList())
|
||||
default:
|
||||
U.HandleErr(w, r, U.ErrInvalidKey("what"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, fmt.Sprintf("invalid what: %s", what))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func listRoute(which string) any {
|
||||
func listFiles(w http.ResponseWriter, r *http.Request) {
|
||||
files, err := utils.ListFiles(common.ConfigBasePath, 0, true)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
resp := map[FileType][]string{
|
||||
@@ -116,12 +116,12 @@ func listFiles(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
mids, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
for _, mid := range mids {
|
||||
mid = strings.TrimPrefix(mid, common.MiddlewareComposeBasePath+"/")
|
||||
resp[FileTypeMiddleware] = append(resp[FileTypeMiddleware], mid)
|
||||
}
|
||||
U.RespondJSON(w, r, resp)
|
||||
gphttp.RespondJSON(w, r, resp)
|
||||
}
|
||||
|
||||
@@ -12,8 +12,9 @@ import (
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
@@ -21,27 +22,27 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
name := q.Get("name")
|
||||
if name == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("name"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("name"))
|
||||
return
|
||||
}
|
||||
host := q.Get("host")
|
||||
if host == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("host"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("host"))
|
||||
return
|
||||
}
|
||||
portStr := q.Get("port")
|
||||
if portStr == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("port"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("port"))
|
||||
return
|
||||
}
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
U.RespondError(w, U.ErrInvalidKey("port"))
|
||||
gphttp.ClientError(w, gphttp.ErrInvalidKey("port"))
|
||||
return
|
||||
}
|
||||
hostport := fmt.Sprintf("%s:%d", host, port)
|
||||
if _, ok := config.GetInstance().GetAgent(hostport); ok {
|
||||
U.RespondError(w, U.ErrAlreadyExists("agent", hostport), http.StatusConflict)
|
||||
gphttp.ClientError(w, gphttp.ErrAlreadyExists("agent", hostport), http.StatusConflict)
|
||||
return
|
||||
}
|
||||
t := q.Get("type")
|
||||
@@ -49,13 +50,13 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
||||
case "docker":
|
||||
break
|
||||
case "system":
|
||||
U.RespondError(w, U.Errorf("system agent is not supported yet"), http.StatusNotImplemented)
|
||||
gphttp.ClientError(w, gperr.Errorf("system agent is not supported yet"), http.StatusNotImplemented)
|
||||
return
|
||||
case "":
|
||||
U.RespondError(w, U.ErrMissingKey("type"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("type"))
|
||||
return
|
||||
default:
|
||||
U.RespondError(w, U.ErrInvalidKey("type"))
|
||||
gphttp.ClientError(w, gphttp.ErrInvalidKey("type"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
ca, srv, client, err := agent.NewAgent()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -83,11 +84,11 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
template, err := cfg.Generate()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
U.RespondJSON(w, r, map[string]any{
|
||||
gphttp.RespondJSON(w, r, map[string]any{
|
||||
"compose": template,
|
||||
"ca": ca,
|
||||
"client": client,
|
||||
@@ -98,7 +99,7 @@ func AddAgent(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
clientPEMData, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -109,24 +110,24 @@ func AddAgent(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(clientPEMData, &data); err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
nRoutesAdded, err := config.GetInstance().AddAgent(data.Host, data.CA, data.Client)
|
||||
if err != nil {
|
||||
U.RespondError(w, err)
|
||||
gphttp.ClientError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
zip, err := certs.ZipCert(data.CA.Cert, data.Client.Cert, data.Client.Key)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(certs.AgentCertsFilename(data.Host), zip, 0600); err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ package v1
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
)
|
||||
|
||||
func Reload(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
if err := cfg.Reload(); err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
U.WriteBody(w, []byte("OK"))
|
||||
gphttp.WriteBody(w, []byte("OK"))
|
||||
}
|
||||
|
||||
@@ -6,19 +6,20 @@ import (
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
func Stats(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
U.PeriodicWS(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
gpwebsocket.Periodic(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
return wsjson.Write(r.Context(), conn, getStats(cfg))
|
||||
})
|
||||
} else {
|
||||
U.RespondJSON(w, r, getStats(cfg))
|
||||
gphttp.RespondJSON(w, r, getStats(cfg))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"net/http"
|
||||
|
||||
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/http/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
)
|
||||
|
||||
func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
@@ -23,7 +23,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
|
||||
|
||||
agent, ok := cfg.GetAgent(agentAddr)
|
||||
if !ok {
|
||||
U.HandleErr(w, r, U.ErrInvalidKey("agent_addr"), http.StatusNotFound)
|
||||
gphttp.NotFound(w, "agent_addr")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,20 +31,20 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
|
||||
if !isWS {
|
||||
respData, status, err := agent.Forward(r, agentPkg.EndpointSystemInfo)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, E.Wrap(err, "failed to forward request to agent"))
|
||||
gphttp.ServerError(w, r, gperr.Wrap(err, "failed to forward request to agent"))
|
||||
return
|
||||
}
|
||||
if status != http.StatusOK {
|
||||
http.Error(w, string(respData), status)
|
||||
return
|
||||
}
|
||||
U.WriteBody(w, respData)
|
||||
gphttp.WriteBody(w, respData)
|
||||
} else {
|
||||
rp := reverseproxy.NewReverseProxy("agent", agentPkg.AgentURL, agent.Transport())
|
||||
header := r.Header.Clone()
|
||||
r, err := http.NewRequestWithContext(r.Context(), r.Method, agentPkg.EndpointSystemInfo+"?"+query.Encode(), nil)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, E.Wrap(err, "failed to create request"))
|
||||
gphttp.ServerError(w, r, gperr.Wrap(err, "failed to create request"))
|
||||
return
|
||||
}
|
||||
r.Header = header
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"syscall"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
||||
)
|
||||
|
||||
// HandleErr logs the error and returns an error code to the client.
|
||||
// If code is specified, it will be used as the HTTP status code; otherwise,
|
||||
// http.StatusInternalServerError is used.
|
||||
//
|
||||
// The error is only logged but not returned to the client.
|
||||
func HandleErr(w http.ResponseWriter, r *http.Request, err error, code ...int) {
|
||||
switch {
|
||||
case err == nil,
|
||||
errors.Is(err, context.Canceled),
|
||||
errors.Is(err, syscall.EPIPE),
|
||||
errors.Is(err, syscall.ECONNRESET):
|
||||
return
|
||||
}
|
||||
LogError(r).Msg(err.Error())
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
return
|
||||
}
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusInternalServerError}
|
||||
}
|
||||
http.Error(w, http.StatusText(code[0]), code[0])
|
||||
}
|
||||
|
||||
// RespondError returns error details to the client.
|
||||
// If code is specified, it will be used as the HTTP status code; otherwise,
|
||||
// http.StatusBadRequest is used.
|
||||
func RespondError(w http.ResponseWriter, err error, code ...int) {
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusBadRequest}
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
http.Error(w, ansi.StripANSI(err.Error()), code[0])
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...any) error {
|
||||
return E.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func ErrMissingKey(k string) error {
|
||||
return E.New(k + " is required")
|
||||
}
|
||||
|
||||
func ErrInvalidKey(k string) error {
|
||||
return E.New(k + " is invalid")
|
||||
}
|
||||
|
||||
func ErrAlreadyExists(k, v string) error {
|
||||
return E.Errorf("%s %q already exists", k, v)
|
||||
}
|
||||
|
||||
func ErrNotFound(k, v string) error {
|
||||
return E.Errorf("%s %q not found", k, v)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
)
|
||||
|
||||
var (
|
||||
httpClient = &http.Client{
|
||||
Timeout: common.ConnectionTimeout,
|
||||
Transport: &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
ForceAttemptHTTP2: false,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: common.DialTimeout,
|
||||
KeepAlive: common.KeepAlive, // this is different from DisableKeepAlives
|
||||
}).DialContext,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
}
|
||||
|
||||
Get = httpClient.Get
|
||||
Post = httpClient.Post
|
||||
Head = httpClient.Head
|
||||
)
|
||||
@@ -1,20 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
)
|
||||
|
||||
func reqLogger(r *http.Request, level zerolog.Level) *zerolog.Event {
|
||||
return logging.WithLevel(level).
|
||||
Str("remote", r.RemoteAddr).
|
||||
Str("host", r.Host).
|
||||
Str("uri", r.Method+" "+r.RequestURI)
|
||||
}
|
||||
|
||||
func LogError(r *http.Request) *zerolog.Event { return reqLogger(r, zerolog.ErrorLevel) }
|
||||
func LogWarn(r *http.Request) *zerolog.Event { return reqLogger(r, zerolog.WarnLevel) }
|
||||
func LogInfo(r *http.Request) *zerolog.Event { return reqLogger(r, zerolog.InfoLevel) }
|
||||
func LogDebug(r *http.Request) *zerolog.Event { return reqLogger(r, zerolog.DebugLevel) }
|
||||
@@ -1,38 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
)
|
||||
|
||||
func WriteBody(w http.ResponseWriter, body []byte) {
|
||||
if _, err := w.Write(body); err != nil {
|
||||
logging.Err(err).Msg("failed to write body")
|
||||
}
|
||||
}
|
||||
|
||||
func RespondJSON(w http.ResponseWriter, r *http.Request, data any, code ...int) (canProceed bool) {
|
||||
if len(code) > 0 {
|
||||
w.WriteHeader(code[0])
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var err error
|
||||
|
||||
switch data := data.(type) {
|
||||
case string:
|
||||
_, err = w.Write([]byte(fmt.Sprintf("%q", data)))
|
||||
case []byte:
|
||||
_, err = w.Write(data)
|
||||
default:
|
||||
err = json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
HandleErr(w, r, err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
)
|
||||
|
||||
func warnNoMatchDomains() {
|
||||
logging.Warn().Msg("no match domains configured, accepting websocket API request from all origins")
|
||||
}
|
||||
|
||||
var warnNoMatchDomainOnce sync.Once
|
||||
|
||||
func InitiateWS(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
|
||||
var originPats []string
|
||||
|
||||
localAddresses := []string{"127.0.0.1", "10.0.*.*", "172.16.*.*", "192.168.*.*"}
|
||||
|
||||
allowedDomains := httpheaders.WebsocketAllowedDomains(r.Header)
|
||||
if len(allowedDomains) == 0 || common.IsDebug {
|
||||
warnNoMatchDomainOnce.Do(warnNoMatchDomains)
|
||||
originPats = []string{"*"}
|
||||
} else {
|
||||
originPats = make([]string, len(allowedDomains))
|
||||
for i, domain := range allowedDomains {
|
||||
if domain[0] != '.' {
|
||||
originPats[i] = "*." + domain
|
||||
} else {
|
||||
originPats[i] = "*" + domain
|
||||
}
|
||||
}
|
||||
originPats = append(originPats, localAddresses...)
|
||||
}
|
||||
return websocket.Accept(w, r, &websocket.AcceptOptions{
|
||||
OriginPatterns: originPats,
|
||||
})
|
||||
}
|
||||
|
||||
func PeriodicWS(w http.ResponseWriter, r *http.Request, interval time.Duration, do func(conn *websocket.Conn) error) {
|
||||
conn, err := InitiateWS(w, r)
|
||||
if err != nil {
|
||||
HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
//nolint:errcheck
|
||||
defer conn.CloseNow()
|
||||
|
||||
if err := do(conn); err != nil {
|
||||
HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
if err := do(conn); err != nil {
|
||||
HandleErr(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,10 @@ package v1
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
)
|
||||
|
||||
func GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
WriteBody(w, []byte(pkg.GetVersion()))
|
||||
gphttp.WriteBody(w, []byte(pkg.GetVersion()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user