diff --git a/modules/plugins/plugin.go b/modules/plugins/plugin.go index aa6d0d3239..e2dceab830 100644 --- a/modules/plugins/plugin.go +++ b/modules/plugins/plugin.go @@ -61,12 +61,12 @@ type RepoRoutesPlugin interface { Plugin // RegisterRepoWebRoutes adds routes under /{owner}/{repo}/ - // The router is a *web.Router from the gitcaddy server - RegisterRepoWebRoutes(m any) + // The router is a PluginRouter that handles path prefixes correctly + RegisterRepoWebRoutes(r PluginRouter) // RegisterRepoAPIRoutes adds routes under /api/v1/repos/{owner}/{repo}/ - // The router is a *web.Router from the gitcaddy server - RegisterRepoAPIRoutes(m any) + // The router is a PluginRouter that handles path prefixes correctly + RegisterRepoAPIRoutes(r PluginRouter) } // LicensedPlugin is implemented by plugins that require a license diff --git a/modules/plugins/registry.go b/modules/plugins/registry.go index c60956d3e1..7c9cccd58a 100644 --- a/modules/plugins/registry.go +++ b/modules/plugins/registry.go @@ -151,27 +151,35 @@ func RegisterAPIRoutes(m any) { } // RegisterRepoWebRoutes registers per-repository web routes from all plugins -func RegisterRepoWebRoutes(m any) { +// The router parameter should implement WebRouter (e.g., *web.Router) +func RegisterRepoWebRoutes(router WebRouter) { registryLock.RLock() defer registryLock.RUnlock() + // Create adapter with empty prefix (routes are relative to current group) + adapter := NewWebRouterAdapter(router, "") + for name, p := range registry { if rp, ok := p.(RepoRoutesPlugin); ok { log.Debug("Registering repo web routes for plugin: %s", name) - rp.RegisterRepoWebRoutes(m) + rp.RegisterRepoWebRoutes(adapter) } } } // RegisterRepoAPIRoutes registers per-repository API routes from all plugins -func RegisterRepoAPIRoutes(m any) { +// The router parameter should implement WebRouter (e.g., *web.Router) +func RegisterRepoAPIRoutes(router WebRouter) { registryLock.RLock() defer registryLock.RUnlock() + // Create adapter with empty prefix (routes are relative to current group) + adapter := NewWebRouterAdapter(router, "") + for name, p := range registry { if rp, ok := p.(RepoRoutesPlugin); ok { log.Debug("Registering repo API routes for plugin: %s", name) - rp.RegisterRepoAPIRoutes(m) + rp.RegisterRepoAPIRoutes(adapter) } } } diff --git a/modules/plugins/router.go b/modules/plugins/router.go new file mode 100644 index 0000000000..49a01a1147 --- /dev/null +++ b/modules/plugins/router.go @@ -0,0 +1,81 @@ +// Copyright 2026 MarketAlly. All rights reserved. +// SPDX-License-Identifier: MIT + +package plugins + +import ( + "net/http" +) + +// PluginRouter is the interface plugins use to register routes. +// It abstracts away the underlying router implementation and ensures +// routes are registered with the correct path prefixes. +type PluginRouter interface { + // Get registers a GET route + Get(pattern string, handler http.HandlerFunc) + + // Post registers a POST route + Post(pattern string, handler http.HandlerFunc) + + // Put registers a PUT route + Put(pattern string, handler http.HandlerFunc) + + // Delete registers a DELETE route + Delete(pattern string, handler http.HandlerFunc) + + // Group creates a sub-router with a path prefix + Group(pattern string, fn func(PluginRouter)) +} + +// WebRouterAdapter wraps a web.Router to implement PluginRouter. +// This adapter ensures routes are registered with the correct path prefixes. +type WebRouterAdapter struct { + router WebRouter + pathPrefix string +} + +// WebRouter is the interface that web.Router implements +type WebRouter interface { + Get(pattern string, h ...any) + Post(pattern string, h ...any) + Put(pattern string, h ...any) + Delete(pattern string, h ...any) + Group(pattern string, fn func(), middlewares ...any) +} + +// NewWebRouterAdapter creates a new adapter for the given web.Router +func NewWebRouterAdapter(router WebRouter, pathPrefix string) *WebRouterAdapter { + return &WebRouterAdapter{ + router: router, + pathPrefix: pathPrefix, + } +} + +// Get implements PluginRouter.Get +func (a *WebRouterAdapter) Get(pattern string, handler http.HandlerFunc) { + a.router.Get(a.pathPrefix+pattern, handler) +} + +// Post implements PluginRouter.Post +func (a *WebRouterAdapter) Post(pattern string, handler http.HandlerFunc) { + a.router.Post(a.pathPrefix+pattern, handler) +} + +// Put implements PluginRouter.Put +func (a *WebRouterAdapter) Put(pattern string, handler http.HandlerFunc) { + a.router.Put(a.pathPrefix+pattern, handler) +} + +// Delete implements PluginRouter.Delete +func (a *WebRouterAdapter) Delete(pattern string, handler http.HandlerFunc) { + a.router.Delete(a.pathPrefix+pattern, handler) +} + +// Group implements PluginRouter.Group +func (a *WebRouterAdapter) Group(pattern string, fn func(PluginRouter)) { + subAdapter := &WebRouterAdapter{ + router: a.router, + pathPrefix: a.pathPrefix + pattern, + } + fn(subAdapter) +}