@@ -83,9 +83,10 @@ func (c *Controller) RegisterRoutes(router *mux.Router) {
8383 if c .devAssets != nil || config .Assets .FS != nil {
8484 c .registerAssetRoutes (router , fullAssetsPath )
8585 }
86+ excludedPrefixes := appRouteExcludedPrefixes (c .applet .BasePath (), config )
8687 pathRouter := router .PathPrefix (c .applet .BasePath ()).Subrouter ()
8788 c .applyMiddleware (pathRouter , config .Middleware )
88- c .registerAppRoutes (pathRouter , config .RoutePatterns )
89+ c .registerAppRoutes (pathRouter , config .RoutePatterns , excludedPrefixes )
8990
9091 for _ , host := range config .Hosts {
9192 host = strings .TrimSpace (host )
@@ -97,7 +98,7 @@ func (c *Controller) RegisterRoutes(router *mux.Router) {
9798 c .registerAssetRoutes (hostRouter , config .Assets .BasePath )
9899 }
99100 c .applyMiddleware (hostRouter , config .Middleware )
100- c .registerAppRoutes (hostRouter , config .RoutePatterns )
101+ c .registerAppRoutes (hostRouter , config .RoutePatterns , excludedPrefixes )
101102 }
102103}
103104
@@ -107,7 +108,7 @@ func (c *Controller) applyMiddleware(router *mux.Router, middleware []mux.Middle
107108 }
108109}
109110
110- func (c * Controller ) registerAppRoutes (router * mux.Router , routePatterns []string ) {
111+ func (c * Controller ) registerAppRoutes (router * mux.Router , routePatterns []string , excludedPrefixes [] string ) {
111112 for _ , p := range routePatterns {
112113 p = strings .TrimSpace (p )
113114 if p == "" {
@@ -120,5 +121,94 @@ func (c *Controller) registerAppRoutes(router *mux.Router, routePatterns []strin
120121 }
121122 router .HandleFunc ("" , c .RenderApp ).Methods (http .MethodGet , http .MethodHead )
122123 router .HandleFunc ("/" , c .RenderApp ).Methods (http .MethodGet , http .MethodHead )
123- router .PathPrefix ("/" ).HandlerFunc (c .RenderApp ).Methods (http .MethodGet , http .MethodHead )
124+ router .MatcherFunc (func (r * http.Request , _ * mux.RouteMatch ) bool {
125+ return shouldRenderAppRoute (r .URL .Path , excludedPrefixes )
126+ }).HandlerFunc (c .RenderApp ).Methods (http .MethodGet , http .MethodHead )
127+ }
128+
129+ func appRouteExcludedPrefixes (basePath string , config api.Config ) []string {
130+ candidates := []string {
131+ config .Assets .BasePath ,
132+ config .Endpoints .GraphQL ,
133+ config .Endpoints .Stream ,
134+ config .Endpoints .REST ,
135+ }
136+ if config .RPC != nil {
137+ candidates = append (candidates , config .RPC .Path )
138+ }
139+
140+ seen := make (map [string ]struct {}, len (candidates )* 2 )
141+ excluded := make ([]string , 0 , len (candidates )* 2 )
142+ for _ , candidate := range candidates {
143+ for _ , prefix := range normalizeExcludedRoutePrefixes (basePath , candidate ) {
144+ if prefix == "" {
145+ continue
146+ }
147+ if _ , exists := seen [prefix ]; exists {
148+ continue
149+ }
150+ seen [prefix ] = struct {}{}
151+ excluded = append (excluded , prefix )
152+ }
153+ }
154+
155+ return excluded
156+ }
157+
158+ func normalizeExcludedRoutePrefixes (basePath , raw string ) []string {
159+ raw = strings .TrimSpace (raw )
160+ if raw == "" {
161+ return nil
162+ }
163+ if ! strings .HasPrefix (raw , "/" ) {
164+ raw = "/" + raw
165+ }
166+
167+ basePath = strings .TrimSpace (basePath )
168+ if basePath == "" {
169+ basePath = "/"
170+ }
171+ if ! strings .HasPrefix (basePath , "/" ) {
172+ basePath = "/" + basePath
173+ }
174+ basePath = path .Clean (basePath )
175+ absolute := path .Clean (raw )
176+
177+ prefixes := make ([]string , 0 , 2 )
178+ prefixes = append (prefixes , absolute )
179+
180+ if absolute == basePath {
181+ prefixes = append (prefixes , "/" )
182+ return prefixes
183+ }
184+
185+ if strings .HasPrefix (absolute , basePath + "/" ) {
186+ relative := strings .TrimPrefix (absolute , basePath )
187+ if relative == "" {
188+ relative = "/"
189+ }
190+ prefixes = append (prefixes , path .Clean (relative ))
191+ }
192+
193+ return prefixes
194+ }
195+
196+ func shouldRenderAppRoute (requestPath string , excludedPrefixes []string ) bool {
197+ cleanPath := path .Clean ("/" + strings .TrimPrefix (strings .TrimSpace (requestPath ), "/" ))
198+ for _ , prefix := range excludedPrefixes {
199+ if pathHasPrefix (cleanPath , prefix ) {
200+ return false
201+ }
202+ }
203+ return true
204+ }
205+
206+ func pathHasPrefix (requestPath , prefix string ) bool {
207+ if prefix == "" {
208+ return false
209+ }
210+ if prefix == "/" {
211+ return requestPath == "/"
212+ }
213+ return requestPath == prefix || strings .HasPrefix (requestPath , prefix + "/" )
124214}
0 commit comments