Implement dynamic route resolution using rewrite rules

- Introduce a new `RewriteRule` type and associated functions for managing URL rewrite rules in `service/static.go`.
- Add `GetRewriteRules` and `ResolveRoute` functions to facilitate the retrieval and application of rewrite rules.
- Update the `sui/api/run.go` to utilize the new route resolver for handling dynamic routes when the config file is not found.
- Define `RouteResolver` in `sui/core/interfaces.go` to allow for dynamic route resolution across the application.
This commit is contained in:
Max 2026-02-13 23:44:28 +08:00
parent baebefdb02
commit 19c2d6b547
3 changed files with 45 additions and 4 deletions

View file

@ -10,6 +10,7 @@ import (
"github.com/yaoapp/yao/data" "github.com/yaoapp/yao/data"
"github.com/yaoapp/yao/service/fs" "github.com/yaoapp/yao/service/fs"
"github.com/yaoapp/yao/share" "github.com/yaoapp/yao/share"
"github.com/yaoapp/yao/sui/core"
) )
// AppFileServer static file server // AppFileServer static file server
@ -24,13 +25,32 @@ var AdminRoot = ""
// AdminRootLen cache // AdminRootLen cache
var AdminRootLen = 0 var AdminRootLen = 0
var rewriteRules = []rewriteRule{} var rewriteRules = []RewriteRule{}
type rewriteRule struct { // RewriteRule is a URL rewrite rule defined in app.yao
type RewriteRule struct {
Pattern *regexp.Regexp Pattern *regexp.Regexp
Replacement string Replacement string
} }
// GetRewriteRules returns the loaded rewrite rules for use by other packages (e.g., sui/api)
func GetRewriteRules() []RewriteRule {
return rewriteRules
}
// ResolveRoute applies rewrite rules to the given route path.
// Returns the rewritten path (with .sui suffix removed) and matched parameter values, or empty string if no rule matches.
func ResolveRoute(route string) (string, []string) {
for _, rule := range rewriteRules {
if matches := rule.Pattern.FindStringSubmatch(route); matches != nil {
rewritten := rule.Pattern.ReplaceAllString(route, rule.Replacement)
rewritten = strings.TrimSuffix(rewritten, ".sui")
return rewritten, matches
}
}
return "", nil
}
// SetupStatic setup static file server // SetupStatic setup static file server
func SetupStatic() error { func SetupStatic() error {
setupAdminRoot() setupAdminRoot()
@ -64,12 +84,15 @@ func setupRewrite() {
continue continue
} }
rewriteRules = append(rewriteRules, rewriteRule{ rewriteRules = append(rewriteRules, RewriteRule{
Pattern: re, Pattern: re,
Replacement: replacement, Replacement: replacement,
}) })
} }
} }
// Register the route resolver for $Backend().Call() dynamic route support
core.RouteResolver = ResolveRoute
} }
// rewrite path // rewrite path

View file

@ -70,8 +70,20 @@ func Run(process *process.Process) interface{} {
// Get the page config // Get the page config
cfg, err := getPageConfig(file, r.Request.DisableCache()) cfg, err := getPageConfig(file, r.Request.DisableCache())
if err != nil { if err != nil && err.Error() == "The config file not found" {
// Try to resolve dynamic route via rewrite rules (e.g., /agents/yao.keeper/entry/abc123 -> /agents/yao.keeper/entry/[id])
if core.RouteResolver != nil {
if resolved, _ := core.RouteResolver(route); resolved != "" {
resolvedFile := filepath.Join("/public", strings.TrimSuffix(resolved, ".sui"))
cfg, err = getPageConfig(resolvedFile, r.Request.DisableCache())
if err == nil {
file = resolvedFile
}
}
}
}
if err != nil {
if err.Error() == "The config file not found" { if err.Error() == "The config file not found" {
exception.New("The page not found (%s)", 404, route).Throw() exception.New("The page not found (%s)", 404, route).Throw()
return nil return nil

View file

@ -18,6 +18,12 @@ var RouteExactMatchers = map[string][][]*Matcher{}
// RouteRegexp the regexp for the route // RouteRegexp the regexp for the route
var RouteRegexp = regexp.MustCompile(`([a-z0-9A-Z_\-]+)`) var RouteRegexp = regexp.MustCompile(`([a-z0-9A-Z_\-]+)`)
// RouteResolver is a function that resolves a dynamic route path via rewrite rules.
// It takes an incoming route (e.g., /agents/yao.keeper/entry/abc123) and returns
// the resolved template path (e.g., /agents/yao.keeper/entry/[id]) and matched values.
// Set by the service package during initialization.
var RouteResolver func(route string) (string, []string)
// SUI is the interface for the SUI // SUI is the interface for the SUI
type SUI interface { type SUI interface {
Setting() (*Setting, error) Setting() (*Setting, error)