重构项目

This commit is contained in:
dragon
2025-11-12 17:32:25 +08:00
parent adb9bf25b8
commit 351e1c2ad1
16 changed files with 441 additions and 344 deletions

12
pkg/filters/common.go Normal file
View File

@@ -0,0 +1,12 @@
package filters
import "gopkg.d7z.net/gitea-pages/pkg/core"
func DefaultFilters() map[string]core.FilterInstance {
return map[string]core.FilterInstance{
"redirect": FilterInstRedirect,
"direct": FilterInstDirect,
"reverse_proxy": FilterInstProxy,
"default_not_found": FilterInstDefaultNotFound,
}
}

30
pkg/filters/default.go Normal file
View File

@@ -0,0 +1,30 @@
package filters
import (
"context"
"io"
"net/http"
"os"
"github.com/pkg/errors"
"gopkg.d7z.net/gitea-pages/pkg/core"
)
var FilterInstDefaultNotFound core.FilterInstance = func(config core.FilterParams) (core.FilterCall, error) {
return func(ctx context.Context, writer http.ResponseWriter, request *http.Request, metadata *core.PageDomainContent, next core.NextCall) error {
err := next(ctx, writer, request, metadata)
if err != nil && errors.Is(err, os.ErrNotExist) {
open, err := metadata.NativeOpen(ctx, "/404.html", nil)
if open != nil {
defer open.Body.Close()
}
if err != nil {
return err
}
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
writer.WriteHeader(http.StatusNotFound)
_, _ = io.Copy(writer, open.Body)
}
return nil
}, nil
}

60
pkg/filters/direct.go Normal file
View File

@@ -0,0 +1,60 @@
package filters
import (
"context"
"io"
"net/http"
"os"
"path/filepath"
"time"
"github.com/pkg/errors"
"go.uber.org/zap"
"gopkg.d7z.net/gitea-pages/pkg/core"
)
var FilterInstDirect core.FilterInstance = func(config core.FilterParams) (core.FilterCall, error) {
var param struct {
Prefix string `json:"prefix"`
}
if err := config.Unmarshal(&param); err != nil {
return nil, err
}
return func(ctx context.Context, writer http.ResponseWriter, request *http.Request, metadata *core.PageDomainContent, next core.NextCall) error {
var resp *http.Response
var path string
var err error
failback := []string{param.Prefix + metadata.Path, param.Prefix + metadata.Path + "/index.html"}
for _, p := range failback {
resp, err = metadata.NativeOpen(request.Context(), p, nil)
if err != nil {
if resp != nil {
resp.Body.Close()
}
if !errors.Is(err, os.ErrNotExist) {
zap.L().Debug("error", zap.Any("error", err))
}
continue
}
path = p
break
}
if resp == nil {
return os.ErrNotExist
}
defer resp.Body.Close()
if err != nil {
return err
}
writer.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
lastMod, err := time.Parse(http.TimeFormat, resp.Header.Get("Last-Modified"))
if err == nil {
if seeker, ok := resp.Body.(io.ReadSeeker); ok {
http.ServeContent(writer, request, filepath.Base(path), lastMod, seeker)
return nil
}
}
_, err = io.Copy(writer, resp.Body)
return err
}, nil
}

49
pkg/filters/proxy.go Normal file
View File

@@ -0,0 +1,49 @@
package filters
import (
"context"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"go.uber.org/zap"
"gopkg.d7z.net/gitea-pages/pkg/core"
"gopkg.d7z.net/gitea-pages/pkg/utils"
)
var FilterInstProxy core.FilterInstance = func(config core.FilterParams) (core.FilterCall, error) {
var param struct {
Prefix string `json:"prefix"`
Target string `json:"target"`
}
if err := config.Unmarshal(&param); err != nil {
return nil, err
}
return func(ctx context.Context, writer http.ResponseWriter, request *http.Request, metadata *core.PageDomainContent, next core.NextCall) error {
proxyPath := "/" + metadata.Path
targetPath := strings.TrimPrefix(proxyPath, param.Prefix)
if !strings.HasPrefix(targetPath, "/") {
targetPath = "/" + targetPath
}
u, _ := url.Parse(param.Target)
request.URL.Path = targetPath
request.RequestURI = request.URL.RequestURI()
proxy := httputil.NewSingleHostReverseProxy(u)
// todo: 处理透传
// proxy.Transport = s.options.HTTPClient.Transport
if host, _, err := net.SplitHostPort(request.RemoteAddr); err == nil {
request.Header.Set("X-Real-IP", host)
}
request.Header.Set("X-Page-IP", utils.GetRemoteIP(request))
request.Header.Set("X-Page-Refer", fmt.Sprintf("%s/%s/%s", metadata.Owner, metadata.Repo, metadata.Path))
request.Header.Set("X-Page-Host", request.Host)
zap.L().Debug("命中反向代理", zap.Any("prefix", param.Prefix), zap.Any("target", param.Target),
zap.Any("path", proxyPath), zap.Any("target", fmt.Sprintf("%s%s", u, targetPath)))
// todo(security): 处理 websocket
proxy.ServeHTTP(writer, request)
return nil
}, nil
}

46
pkg/filters/redirect.go Normal file
View File

@@ -0,0 +1,46 @@
package filters
import (
"context"
"fmt"
"net/http"
"net/url"
"regexp"
"slices"
"strings"
"go.uber.org/zap"
"gopkg.d7z.net/gitea-pages/pkg/core"
)
var portExp = regexp.MustCompile(`:\d+$`)
var FilterInstRedirect core.FilterInstance = func(config core.FilterParams) (core.FilterCall, error) {
var param struct {
Targets []string `json:"targets"`
}
if err := config.Unmarshal(&param); err != nil {
return nil, err
}
return func(ctx context.Context, writer http.ResponseWriter, request *http.Request, metadata *core.PageDomainContent, next core.NextCall) error {
domain := portExp.ReplaceAllString(strings.ToLower(request.Host), "")
if len(param.Targets) > 0 && !slices.Contains(metadata.Alias, domain) {
// 重定向到配置的地址
zap.L().Debug("redirect", zap.Any("src", request.Host), zap.Any("dst", param.Targets[0]))
path := metadata.Path
if strings.HasSuffix(path, "/index.html") || path == "index.html" {
path = strings.TrimSuffix(path, "index.html")
}
target, err := url.Parse(fmt.Sprintf("https://%s/%s", param.Targets[0], path))
if err != nil {
return err
}
target.RawQuery = request.URL.RawQuery
http.Redirect(writer, request, target.String(), http.StatusFound)
return nil
} else {
return next(ctx, writer, request, metadata)
}
}, nil
}