From 19c2d6b547bca81aaa30b5a0a14b1acd4dbc41ce Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 13 Feb 2026 23:44:28 +0800 Subject: [PATCH] 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. --- service/static.go | 29 ++++++++++++++++++++++++++--- sui/api/run.go | 14 +++++++++++++- sui/core/interfaces.go | 6 ++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/service/static.go b/service/static.go index 6e0c6fcd..71d30cce 100644 --- a/service/static.go +++ b/service/static.go @@ -10,6 +10,7 @@ import ( "github.com/yaoapp/yao/data" "github.com/yaoapp/yao/service/fs" "github.com/yaoapp/yao/share" + "github.com/yaoapp/yao/sui/core" ) // AppFileServer static file server @@ -24,13 +25,32 @@ var AdminRoot = "" // AdminRootLen cache 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 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 func SetupStatic() error { setupAdminRoot() @@ -64,12 +84,15 @@ func setupRewrite() { continue } - rewriteRules = append(rewriteRules, rewriteRule{ + rewriteRules = append(rewriteRules, RewriteRule{ Pattern: re, Replacement: replacement, }) } } + + // Register the route resolver for $Backend().Call() dynamic route support + core.RouteResolver = ResolveRoute } // rewrite path diff --git a/sui/api/run.go b/sui/api/run.go index bd2aef78..56e32699 100644 --- a/sui/api/run.go +++ b/sui/api/run.go @@ -70,8 +70,20 @@ func Run(process *process.Process) interface{} { // Get the page config 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" { exception.New("The page not found (%s)", 404, route).Throw() return nil diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 07c886d4..109274a4 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -18,6 +18,12 @@ var RouteExactMatchers = map[string][][]*Matcher{} // RouteRegexp the regexp for the route 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 type SUI interface { Setting() (*Setting, error)