Uses v2 configration schema

This commit is contained in:
2024-01-25 00:09:39 +08:00
parent 7ad17d9417
commit b906edc022
37 changed files with 542 additions and 437 deletions

View File

@ -4,34 +4,35 @@ import (
"io"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v2"
"github.com/pelletier/go-toml/v2"
)
var App *RoadApp
var R *RoadApp
func ReadInConfig(root string) error {
instance := &RoadApp{
Sites: []*SiteConfig{},
Regions: make([]*Region, 0),
}
if err := filepath.Walk(root, func(fp string, info os.FileInfo, err error) error {
var site SiteConfig
if err := filepath.Walk(root, func(fp string, info os.FileInfo, _ error) error {
var region Region
if info.IsDir() {
return nil
} else if file, err := os.OpenFile(fp, os.O_RDONLY, 0755); err != nil {
return err
} else if data, err := io.ReadAll(file); err != nil {
return err
} else if err := yaml.Unmarshal(data, &site); err != nil {
} else if err := toml.Unmarshal(data, &region); err != nil {
return err
} else {
defer file.Close()
// Extract file name as site id
site.ID = strings.SplitN(filepath.Base(fp), ".", 2)[0]
instance.Sites = append(instance.Sites, &site)
if region.Disabled {
return nil
}
instance.Regions = append(instance.Regions, &region)
}
return nil
@ -39,7 +40,7 @@ func ReadInConfig(root string) error {
return err
}
App = instance
R = instance
return nil
}

View File

@ -18,16 +18,16 @@ import (
"github.com/valyala/fasthttp"
)
func makeHypertextResponse(c *fiber.Ctx, upstream *UpstreamInstance) error {
func makeHypertextResponse(c *fiber.Ctx, dest *Destination) error {
timeout := time.Duration(viper.GetInt64("performance.network_timeout")) * time.Millisecond
return proxy.Do(c, upstream.MakeURI(c), &fasthttp.Client{
return proxy.Do(c, dest.MakeUri(c), &fasthttp.Client{
ReadTimeout: timeout,
WriteTimeout: timeout,
})
}
func makeFileResponse(c *fiber.Ctx, upstream *UpstreamInstance) error {
uri, queries := upstream.GetRawURI()
func makeFileResponse(c *fiber.Ctx, dest *Destination) error {
uri, queries := dest.GetRawUri()
root := http.Dir(uri)
method := c.Method()

View File

@ -1,49 +1,24 @@
package navi
import (
"errors"
"math/rand"
"code.smartsheep.studio/goatworks/roadsign/pkg/navi/transformers"
"github.com/gofiber/fiber/v2"
)
type RoadApp struct {
Sites []*SiteConfig `json:"sites"`
Regions []*Region `json:"regions"`
}
func (v *RoadApp) Forward(ctx *fiber.Ctx, site *SiteConfig) error {
if len(site.Upstreams) == 0 {
return errors.New("invalid configuration")
}
// Do forward
idx := rand.Intn(len(site.Upstreams))
upstream := site.Upstreams[idx]
switch upstream.GetType() {
case UpstreamTypeHypertext:
return makeHypertextResponse(ctx, upstream)
case UpstreamTypeFile:
return makeFileResponse(ctx, upstream)
func (v *RoadApp) Forward(ctx *fiber.Ctx, dest *Destination) error {
switch dest.GetType() {
case DestinationHypertext:
return makeHypertextResponse(ctx, dest)
case DestinationStaticFile:
return makeFileResponse(ctx, dest)
default:
return fiber.ErrBadGateway
}
}
type RequestTransformerConfig = transformers.RequestTransformerConfig
type SiteConfig struct {
ID string `json:"id"`
Rules []*RouterRule `json:"rules" yaml:"rules"`
Transformers []*RequestTransformerConfig `json:"transformers" yaml:"transformers"`
Upstreams []*UpstreamInstance `json:"upstreams" yaml:"upstreams"`
}
type RouterRule struct {
Host []string `json:"host" yaml:"host"`
Path []string `json:"path" yaml:"path"`
Queries map[string]string `json:"queries" yaml:"queries"`
Headers map[string][]string `json:"headers" yaml:"headers"`
}
type RequestTransformerConfig = transformers.TransformerConfig

78
pkg/navi/struct.go Normal file
View File

@ -0,0 +1,78 @@
package navi
import (
"fmt"
"net/url"
"strings"
"code.smartsheep.studio/goatworks/roadsign/pkg/navi/transformers"
"code.smartsheep.studio/goatworks/roadsign/pkg/warden"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
)
type Region struct {
ID string `json:"id" toml:"id"`
Disabled bool `json:"disabled" toml:"disabled"`
Locations []Location `json:"locations" toml:"locations"`
Applications []warden.Application `json:"applications" toml:"applications"`
}
type Location struct {
ID string `json:"id" toml:"id"`
Host []string `json:"host" toml:"host"`
Path []string `json:"path" toml:"path"`
Queries map[string]string `json:"queries" toml:"queries"`
Headers map[string][]string `json:"headers" toml:"headers"`
Destinations []Destination `json:"destinations" toml:"destinations"`
}
type DestinationType = int8
const (
DestinationHypertext = DestinationType(iota)
DestinationStaticFile
DestinationUnknown
)
type Destination struct {
ID string `json:"id" toml:"id"`
Uri string `json:"uri" toml:"uri"`
Transformers []transformers.TransformerConfig `json:"transformers" toml:"transformers"`
}
func (v *Destination) GetType() DestinationType {
protocol := strings.SplitN(v.Uri, "://", 2)[0]
switch protocol {
case "http", "https":
return DestinationHypertext
case "file", "files":
return DestinationStaticFile
}
return DestinationUnknown
}
func (v *Destination) GetRawUri() (string, url.Values) {
uri := strings.SplitN(v.Uri, "://", 2)[1]
data := strings.SplitN(uri, "?", 2)
data = append(data, " ") // Make data array least have two element
qs, _ := url.ParseQuery(data[0])
return data[0], qs
}
func (v *Destination) MakeUri(ctx *fiber.Ctx) string {
var queries []string
for k, v := range ctx.Queries() {
parsed, _ := url.QueryUnescape(v)
value := url.QueryEscape(parsed)
queries = append(queries, fmt.Sprintf("%s=%s", k, value))
}
path := string(ctx.Request().URI().Path())
hash := string(ctx.Request().URI().Hash())
return v.Uri + path +
lo.Ternary(len(queries) > 0, "?"+strings.Join(queries, "&"), "") +
lo.Ternary(len(hash) > 0, "#"+hash, "")
}

View File

@ -5,13 +5,13 @@ import (
"github.com/valyala/fasthttp"
)
var CompressResponse = RequestTransformer{
var CompressResponse = Transformer{
ModifyResponse: func(options any, ctx *fiber.Ctx) error {
opts := DeserializeOptions[struct {
Level int `json:"level" yaml:"level"`
Level int `json:"level" toml:"level"`
}](options)
var fctx = func(c *fasthttp.RequestCtx) {}
fctx := func(c *fasthttp.RequestCtx) {}
var compressor fasthttp.RequestHandler
switch opts.Level {
// Best Speed Mode

View File

@ -9,17 +9,17 @@ import (
var json = jsoniter.ConfigCompatibleWithStandardLibrary
type RequestTransformer struct {
type Transformer struct {
ModifyRequest func(options any, ctx *fiber.Ctx) error
ModifyResponse func(options any, ctx *fiber.Ctx) error
}
type RequestTransformerConfig struct {
Type string `json:"type" yaml:"type"`
Options any `json:"options" yaml:"options"`
type TransformerConfig struct {
Type string `json:"type" toml:"type"`
Options any `json:"options" toml:"options"`
}
func (v *RequestTransformerConfig) TransformRequest(ctx *fiber.Ctx) error {
func (v *TransformerConfig) TransformRequest(ctx *fiber.Ctx) error {
for k, f := range Transformers {
if k == v.Type {
if f.ModifyRequest != nil {
@ -31,7 +31,7 @@ func (v *RequestTransformerConfig) TransformRequest(ctx *fiber.Ctx) error {
return nil
}
func (v *RequestTransformerConfig) TransformResponse(ctx *fiber.Ctx) error {
func (v *TransformerConfig) TransformResponse(ctx *fiber.Ctx) error {
for k, f := range Transformers {
if k == v.Type {
if f.ModifyResponse != nil {
@ -55,7 +55,7 @@ func DeserializeOptions[T any](data any) T {
// Map of Transformers
// Every transformer need to be mapped here so that they can get work.
var Transformers = map[string]RequestTransformer{
var Transformers = map[string]Transformer{
"replacePath": ReplacePath,
"compressResponse": CompressResponse,
}

View File

@ -1,18 +1,19 @@
package transformers
import (
"github.com/gofiber/fiber/v2"
"regexp"
"strings"
"github.com/gofiber/fiber/v2"
)
var ReplacePath = RequestTransformer{
var ReplacePath = Transformer{
ModifyRequest: func(options any, ctx *fiber.Ctx) error {
opts := DeserializeOptions[struct {
Pattern string `json:"pattern" yaml:"pattern"`
Value string `json:"value" yaml:"value"`
Repl string `json:"repl" yaml:"repl"` // Use when complex mode(regexp) enabled
Complex bool `json:"complex" yaml:"complex"`
Pattern string `json:"pattern" toml:"pattern"`
Value string `json:"value" toml:"value"`
Repl string `json:"repl" toml:"repl"` // Use when complex mode(regexp) enabled
Complex bool `json:"complex" toml:"complex"`
}](options)
path := string(ctx.Request().URI().Path())
if !opts.Complex {

View File

@ -1,58 +0,0 @@
package navi
import (
"fmt"
"net/url"
"strings"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
)
const (
UpstreamTypeFile = "file"
UpstreamTypeHypertext = "hypertext"
UpstreamTypeUnknown = "unknown"
)
type UpstreamInstance struct {
ID string `json:"id" yaml:"id"`
URI string `json:"uri" yaml:"uri"`
}
func (v *UpstreamInstance) GetType() string {
protocol := strings.SplitN(v.URI, "://", 2)[0]
switch protocol {
case "file", "files":
return UpstreamTypeFile
case "http", "https":
return UpstreamTypeHypertext
}
return UpstreamTypeUnknown
}
func (v *UpstreamInstance) GetRawURI() (string, url.Values) {
uri := strings.SplitN(v.URI, "://", 2)[1]
data := strings.SplitN(uri, "?", 2)
data = append(data, " ") // Make data array least have two element
qs, _ := url.ParseQuery(data[0])
return data[0], qs
}
func (v *UpstreamInstance) MakeURI(ctx *fiber.Ctx) string {
var queries []string
for k, v := range ctx.Queries() {
parsed, _ := url.QueryUnescape(v)
value := url.QueryEscape(parsed)
queries = append(queries, fmt.Sprintf("%s=%s", k, value))
}
path := string(ctx.Request().URI().Path())
hash := string(ctx.Request().URI().Hash())
return v.URI + path +
lo.Ternary(len(queries) > 0, "?"+strings.Join(queries, "&"), "") +
lo.Ternary(len(hash) > 0, "#"+hash, "")
}

17
pkg/navi/warden.go Normal file
View File

@ -0,0 +1,17 @@
package navi
import "code.smartsheep.studio/goatworks/roadsign/pkg/warden"
func InitializeWarden(regions []*Region) {
for _, region := range regions {
for _, application := range region.Applications {
warden.InstancePool = append(warden.InstancePool, &warden.AppInstance{
Manifest: application,
})
}
}
for _, instance := range warden.InstancePool {
instance.Wake()
}
}