MCPcopy
hub / github.com/PatchMon/PatchMon / NewRouter

Function NewRouter

server-source-code/internal/server/router.go:40–705  ·  view source on GitHub ↗

NewRouter creates the HTTP router with all routes and middleware. Returns (handler, guacdProcess) - guacdProcess should be stopped on shutdown. frontendFS is the embedded frontend static files (static/frontend/dist); pass nil to disable SPA serving.

(ctx context.Context, cfg *config.Config, db *database.DB, rdb *redisclient.Client, registry *agentregistry.Registry, queueClient *asynq.Client, queueInspector *asynq.Inspector, ctxRegistry *hostctx.Registry, poolCache *hostctx.PoolCache, redisCache *hostctx.RedisCache, notifyEmit *notifications.Emitter, log *slog.Logger, frontendFS fs.FS)

Source from the content-addressed store, hash-verified

38// Returns (handler, guacdProcess) - guacdProcess should be stopped on shutdown.
39// frontendFS is the embedded frontend static files (static/frontend/dist); pass nil to disable SPA serving.
40func NewRouter(ctx context.Context, cfg *config.Config, db *database.DB, rdb *redisclient.Client, registry *agentregistry.Registry, queueClient *asynq.Client, queueInspector *asynq.Inspector, ctxRegistry *hostctx.Registry, poolCache *hostctx.PoolCache, redisCache *hostctx.RedisCache, notifyEmit *notifications.Emitter, log *slog.Logger, frontendFS fs.FS) (http.Handler, *guacd.Process) {
41 r := chi.NewRouter()
42
43 var dbProvider database.DBProvider
44 if poolCache != nil {
45 dbProvider = &hostctx.DBResolver{Default: db}
46 } else {
47 dbProvider = db
48 }
49
50 // Build a RedisResolver so stores always call .RDB(ctx) for per-context isolation.
51 redisResolver := &hostctx.RedisResolver{Default: rdb}
52 settingsStore := store.NewSettingsStore(dbProvider)
53 settings, _ := settingsStore.GetFirst(ctx)
54 resolved := config.ResolveConfig(ctx, cfg, settings)
55
56 r.Use(middleware.RequestID())
57 r.Use(middleware.Recovery(log))
58 if poolCache != nil {
59 r.Use(hostctx.Middleware(ctxRegistry, poolCache, redisCache, db, rdb, cfg.RegistryReloadSecret))
60 } else {
61 r.Use(func(next http.Handler) http.Handler {
62 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
63 ctx := hostctx.WithDB(r.Context(), db)
64 ctx = hostctx.WithRedis(ctx, rdb)
65 next.ServeHTTP(w, r.WithContext(ctx))
66 })
67 })
68 }
69 r.Use(middleware.CORS(resolved.CORSOrigin, corsOriginResolver(ctxRegistry)))
70 if resolved.TrustProxy {
71 r.Use(chimw.RealIP)
72 }
73 // Note: chimw.Timeout is NOT applied globally because it conflicts with
74 // WebSocket/SSE routes (hijacked connections). It writes a 503 to a
75 // hijacked ResponseWriter causing "WriteHeader on hijacked connection".
76 // Instead, timeout is applied per-group below, skipping WS routes.
77
78 if poolCache != nil && cfg.RegistryReloadSecret != "" {
79 r.Post("/internal/reload-tenant", hostctx.ReloadHandler(poolCache, redisCache, cfg.RegistryReloadSecret))
80 }
81
82 usersStore := store.NewUsersStore(dbProvider)
83 permissionsStore := store.NewPermissionsStore(dbProvider)
84
85 if cfg.EnablePprof {
86 // Pprof endpoints require admin authentication to prevent information leakage.
87 r.Group(func(r chi.Router) {
88 r.Use(middleware.RequirePermission("can_manage_settings", permissionsStore))
89 r.Handle("/debug/pprof/*", http.HandlerFunc(pprof.Index))
90 r.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
91 r.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
92 r.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
93 r.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
94 })
95 }
96 dashboardPrefsStore := store.NewDashboardPreferencesStore(dbProvider)
97

Callers

nothing calls this directly

Calls 15

GetFirstMethod · 0.95
DecryptMethod · 0.95
SetStreamDependenciesMethod · 0.95
GetMyBillingMethod · 0.95
GetMyBillingPortalMethod · 0.95
PostMyBillingSyncMethod · 0.95
corsOriginResolverFunction · 0.85
healthHandlerFunction · 0.85
SPAHandlerFunction · 0.85

Tested by

no test coverage detected