feat: add middleware, route groups, and codebase cleanup
* feat: add middleware example demonstrating route groups Self-contained example covering v.Use(), v.Group(), nested groups, Group.Use(), and middleware chaining with role-based access control. * feat: add per-action middleware via WithMiddleware ActionOption Reuses the existing Middleware type so the same auth/logging functions work at both page and action level. Middleware runs after CSRF and rate-limit checks, with full access to session and signals. * feat: add RedirectView helper and refactor session example to use middleware RedirectView lets middleware abort and redirect in one step. The session example now uses an authRequired middleware on a route group instead of an inline check inside the view. * fix: remove dead code, fix double Load and extractParams mismatch - Remove componentRegistry (written, never read) - Remove unused signal methods: Bytes, Int64, Float - Remove unreachable nil check in registerCtx - Simplify injectRouteParams (extractParams already returns fresh map) - Fix double sync.Map.Load in injectSignals - Merge Shutdown/shutdown into single method - Inline currSessionNum - Fix extractParams: mismatched literal segment now returns nil - Minor: new(bytes.Buffer), go c.Sync(), genRandID reads 4 bytes
This commit is contained in:
28
context.go
28
context.go
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -25,8 +24,7 @@ type Context struct {
|
||||
app *V
|
||||
view func() h.H
|
||||
routeParams map[string]string
|
||||
componentRegistry map[string]*Context
|
||||
parentPageCtx *Context
|
||||
parentPageCtx *Context
|
||||
patchChan chan patch
|
||||
actionLimiter *rate.Limiter
|
||||
actionRegistry map[string]actionEntry
|
||||
@@ -81,7 +79,6 @@ func (c *Context) Component(initCtx func(c *Context)) func() h.H {
|
||||
compCtx.parentPageCtx = c
|
||||
}
|
||||
initCtx(compCtx)
|
||||
c.componentRegistry[id] = compCtx
|
||||
return compCtx.view
|
||||
}
|
||||
|
||||
@@ -208,14 +205,14 @@ func (c *Context) injectSignals(sigs map[string]any) {
|
||||
defer c.mu.Unlock()
|
||||
|
||||
for sigID, val := range sigs {
|
||||
if _, ok := c.signals.Load(sigID); !ok {
|
||||
item, ok := c.signals.Load(sigID)
|
||||
if !ok {
|
||||
c.signals.Store(sigID, &signal{
|
||||
id: sigID,
|
||||
val: val,
|
||||
})
|
||||
continue
|
||||
}
|
||||
item, _ := c.signals.Load(sigID)
|
||||
if sig, ok := item.(*signal); ok {
|
||||
sig.val = val
|
||||
sig.changed = false
|
||||
@@ -266,7 +263,7 @@ func (c *Context) sendPatch(p patch) {
|
||||
// Sync pushes the current view state and signal changes to the browser immediately
|
||||
// over the live SSE event stream.
|
||||
func (c *Context) Sync() {
|
||||
elemsPatch := bytes.NewBuffer(make([]byte, 0))
|
||||
elemsPatch := new(bytes.Buffer)
|
||||
if err := c.view().Render(elemsPatch); err != nil {
|
||||
c.app.logErr(c, "sync view failed: %v", err)
|
||||
return
|
||||
@@ -331,6 +328,15 @@ func (c *Context) ExecScript(s string) {
|
||||
c.sendPatch(patch{patchTypeScript, s})
|
||||
}
|
||||
|
||||
// RedirectView sets a view that redirects the browser to the given URL.
|
||||
// Use this in middleware to abort the chain and redirect in one step.
|
||||
func (c *Context) RedirectView(url string) {
|
||||
c.View(func() h.H {
|
||||
c.Redirect(url)
|
||||
return h.Div()
|
||||
})
|
||||
}
|
||||
|
||||
// Redirect navigates the browser to the given URL.
|
||||
// This triggers a full page navigation - the current context will be disposed
|
||||
// and a new context created at the destination URL.
|
||||
@@ -386,12 +392,9 @@ func (c *Context) injectRouteParams(params map[string]string) {
|
||||
if params == nil {
|
||||
return
|
||||
}
|
||||
m := make(map[string]string)
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
maps.Copy(m, params)
|
||||
c.routeParams = m
|
||||
|
||||
c.routeParams = params
|
||||
}
|
||||
|
||||
// GetPathParam retrieves the value from the page request URL for the given parameter name
|
||||
@@ -488,8 +491,7 @@ func newContext(id string, route string, v *V) *Context {
|
||||
csrfToken: genCSRFToken(),
|
||||
routeParams: make(map[string]string),
|
||||
app: v,
|
||||
componentRegistry: make(map[string]*Context),
|
||||
actionLimiter: newLimiter(v.actionRateLimit, defaultActionRate, defaultActionBurst),
|
||||
actionLimiter: newLimiter(v.actionRateLimit, defaultActionRate, defaultActionBurst),
|
||||
actionRegistry: make(map[string]actionEntry),
|
||||
signals: new(sync.Map),
|
||||
patchChan: make(chan patch, 1),
|
||||
|
||||
Reference in New Issue
Block a user