Files
any-hub/internal/server/routes/modules.go
2025-11-15 21:15:12 +08:00

115 lines
3.3 KiB
Go

package routes
import (
"sort"
"strings"
"time"
"github.com/gofiber/fiber/v3"
"github.com/any-hub/any-hub/internal/hubmodule"
"github.com/any-hub/any-hub/internal/server"
)
// RegisterModuleRoutes 暴露 /-/modules 诊断接口,供 SRE 查询模块与 Hub 绑定关系。
func RegisterModuleRoutes(app *fiber.App, registry *server.HubRegistry) {
if app == nil || registry == nil {
return
}
app.Get("/-/modules", func(c fiber.Ctx) error {
payload := fiber.Map{
"modules": encodeModules(hubmodule.List()),
"hubs": encodeHubBindings(registry.List()),
}
return c.JSON(payload)
})
app.Get("/-/modules/:key", func(c fiber.Ctx) error {
key := strings.ToLower(strings.TrimSpace(c.Params("key")))
if key == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "module_key_required"})
}
meta, ok := hubmodule.Resolve(key)
if !ok {
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "module_not_found"})
}
return c.JSON(encodeModule(meta))
})
}
type modulePayload struct {
Key string `json:"key"`
Description string `json:"description"`
MigrationState hubmodule.MigrationState `json:"migration_state"`
SupportedProtocols []string `json:"supported_protocols"`
CacheStrategy cacheStrategyPayload `json:"cache_strategy"`
}
type cacheStrategyPayload struct {
TTLSeconds int64 `json:"ttl_seconds"`
ValidationMode string `json:"validation_mode"`
DiskLayout string `json:"disk_layout"`
RequiresMetadataFile bool `json:"requires_metadata_file"`
SupportsStreamingWrite bool `json:"supports_streaming_write"`
}
type hubBindingPayload struct {
HubName string `json:"hub_name"`
ModuleKey string `json:"module_key"`
Domain string `json:"domain"`
Port int `json:"port"`
Rollout string `json:"rollout_flag"`
}
func encodeModules(mods []hubmodule.ModuleMetadata) []modulePayload {
if len(mods) == 0 {
return nil
}
sort.Slice(mods, func(i, j int) bool {
return mods[i].Key < mods[j].Key
})
result := make([]modulePayload, 0, len(mods))
for _, meta := range mods {
result = append(result, encodeModule(meta))
}
return result
}
func encodeModule(meta hubmodule.ModuleMetadata) modulePayload {
strategy := meta.CacheStrategy
return modulePayload{
Key: meta.Key,
Description: meta.Description,
MigrationState: meta.MigrationState,
SupportedProtocols: append([]string(nil), meta.SupportedProtocols...),
CacheStrategy: cacheStrategyPayload{
TTLSeconds: int64(strategy.TTLHint / time.Second),
ValidationMode: string(strategy.ValidationMode),
DiskLayout: strategy.DiskLayout,
RequiresMetadataFile: strategy.RequiresMetadataFile,
SupportsStreamingWrite: strategy.SupportsStreamingWrite,
},
}
}
func encodeHubBindings(routes []server.HubRoute) []hubBindingPayload {
if len(routes) == 0 {
return nil
}
sort.Slice(routes, func(i, j int) bool {
return routes[i].Config.Name < routes[j].Config.Name
})
result := make([]hubBindingPayload, 0, len(routes))
for _, route := range routes {
result = append(result, hubBindingPayload{
HubName: route.Config.Name,
ModuleKey: route.ModuleKey,
Domain: route.Config.Domain,
Port: route.ListenPort,
Rollout: string(route.RolloutFlag),
})
}
return result
}