diff --git a/internal/config/defaults.go b/internal/config/defaults.go index 601790e..414fbda 100644 --- a/internal/config/defaults.go +++ b/internal/config/defaults.go @@ -54,6 +54,17 @@ func DefaultConfig() *Config { Token: "", }, }, + Lua: &LuaMiddlewareConfig{ + Enabled: false, + Scripts: []LuaScriptConfig{}, + GlobalSettings: LuaGlobalSettings{ + MaxConcurrentCoroutines: 1000, + CoroutineTimeout: 30 * time.Second, + CodeCacheSize: 1000, + EnableFileWatch: true, + MaxExecutionTime: 30 * time.Second, + }, + }, Static: []StaticConfig{{ Path: "/", Root: "/var/www/html", @@ -164,8 +175,9 @@ func DefaultConfig() *Config { }, Monitoring: MonitoringConfig{ Status: StatusConfig{ - Path: "/_status", - Allow: []string{"127.0.0.1"}, + Path: "/_status", + Format: "text", + Allow: []string{"127.0.0.1"}, }, Pprof: PprofConfig{ Enabled: false, @@ -235,6 +247,37 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { fmt.Fprintf(&buf, " client_max_body_size: \"1MB\" # 请求体大小限制(支持单位: b, kb, mb, gb)\n") buf.WriteString("\n") + // cache_api 配置 + buf.WriteString(" # 缓存清理 API 配置(用于主动清理代理缓存)\n") + buf.WriteString(" # cache_api:\n") + fmt.Fprintf(&buf, " # enabled: %v # 是否启用缓存清理 API\n", cfg.Server.CacheAPI.Enabled) + fmt.Fprintf(&buf, " # path: \"%s\" # API 端点路径\n", cfg.Server.CacheAPI.Path) + buf.WriteString(" # allow: # 允许访问的 IP\n") + for _, ip := range cfg.Server.CacheAPI.Allow { + fmt.Fprintf(&buf, " # - \"%s\"\n", ip) + } + buf.WriteString(" # auth: # 认证配置\n") + fmt.Fprintf(&buf, " # type: \"%s\" # 认证类型(有效值: none, token)\n", cfg.Server.CacheAPI.Auth.Type) + buf.WriteString(" # token: \"\" # 认证令牌(支持环境变量 ${CACHE_API_TOKEN})\n") + buf.WriteString("\n") + + // lua 配置 + buf.WriteString(" # Lua 中间件配置(在请求处理流程中嵌入 Lua 脚本)\n") + buf.WriteString(" # lua:\n") + fmt.Fprintf(&buf, " # enabled: %v # 是否启用 Lua 中间件\n", cfg.Server.Lua.Enabled) + buf.WriteString(" # scripts: # Lua 脚本列表\n") + buf.WriteString(" # - path: \"/scripts/auth.lua\" # 脚本路径\n") + buf.WriteString(" # phase: \"access\" # 执行阶段(有效值: rewrite, access, content, log, header_filter, body_filter)\n") + buf.WriteString(" # timeout: 10s # 执行超时\n") + buf.WriteString(" # enabled: true # 是否启用此脚本\n") + buf.WriteString(" # global_settings: # 全局设置\n") + buf.WriteString(" # max_concurrent_coroutines: 1000 # 最大并发协程数\n") + buf.WriteString(" # coroutine_timeout: 30s # 协程执行超时\n") + buf.WriteString(" # code_cache_size: 1000 # 字节码缓存条目数\n") + buf.WriteString(" # enable_file_watch: true # 启用文件变更检测\n") + buf.WriteString(" # max_execution_time: 30s # 最大执行时间\n") + buf.WriteString("\n") + // static 配置 buf.WriteString(" # 静态文件服务配置(支持多个目录)\n") buf.WriteString(" static:\n") @@ -328,6 +371,7 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { fmt.Fprintf(&buf, " # idle_timeout: %ds # 空闲超时\n", int(cfg.Server.SSL.HTTP2.IdleTimeout.Seconds())) fmt.Fprintf(&buf, " # push_enabled: %v # 是否启用 Server Push\n", cfg.Server.SSL.HTTP2.PushEnabled) fmt.Fprintf(&buf, " # h2c_enabled: %v # 是否启用 H2C(明文 HTTP/2)\n", cfg.Server.SSL.HTTP2.H2CEnabled) + fmt.Fprintf(&buf, " # graceful_shutdown_timeout: %ds # HTTP/2 优雅关闭超时\n", int(cfg.Server.SSL.HTTP2.GracefulShutdownTimeout.Seconds())) buf.WriteString("\n") // security 配置 @@ -548,6 +592,7 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) { buf.WriteString("monitoring:\n") buf.WriteString(" status:\n") fmt.Fprintf(&buf, " path: \"%s\" # 状态端点路径\n", cfg.Monitoring.Status.Path) + fmt.Fprintf(&buf, " format: \"%s\" # 输出格式(有效值: text, json, html)\n", cfg.Monitoring.Status.Format) buf.WriteString(" allow: # 允许访问的 IP\n") for _, ip := range cfg.Monitoring.Status.Allow { fmt.Fprintf(&buf, " - \"%s\"\n", ip) diff --git a/internal/config/validate.go b/internal/config/validate.go index 121f700..9d458b2 100644 --- a/internal/config/validate.go +++ b/internal/config/validate.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "net" + "slices" "strings" "rua.plus/lolly/internal/loadbalance" @@ -29,10 +30,8 @@ import ( // ValidateEnum 验证值是否在有效枚举列表中 func ValidateEnum(value string, validValues []string, fieldName string) error { - for _, v := range validValues { - if value == v { - return nil - } + if slices.Contains(validValues, value) { + return nil } return fmt.Errorf("无效的 %s: %s(仅支持 %v)", fieldName, value, validValues) } @@ -760,14 +759,7 @@ func validateRewrite(r *RewriteRule) error { // 验证标志 validFlags := []string{"", "last", "redirect", "permanent", "break"} - valid := false - for _, f := range validFlags { - if r.Flag == f { - valid = true - break - } - } - if !valid { + if !slices.Contains(validFlags, r.Flag) { return fmt.Errorf("无效的 flag: %s(仅支持 last, redirect, permanent, break)", r.Flag) } @@ -819,14 +811,7 @@ func validateLogging(l *LoggingConfig) error { func validateSecurityHeaders(h *SecurityHeaders) error { // 验证 X-Frame-Options validFrameOptions := []string{"", "DENY", "SAMEORIGIN"} - valid := false - for _, opt := range validFrameOptions { - if h.XFrameOptions == opt { - valid = true - break - } - } - if !valid { + if !slices.Contains(validFrameOptions, h.XFrameOptions) { return fmt.Errorf("无效的 x_frame_options: %s(仅支持 DENY, SAMEORIGIN 或空)", h.XFrameOptions) } @@ -836,14 +821,7 @@ func validateSecurityHeaders(h *SecurityHeaders) error { "origin-when-cross-origin", "same-origin", "strict-origin", "strict-origin-when-cross-origin", "unsafe-url", } - valid = false - for _, policy := range validReferrerPolicies { - if h.ReferrerPolicy == policy { - valid = true - break - } - } - if !valid { + if !slices.Contains(validReferrerPolicies, h.ReferrerPolicy) { return fmt.Errorf("无效的 referrer_policy: %s", h.ReferrerPolicy) } @@ -1023,14 +1001,7 @@ func validateLua(l *LuaMiddlewareConfig) error { // 验证阶段值 validPhases := []string{"rewrite", "access", "content", "log", "header_filter", "body_filter"} - valid := false - for _, phase := range validPhases { - if script.Phase == phase { - valid = true - break - } - } - if !valid { + if !slices.Contains(validPhases, script.Phase) { return fmt.Errorf("scripts[%d].phase 无效: %s(仅支持 rewrite, access, content, log, header_filter, body_filter)", i, script.Phase) }