重构路由
This commit is contained in:
@@ -1,46 +1,48 @@
|
||||
package core
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type PageConfig struct {
|
||||
Alias []string `yaml:"alias"` // 重定向地址
|
||||
Render map[string]string `yaml:"templates"` // 渲染器地址
|
||||
|
||||
VirtualRoute bool `yaml:"v-route"` // 是否使用虚拟路由(任何路径均使用 /index.html 返回 200 响应)
|
||||
ReverseProxy map[string]string `yaml:"proxy"` // 反向代理路由
|
||||
|
||||
Ignore string `yaml:"ignore"` // 跳过展示的内容
|
||||
Alias []string `yaml:"alias"` // 重定向地址
|
||||
Routes []PageConfigRoute `yaml:"routes"` // 路由配置
|
||||
}
|
||||
|
||||
func (p *PageConfig) Ignores() []string {
|
||||
i := make([]string, 0)
|
||||
if p.Ignore == "" {
|
||||
return i
|
||||
}
|
||||
for _, line := range strings.Split(p.Ignore, "\n") {
|
||||
for _, item := range strings.Split(line, ",") {
|
||||
item = strings.TrimSpace(item)
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
i = append(i, item)
|
||||
}
|
||||
}
|
||||
return i
|
||||
type PageConfigRoute struct {
|
||||
Path string `yaml:"path"`
|
||||
Type string `yaml:"type"`
|
||||
Params map[string]any `yaml:"params"`
|
||||
}
|
||||
|
||||
func (p *PageConfig) Renders() map[string]string {
|
||||
result := make(map[string]string)
|
||||
for sType, patterns := range p.Render {
|
||||
for _, line := range strings.Split(patterns, "\n") {
|
||||
for _, item := range strings.Split(line, ",") {
|
||||
item = strings.TrimSpace(item)
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
result[sType] = item
|
||||
}
|
||||
}
|
||||
func (p *PageConfigRoute) UnmarshalYAML(value *yaml.Node) error {
|
||||
var data map[string]any
|
||||
if err := value.Decode(&data); err != nil {
|
||||
return err
|
||||
}
|
||||
return result
|
||||
if item, ok := data["path"]; ok {
|
||||
p.Path = item.(string)
|
||||
} else {
|
||||
return errors.New("missing path field")
|
||||
}
|
||||
delete(data, "path")
|
||||
keys := make([]string, 0)
|
||||
for k := range data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
if len(keys) != 1 {
|
||||
return errors.New("invalid param")
|
||||
}
|
||||
p.Type = keys[0]
|
||||
params := data[p.Type]
|
||||
// 跳过空参数
|
||||
if _, ok := params.(string); ok || params == nil {
|
||||
return nil
|
||||
}
|
||||
out, err := yaml.Marshal(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return yaml.Unmarshal(out, &p.Params)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,21 @@ package core
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type FilterParams map[string]any
|
||||
|
||||
func (f FilterParams) String() string {
|
||||
marshal, _ := json.Marshal(f)
|
||||
return strings.ReplaceAll(string(marshal), "\"", "'")
|
||||
}
|
||||
|
||||
func (f FilterParams) Unmarshal(target any) error {
|
||||
marshal, err := json.Marshal(f)
|
||||
if err != nil {
|
||||
@@ -22,9 +32,12 @@ type Filter struct {
|
||||
Params FilterParams `json:"params"`
|
||||
}
|
||||
|
||||
func NextCallWrapper(call FilterCall, parentCall NextCall) NextCall {
|
||||
func NextCallWrapper(call FilterCall, parentCall NextCall, stack Filter) NextCall {
|
||||
return func(ctx context.Context, writer http.ResponseWriter, request *http.Request, metadata *PageDomainContent) error {
|
||||
return call(ctx, writer, request, metadata, parentCall)
|
||||
zap.L().Debug(fmt.Sprintf("call filter(%s) before", stack.Type), zap.Any("filter", stack))
|
||||
err := call(ctx, writer, request, metadata, parentCall)
|
||||
zap.L().Debug(fmt.Sprintf("call filter(%s) after", stack.Type), zap.Any("filter", stack), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +48,10 @@ type NextCall func(
|
||||
metadata *PageDomainContent,
|
||||
) error
|
||||
|
||||
var NotFountNextCall = func(ctx context.Context, writer http.ResponseWriter, request *http.Request, metadata *PageDomainContent) error {
|
||||
return os.ErrNotExist
|
||||
}
|
||||
|
||||
type FilterCall func(
|
||||
ctx context.Context,
|
||||
writer http.ResponseWriter,
|
||||
|
||||
117
pkg/core/meta.go
117
pkg/core/meta.go
@@ -5,13 +5,12 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.d7z.net/middleware/kv"
|
||||
"gopkg.d7z.net/middleware/tools"
|
||||
@@ -50,23 +49,18 @@ func NewEmptyPageMetaContent() *PageMetaContent {
|
||||
Filters: []Filter{
|
||||
{
|
||||
Path: "**",
|
||||
Type: "default_not_found",
|
||||
Type: "_404_",
|
||||
Params: map[string]any{},
|
||||
},
|
||||
{ // 默认阻塞
|
||||
Path: ".git/**",
|
||||
Type: "block",
|
||||
Params: map[string]any{
|
||||
"code": "404",
|
||||
"message": "Not found",
|
||||
},
|
||||
}, { // 默认阻塞
|
||||
Path: ".pages.yaml",
|
||||
Type: "block",
|
||||
Params: map[string]any{
|
||||
"code": "404",
|
||||
"message": "Not found",
|
||||
},
|
||||
Path: ".git/**",
|
||||
Type: "block",
|
||||
Params: map[string]any{},
|
||||
},
|
||||
{ // 默认阻塞
|
||||
Path: ".pages.yaml",
|
||||
Type: "block",
|
||||
Params: map[string]any{},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -161,11 +155,20 @@ func (s *ServerMeta) parsePageConfig(ctx context.Context, meta *PageMetaContent,
|
||||
defer func(alias *[]string) {
|
||||
meta.Alias = *alias
|
||||
direct := *alias
|
||||
if len(direct) > 0 {
|
||||
meta.Filters = append(meta.Filters, Filter{
|
||||
Path: "**",
|
||||
Type: "redirect",
|
||||
Params: map[string]any{
|
||||
"targets": direct,
|
||||
},
|
||||
})
|
||||
}
|
||||
meta.Filters = append(meta.Filters, Filter{
|
||||
Path: "**",
|
||||
Type: "redirect",
|
||||
Type: "direct",
|
||||
Params: map[string]any{
|
||||
"targets": direct,
|
||||
"prefix": "",
|
||||
},
|
||||
})
|
||||
}(&alias)
|
||||
@@ -187,76 +190,36 @@ func (s *ServerMeta) parsePageConfig(ctx context.Context, meta *PageMetaContent,
|
||||
if err = yaml.Unmarshal([]byte(data), cfg); err != nil {
|
||||
return errors.Wrap(err, "parse .pages.yaml failed")
|
||||
}
|
||||
if cfg.VirtualRoute {
|
||||
meta.Filters = append(meta.Filters, Filter{
|
||||
Path: "**",
|
||||
Type: "forward",
|
||||
Params: map[string]any{
|
||||
"path": "index.html",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 处理别名
|
||||
for _, cname := range cfg.Alias {
|
||||
if cname == "" {
|
||||
for _, item := range cfg.Alias {
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
if al, ok := s.aliasCheck(cname); ok {
|
||||
if al, ok := s.aliasCheck(item); ok {
|
||||
alias = append(alias, al)
|
||||
} else {
|
||||
return fmt.Errorf("invalid alias %s", cname)
|
||||
return fmt.Errorf("invalid alias %s", item)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理渲染器
|
||||
for sType, pattern := range cfg.Renders() {
|
||||
meta.Filters = append(meta.Filters, Filter{
|
||||
Path: pattern,
|
||||
Type: sType,
|
||||
Params: map[string]any{},
|
||||
})
|
||||
}
|
||||
|
||||
// 处理跳过内容
|
||||
for _, pattern := range cfg.Ignores() {
|
||||
meta.Filters = append(meta.Filters, Filter{ // 默认直连
|
||||
Path: pattern,
|
||||
Type: "block",
|
||||
Params: map[string]any{
|
||||
"code": "404",
|
||||
"message": "Not found",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// 处理反向代理
|
||||
for path, backend := range cfg.ReverseProxy {
|
||||
path = filepath.ToSlash(filepath.Clean(path))
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
// 处理自定义路由
|
||||
for _, r := range cfg.Routes {
|
||||
for _, item := range strings.Split(r.Path, ",") {
|
||||
item = strings.TrimSpace(item)
|
||||
if item == "" {
|
||||
continue
|
||||
}
|
||||
if _, err := glob.Compile(item); err != nil {
|
||||
return errors.Wrapf(err, "invalid route glob pattern: %s", item)
|
||||
}
|
||||
meta.Filters = append(meta.Filters, Filter{
|
||||
Path: item,
|
||||
Type: r.Type,
|
||||
Params: r.Params,
|
||||
})
|
||||
}
|
||||
path = strings.TrimSuffix(path, "/")
|
||||
|
||||
rURL, err := url.Parse(backend)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "parse backend url failed: %s", backend)
|
||||
}
|
||||
|
||||
if rURL.Scheme != "http" && rURL.Scheme != "https" {
|
||||
return errors.Errorf("invalid backend url scheme: %s", backend)
|
||||
}
|
||||
meta.Filters = append(meta.Filters, Filter{
|
||||
Path: path,
|
||||
Type: "reverse_proxy",
|
||||
Params: map[string]any{
|
||||
"prefix": path,
|
||||
"target": rURL.String(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user