Feat v0.2.5 ui polish dead code (#9)
Some checks failed
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Failing after 5s
CI/CD / test-go-pg (push) Failing after 2m34s
CI/CD / test-sqlite (push) Successful in 2m39s
CI/CD / build-and-deploy (push) Has been skipped

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #9.
This commit is contained in:
2026-03-27 14:16:36 +00:00
committed by xcaliber
parent 389354826b
commit 3cc3360624
34 changed files with 369 additions and 806 deletions

View File

@@ -6,6 +6,8 @@ import (
"net/http"
"github.com/gin-gonic/gin"
"switchboard-core/middleware"
)
// SeedSurfaces writes core surface manifests to the registry table.
@@ -48,11 +50,10 @@ func (e *Engine) SeedSurfaces() {
}
// IsSurfaceEnabled checks if a surface is enabled in the registry.
// Chat and Admin are always enabled (system-critical).
// Admin is always enabled (system-critical).
// Returns true if the surface is not found (fail-open for backward compat).
func (e *Engine) IsSurfaceEnabled(surfaceID string) bool {
// Chat and Admin cannot be disabled — they're system-critical
if surfaceID == "chat" || surfaceID == "admin" {
if surfaceID == "admin" {
return true
}
if e.stores.Packages == nil {
@@ -92,22 +93,39 @@ func (e *Engine) EnabledSurfaceIDs() []string {
// DefaultSurfaceRedirect returns a handler for GET / that redirects to the
// configured default surface, falling back to the first enabled extension
// surface, then /admin.
// surface, then /welcome.
//
// This route has no auth middleware, so we opportunistically read the JWT
// from the cookie to check user preferences. If the cookie is missing or
// invalid, user preference is skipped (falls through to global default).
func (e *Engine) DefaultSurfaceRedirect() gin.HandlerFunc {
return func(c *gin.Context) {
target := e.resolveDefaultSurface(c.Request.Context())
userID := middleware.UserIDFromCookie(c, e.cfg.JWTSecret)
target := e.resolveDefaultSurface(c.Request.Context(), userID)
c.Redirect(http.StatusTemporaryRedirect, e.cfg.BasePath+target)
}
}
// resolveDefaultSurface returns the path to redirect to (without BasePath).
// Priority: configured default_surface → first enabled extension surface → /admin.
func (e *Engine) resolveDefaultSurface(ctx context.Context) string {
// Priority: user preference → global default_surface → first enabled extension → /welcome.
func (e *Engine) resolveDefaultSurface(ctx context.Context, userID string) string {
if e.stores.GlobalConfig == nil || e.stores.Packages == nil {
return "/admin"
return "/welcome"
}
// 1. Check configured default_surface
// 1. Check user preference (default_surface in user settings)
if userID != "" && e.stores.Users != nil {
if user, err := e.stores.Users.GetByID(ctx, userID); err == nil && user != nil {
if id, _ := user.Settings["default_surface"].(string); id != "" {
if path := e.surfacePath(ctx, id); path != "" {
return path
}
// User's chosen surface is missing or disabled — fall through
}
}
}
// 2. Check admin-configured global default_surface
if raw, err := e.stores.GlobalConfig.Get(ctx, "default_surface"); err == nil && raw != nil {
if id, ok := raw["id"].(string); ok && id != "" {
if path := e.surfacePath(ctx, id); path != "" {
@@ -117,21 +135,24 @@ func (e *Engine) resolveDefaultSurface(ctx context.Context) string {
}
}
// 2. First enabled extension surface
surfaces, err := e.stores.Packages.ListEnabledByType(ctx, "surface")
// 3. First enabled extension surface (type "surface" or "full")
allPkgs, err := e.stores.Packages.List(ctx)
if err == nil {
for _, s := range surfaces {
if s.Source == "core" {
for _, s := range allPkgs {
if s.Source == "core" || s.Source == "builtin" {
continue
}
if s.Enabled {
if !s.Enabled {
continue
}
if s.Type == "surface" || s.Type == "full" {
return "/s/" + s.ID
}
}
}
// 3. Fallback
return "/admin"
// 4. Fallback — welcome surface (no extensions installed)
return "/welcome"
}
// surfacePath returns the URL path for a surface ID, or "" if the surface
@@ -156,9 +177,8 @@ func (e *Engine) surfacePath(ctx context.Context, id string) string {
return "/s/" + id
}
// disabledRedirect returns a handler that redirects to /admin.
// Uses /admin (not /) to avoid a redirect loop when the default surface
// is the one being disabled.
// disabledRedirect returns a handler that redirects to /.
// The DefaultSurfaceRedirect handler will resolve to the appropriate surface.
func (e *Engine) disabledRedirect() gin.HandlerFunc {
return func(c *gin.Context) {
c.Redirect(http.StatusTemporaryRedirect, e.cfg.BasePath+"/admin")