Feat/fileserver (#60)

* cleanup code for URL type

* fix makefile for trace mode

* refactor, merge Entry, RawEntry and Route into one. 

* Implement fileserver.

* refactor: rename HTTPRoute to ReverseProxyRoute to avoid confusion

* refactor: move metrics logger to middleware package

- fix prometheus metrics for load balanced routes
  - route will now fail when health monitor fail to start

* fix extra output of ls-* commands by defer initializaing stuff, speed up start time

* add test for path traversal attack, small fix on FileServer.Start method

* rename rule.on.bypass to pass

* refactor and fixed map-to-map  deserialization

* updated route loading logic

* schemas: add "add_prefix" option to modify_request middleware


* updated route JSONMarshalling

---------

Co-authored-by: yusing <yusing@6uo.me>
This commit is contained in:
Yuzerion
2025-02-06 18:23:10 +08:00
committed by GitHub
parent 4d47eb0e91
commit 1a5f3735cf
79 changed files with 1484 additions and 1276 deletions

View File

@@ -14,7 +14,6 @@ import (
"github.com/coreos/go-oidc/v3/oidc"
"github.com/golang-jwt/jwt/v5"
"github.com/yusing/go-proxy/internal/common"
E "github.com/yusing/go-proxy/internal/error"
"golang.org/x/oauth2"
. "github.com/yusing/go-proxy/internal/utils/testing"
@@ -227,7 +226,7 @@ func TestOIDCCallbackHandler(t *testing.T) {
}
if tt.wantStatus == http.StatusTemporaryRedirect {
setCookie := E.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
ExpectEqual(t, setCookie.Name, defaultAuth.TokenCookieName())
ExpectTrue(t, setCookie.Value != "")
ExpectEqual(t, setCookie.Path, "/")

View File

@@ -9,7 +9,6 @@ import (
"testing"
"time"
E "github.com/yusing/go-proxy/internal/error"
. "github.com/yusing/go-proxy/internal/utils/testing"
"golang.org/x/crypto/bcrypt"
)
@@ -17,7 +16,7 @@ import (
func newMockUserPassAuth() *UserPassAuth {
return &UserPassAuth{
username: "username",
pwdHash: E.Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
pwdHash: Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
secret: []byte("abcdefghijklmnopqrstuvwxyz"),
tokenTTL: time.Hour,
}
@@ -97,13 +96,13 @@ func TestUserPassLoginCallbackHandler(t *testing.T) {
w := httptest.NewRecorder()
req := &http.Request{
Host: "app.example.com",
Body: io.NopCloser(bytes.NewReader(E.Must(json.Marshal(tt.creds)))),
Body: io.NopCloser(bytes.NewReader(Must(json.Marshal(tt.creds)))),
}
auth.LoginCallbackHandler(w, req)
if tt.wantErr {
ExpectEqual(t, w.Code, http.StatusUnauthorized)
} else {
setCookie := E.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
ExpectTrue(t, setCookie.Name == auth.TokenCookieName())
ExpectTrue(t, setCookie.Value != "")
ExpectEqual(t, setCookie.Domain, "example.com")

View File

@@ -29,6 +29,9 @@ const (
)
func InitIconCache() {
iconCacheMu.Lock()
defer iconCacheMu.Unlock()
err := utils.LoadJSONIfExist(common.IconCachePath, &iconCache)
if err != nil {
logging.Error().Err(err).Msg("failed to load icon cache")
@@ -78,7 +81,7 @@ func pruneExpiredIconCache() {
}
func routeKey(r route.HTTPRoute) string {
return r.RawEntry().Provider + ":" + r.TargetName()
return r.ProviderName() + ":" + r.TargetName()
}
func PruneRouteIconCache(route route.HTTPRoute) {

View File

@@ -87,7 +87,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
}
var result *fetchResult
hp := r.RawEntry().Homepage.GetOverride()
hp := r.HomepageConfig().GetOverride()
if !hp.IsEmpty() && hp.Icon != nil {
if hp.Icon.IconSource == homepage.IconSourceRelative {
result = findIcon(r, req, hp.Icon.Value)
@@ -189,7 +189,7 @@ func findIcon(r route.HTTPRoute, req *http.Request, uri string) *fetchResult {
}
result := fetchIcon("png", sanitizeName(r.TargetName()))
cont := r.RawEntry().Container
cont := r.ContainerInfo()
if !result.OK() && cont != nil {
result = fetchIcon("png", sanitizeName(cont.ImageName))
}