mirror of
https://github.com/oarkflow/mq.git
synced 2025-10-05 16:06:55 +08:00
feat: update
This commit is contained in:
16
services/examples/config/policies/web.json
Normal file
16
services/examples/config/policies/web.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"prefix": "/",
|
||||||
|
"middlewares": [
|
||||||
|
{"name": "cors"}
|
||||||
|
],
|
||||||
|
"static": {
|
||||||
|
"dir": "./public",
|
||||||
|
"prefix": "/",
|
||||||
|
"options": {
|
||||||
|
"byte_range": true,
|
||||||
|
"browse": true,
|
||||||
|
"compress": true,
|
||||||
|
"index_file": "index.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
services/examples/public/assets/InterVariable-CWi-zmRD.woff2
Normal file
BIN
services/examples/public/assets/InterVariable-CWi-zmRD.woff2
Normal file
Binary file not shown.
Binary file not shown.
1102
services/examples/public/assets/index-B0w5Q249.js
Normal file
1102
services/examples/public/assets/index-B0w5Q249.js
Normal file
File diff suppressed because one or more lines are too long
BIN
services/examples/public/assets/index-B0w5Q249.js.fiber.gz
Normal file
BIN
services/examples/public/assets/index-B0w5Q249.js.fiber.gz
Normal file
Binary file not shown.
1
services/examples/public/assets/index-Bc6Em-gk.css
Normal file
1
services/examples/public/assets/index-Bc6Em-gk.css
Normal file
File diff suppressed because one or more lines are too long
BIN
services/examples/public/assets/index-Bc6Em-gk.css.fiber.gz
Normal file
BIN
services/examples/public/assets/index-Bc6Em-gk.css.fiber.gz
Normal file
Binary file not shown.
14
services/examples/public/index.html
Normal file
14
services/examples/public/index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>RAM LIMO LLC.</title>
|
||||||
|
<script type="module" crossorigin src="/assets/index-B0w5Q249.js"></script>
|
||||||
|
<link rel="stylesheet" crossorigin href="/assets/index-Bc6Em-gk.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
services/examples/public/index.html.fiber.gz
Normal file
BIN
services/examples/public/index.html.fiber.gz
Normal file
Binary file not shown.
38
services/middlewares/rewrite/config.go
Normal file
38
services/middlewares/rewrite/config.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package rewrite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config defines the config for middleware.
|
||||||
|
type Config struct {
|
||||||
|
// Next defines a function to skip middleware.
|
||||||
|
// Optional. Default: nil
|
||||||
|
Next func(*fiber.Ctx) bool
|
||||||
|
|
||||||
|
// Rules defines the URL path rewrite rules. The values captured in asterisk can be
|
||||||
|
// retrieved by index e.g. $1, $2 and so on.
|
||||||
|
// Required. Example:
|
||||||
|
// "/old": "/new",
|
||||||
|
// "/api/*": "/$1",
|
||||||
|
// "/js/*": "/public/javascripts/$1",
|
||||||
|
// "/users/*/orders/*": "/user/$1/order/$2",
|
||||||
|
Rules map[string]string
|
||||||
|
|
||||||
|
rulesRegex map[*regexp.Regexp]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to set default values
|
||||||
|
func configDefault(config ...Config) Config {
|
||||||
|
// Return default config if nothing provided
|
||||||
|
if len(config) < 1 {
|
||||||
|
return Config{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override default config
|
||||||
|
cfg := config[0]
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
62
services/middlewares/rewrite/rewrite.go
Normal file
62
services/middlewares/rewrite/rewrite.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package rewrite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New creates a new middleware handler
|
||||||
|
func New(config ...Config) fiber.Handler {
|
||||||
|
cfg := configDefault(config...)
|
||||||
|
cfg.rulesRegex = map[*regexp.Regexp]string{}
|
||||||
|
for k, v := range cfg.Rules {
|
||||||
|
fmt.Printf("Rewriting \033[32m%s\033[0m ~> \033[33m%s\033[0m\n", k, v)
|
||||||
|
escaped := regexp.QuoteMeta(k)
|
||||||
|
pattern := strings.ReplaceAll(escaped, `\*`, `(.*)`)
|
||||||
|
pattern = "^" + pattern + "$"
|
||||||
|
rx, err := regexp.Compile(pattern)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Unable to compile regex: %v, %s", err, pattern)
|
||||||
|
} else {
|
||||||
|
cfg.rulesRegex[rx] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middleware function
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
// Next request to skip middleware
|
||||||
|
if cfg.Next != nil && cfg.Next(c) {
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
// Rewrite
|
||||||
|
for k, v := range cfg.rulesRegex {
|
||||||
|
replacer := captureTokens(k, c.Path())
|
||||||
|
if replacer != nil {
|
||||||
|
c.Path(replacer.Replace(v))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/labstack/echo/blob/master/middleware/rewrite.go
|
||||||
|
func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer {
|
||||||
|
groups := pattern.FindAllStringSubmatch(input, -1)
|
||||||
|
if groups == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
values := groups[0][1:]
|
||||||
|
replace := make([]string, 2*len(values))
|
||||||
|
for i, v := range values {
|
||||||
|
j := 2 * i
|
||||||
|
replace[j] = "$" + strconv.Itoa(i+1)
|
||||||
|
replace[j+1] = v
|
||||||
|
}
|
||||||
|
return strings.NewReplacer(replace...)
|
||||||
|
}
|
103
services/renderer/renderer.go
Normal file
103
services/renderer/renderer.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package renderer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"mime"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||||
|
"github.com/oarkflow/jet"
|
||||||
|
|
||||||
|
"github.com/oarkflow/mq/services/middlewares/rewrite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Prefix string `json:"prefix"`
|
||||||
|
Root string `json:"root"`
|
||||||
|
Index string `json:"index"`
|
||||||
|
UseIndex bool `json:"use_index"`
|
||||||
|
Compress bool `json:"compress"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(router fiber.Router, cfg ...Config) error {
|
||||||
|
var config Config
|
||||||
|
if len(cfg) > 0 {
|
||||||
|
config = cfg[0]
|
||||||
|
}
|
||||||
|
if config.Root == "" {
|
||||||
|
config.Root = "./"
|
||||||
|
}
|
||||||
|
if config.Prefix == "/" {
|
||||||
|
config.Prefix = ""
|
||||||
|
}
|
||||||
|
if config.UseIndex && config.Index == "" {
|
||||||
|
config.Index = "index.html"
|
||||||
|
}
|
||||||
|
if config.Compress {
|
||||||
|
router.Use(compress.New(compress.Config{
|
||||||
|
Level: compress.LevelBestSpeed,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
rules := make(map[string]string)
|
||||||
|
root := filepath.Clean(config.Root)
|
||||||
|
err := filepath.WalkDir(config.Root, func(path string, d os.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !d.IsDir() {
|
||||||
|
path = strings.TrimPrefix(path, root)
|
||||||
|
rules[path] = filepath.Join(config.Prefix, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
router.Use(rewrite.New(rewrite.Config{Rules: rules}))
|
||||||
|
router.Get(config.Prefix+"/*", handleStaticFile(config))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStaticFile(config Config) fiber.Handler {
|
||||||
|
return func(c *fiber.Ctx) error {
|
||||||
|
fullPath := c.Params("*")
|
||||||
|
filePath := filepath.Join(config.Root, fullPath)
|
||||||
|
fileInfo, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusNotFound).SendString(fmt.Sprintf("File %s not found", filePath))
|
||||||
|
}
|
||||||
|
isIndex := false
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
if !config.UseIndex {
|
||||||
|
return c.Status(fiber.StatusNotFound).SendString("Invalid file")
|
||||||
|
}
|
||||||
|
isIndex = true
|
||||||
|
filePath = filepath.Join(filePath, config.Index)
|
||||||
|
}
|
||||||
|
fileContent, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusNotFound).SendString(fmt.Sprintf("File %s not found", filePath))
|
||||||
|
}
|
||||||
|
if isIndex {
|
||||||
|
parser := jet.NewWithMemory(jet.WithDelims("{{", "}}"))
|
||||||
|
content, err := parser.ParseTemplate(string(fileContent), map[string]any{
|
||||||
|
"json_data": map[string]any{},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusNotFound).SendString(err.Error())
|
||||||
|
}
|
||||||
|
fileContent = []byte(content)
|
||||||
|
}
|
||||||
|
ext := filepath.Ext(filePath)
|
||||||
|
mimeType := mime.TypeByExtension(ext)
|
||||||
|
if mimeType == "" {
|
||||||
|
mimeType = "application/octet-stream"
|
||||||
|
}
|
||||||
|
c.Set("Content-Type", mimeType)
|
||||||
|
c.Set("Cache-Control", "public, max-age=31536000")
|
||||||
|
return c.Send(fileContent)
|
||||||
|
}
|
||||||
|
}
|
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/oarkflow/mq/dag"
|
"github.com/oarkflow/mq/dag"
|
||||||
"github.com/oarkflow/mq/services/http/responses"
|
"github.com/oarkflow/mq/services/http/responses"
|
||||||
"github.com/oarkflow/mq/services/middlewares"
|
"github.com/oarkflow/mq/services/middlewares"
|
||||||
|
"github.com/oarkflow/mq/services/renderer"
|
||||||
"github.com/oarkflow/mq/services/utils"
|
"github.com/oarkflow/mq/services/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -34,7 +35,10 @@ func Setup(loader *Loader, serverApp *fiber.App, brokerAddr string) {
|
|||||||
if loader.UserConfig == nil {
|
if loader.UserConfig == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
SetupServices(loader.Prefix(), serverApp, brokerAddr)
|
err := SetupServices(loader.Prefix(), serverApp, brokerAddr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupHandler(handler Handler, brokerAddr string, async ...bool) *dag.DAG {
|
func SetupHandler(handler Handler, brokerAddr string, async ...bool) *dag.DAG {
|
||||||
@@ -213,6 +217,23 @@ func SetupServices(prefix string, router fiber.Router, brokerAddr string) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
static := userConfig.Policy.Web.Static
|
||||||
|
if static != nil && static.Dir != "" {
|
||||||
|
router.Static(
|
||||||
|
static.Prefix,
|
||||||
|
static.Dir,
|
||||||
|
fiber.Static{
|
||||||
|
Compress: static.Options.Compress,
|
||||||
|
ByteRange: static.Options.ByteRange,
|
||||||
|
Browse: static.Options.Browse,
|
||||||
|
Index: static.Options.IndexFile,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
err = setupRender(prefix, router)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to setup render: %w", err)
|
||||||
|
}
|
||||||
return SetupAPI(prefix, router, brokerAddr)
|
return SetupAPI(prefix, router, brokerAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -441,6 +462,24 @@ func ruleMiddleware(rules map[string]string) fiber.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupRender(_ string, clientRoutes fiber.Router) error {
|
||||||
|
for _, cfg := range userConfig.Policy.Web.Render {
|
||||||
|
if cfg.Root != "" && cfg.ID != "" {
|
||||||
|
err := renderer.New(clientRoutes, renderer.Config{
|
||||||
|
Prefix: cfg.Prefix,
|
||||||
|
Root: cfg.Root,
|
||||||
|
Index: cfg.Index,
|
||||||
|
UseIndex: cfg.UseIndex,
|
||||||
|
Compress: cfg.Compress,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// requestMiddleware validates the request body in the original form of byte array
|
// requestMiddleware validates the request body in the original form of byte array
|
||||||
// against the provided request JSON schema to ensure that the request body is valid.
|
// against the provided request JSON schema to ensure that the request body is valid.
|
||||||
func requestMiddleware(prefix string, route *Route) fiber.Handler {
|
func requestMiddleware(prefix string, route *Route) fiber.Handler {
|
||||||
|
Reference in New Issue
Block a user