mirror of
https://github.com/oarkflow/mq.git
synced 2025-09-26 20:11:16 +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/services/http/responses"
|
||||
"github.com/oarkflow/mq/services/middlewares"
|
||||
"github.com/oarkflow/mq/services/renderer"
|
||||
"github.com/oarkflow/mq/services/utils"
|
||||
)
|
||||
|
||||
@@ -34,7 +35,10 @@ func Setup(loader *Loader, serverApp *fiber.App, brokerAddr string) {
|
||||
if loader.UserConfig == nil {
|
||||
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 {
|
||||
@@ -213,6 +217,23 @@ func SetupServices(prefix string, router fiber.Router, brokerAddr string) error
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// against the provided request JSON schema to ensure that the request body is valid.
|
||||
func requestMiddleware(prefix string, route *Route) fiber.Handler {
|
||||
|
Reference in New Issue
Block a user