feat: 004/phase 1

This commit is contained in:
2025-11-14 23:54:50 +08:00
parent 9692219e0f
commit 0d52bae1e8
34 changed files with 1222 additions and 21 deletions

View File

@@ -5,10 +5,13 @@ import (
"path/filepath"
"reflect"
"strconv"
"strings"
"time"
"github.com/mitchellh/mapstructure"
"github.com/spf13/viper"
"github.com/any-hub/any-hub/internal/hubmodule"
)
// Load 读取并解析 TOML 配置文件,同时注入默认值与校验逻辑。
@@ -86,6 +89,14 @@ func applyHubDefaults(h *HubConfig) {
if h.CacheTTL.DurationValue() < 0 {
h.CacheTTL = Duration(0)
}
if trimmed := strings.TrimSpace(h.Module); trimmed == "" {
h.Module = hubmodule.DefaultModuleKey()
} else {
h.Module = strings.ToLower(trimmed)
}
if h.ValidationMode == "" {
h.ValidationMode = string(hubmodule.ValidationModeETag)
}
}
func durationDecodeHook() mapstructure.DecodeHookFunc {

View File

@@ -0,0 +1,3 @@
package config
import _ "github.com/any-hub/any-hub/internal/hubmodule/legacy"

View File

@@ -0,0 +1,25 @@
package config
import (
"github.com/any-hub/any-hub/internal/hubmodule"
)
// HubRuntime 将 Hub 配置与模块元数据合并,方便运行时快速取用策略。
type HubRuntime struct {
Config HubConfig
Module hubmodule.ModuleMetadata
CacheStrategy hubmodule.CacheStrategyProfile
}
// BuildHubRuntime 根据 Hub 配置和模块元数据创建运行时描述。
func BuildHubRuntime(cfg HubConfig, meta hubmodule.ModuleMetadata) HubRuntime {
strategy := hubmodule.ResolveStrategy(meta, hubmodule.StrategyOptions{
TTLOverride: cfg.CacheTTL.DurationValue(),
ValidationOverride: hubmodule.ValidationMode(cfg.ValidationMode),
})
return HubRuntime{
Config: cfg,
Module: meta,
CacheStrategy: strategy,
}
}

View File

@@ -67,9 +67,11 @@ type HubConfig struct {
Upstream string `mapstructure:"Upstream"`
Proxy string `mapstructure:"Proxy"`
Type string `mapstructure:"Type"`
Module string `mapstructure:"Module"`
Username string `mapstructure:"Username"`
Password string `mapstructure:"Password"`
CacheTTL Duration `mapstructure:"CacheTTL"`
ValidationMode string `mapstructure:"ValidationMode"`
EnableHeadCheck bool `mapstructure:"EnableHeadCheck"`
}

View File

@@ -6,6 +6,8 @@ import (
"net/url"
"strings"
"time"
"github.com/any-hub/any-hub/internal/hubmodule"
)
var supportedHubTypes = map[string]struct{}{
@@ -74,6 +76,24 @@ func (c *Config) Validate() error {
}
hub.Type = normalizedType
moduleKey := strings.ToLower(strings.TrimSpace(hub.Module))
if moduleKey == "" {
moduleKey = hubmodule.DefaultModuleKey()
}
if _, ok := hubmodule.Resolve(moduleKey); !ok {
return newFieldError(hubField(hub.Name, "Module"), fmt.Sprintf("未注册模块: %s", moduleKey))
}
hub.Module = moduleKey
if hub.ValidationMode != "" {
mode := strings.ToLower(strings.TrimSpace(hub.ValidationMode))
switch mode {
case string(hubmodule.ValidationModeETag), string(hubmodule.ValidationModeLastModified), string(hubmodule.ValidationModeNever):
hub.ValidationMode = mode
default:
return newFieldError(hubField(hub.Name, "ValidationMode"), "仅支持 etag/last-modified/never")
}
}
if (hub.Username == "") != (hub.Password == "") {
return newFieldError(hubField(hub.Name, "Username/Password"), "必须同时提供或同时留空")
}