diff --git a/docs/changelog/index.md b/docs/changelog/index.md index bb1ef1b2..94098a6a 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -23,6 +23,7 @@ sidebar: false ### 修复 +- 修复配置版本发布到 Agent 后 `openresty -t` 因 `proxy_cache_path` 使用 `/var/cache/openresty` 导致非 root 用户 `mkdir` 失败的问题:发布快照与渲染将 `/var/` 下路径规范为 `__OPENFLARE_PROXY_CACHE_PATH__`,Agent 应用时落地为 `data_dir/var/cache/openflare_proxy` 并兼容重写已发布配置中的旧路径。 - 修复配置版本发布到 Agent 后 `openresty -t` 因证书私钥无法解析而失败的问题。根因是发布快照生成 `certs/{id}.key` 时直接写入库内加密的 `KeyPEM`(`enc:v1:`),未解密为 PEM;现与证书详情接口一致,发布前通过 `OpenKeyPEM` 解密后再下发。 - 修复 `/api/v1/d/option` 批量更新 OpenResty 等业务配置不生效的问题。根本原因是 option 模块在读写时做了 PascalCase 与 snake_case 的机械转换(如 `OpenRestyEventsUse` → `open_resty_events_use`),与 `w_system_configs` 中实际 key(`openresty_events_use`)不一致,更新写入了错误的幽灵配置行。现改为 API 直接使用与数据库一致的 snake_case key,并同步更新前端性能调优与运维设置页。 - 修复 PostgreSQL 数据库执行迁移时报 `duplicate key value violates unique constraint "goose_db_version_pkey"` 导致迁移中断的问题。根本原因:`goose_db_version.id` 自增序列落后于表内 `MAX(id)`(常见于从 dump 恢复或历史迁移以显式 id 复制版本记录后),goose 记录新版本号时自增 id 与既有行冲突。修复方式:在 `goose.Up` 前对 PostgreSQL 执行 `setval` 重新对齐 `goose_db_version` 的 id 序列。 diff --git a/internal/apps/agent/nginx/manager.go b/internal/apps/agent/nginx/manager.go index 10169ed7..52ae040f 100644 --- a/internal/apps/agent/nginx/manager.go +++ b/internal/apps/agent/nginx/manager.go @@ -302,6 +302,9 @@ func (m *Manager) ensureOpenRestyWorkerReadAccess() error { if pidPath := m.pidRuntimePath(); pidPath != "" { targets = append(targets, filepath.Dir(pidPath)) } + if proxyCacheDir := m.proxyCacheRuntimeDir(); proxyCacheDir != "" { + targets = append(targets, proxyCacheDir) + } seen := make(map[string]struct{}, len(targets)) for _, target := range targets { cleaned := filepath.Clean(strings.TrimSpace(target)) @@ -1263,6 +1266,14 @@ func (m *Manager) renderMainConfig(content string) string { } } } + if proxyCacheDir := m.proxyCacheRuntimeDir(); proxyCacheDir != "" { + slashProxyCache := filepath.ToSlash(proxyCacheDir) + rendered = strings.ReplaceAll(rendered, openrestyrender.ProxyCachePathPlaceholder, slashProxyCache) + rendered = strings.ReplaceAll(rendered, "/var/cache/openresty", slashProxyCache) + if err := os.MkdirAll(proxyCacheDir, nginxDirPerm); err != nil { + slog.Warn("ensure proxy cache directory failed", "path", proxyCacheDir, "error", err) + } + } if luaDir := m.luaRuntimePath(); luaDir != "" { rendered = strings.ReplaceAll(rendered, openrestyrender.LuaDirPlaceholder, luaDir) } @@ -1398,6 +1409,13 @@ func (m *Manager) nginxCacheRuntimeDir() string { return "" } +func (m *Manager) proxyCacheRuntimeDir() string { + if varRoot := m.varRuntimeDir(); varRoot != "" { + return filepath.Join(varRoot, "cache", "openflare_proxy") + } + return "" +} + func (m *Manager) luaRuntimePath() string { if strings.TrimSpace(m.NginxLuaDir) == "" { return "" diff --git a/internal/apps/openflare/config_version/proxy_cache_path_test.go b/internal/apps/openflare/config_version/proxy_cache_path_test.go new file mode 100644 index 00000000..4d3cbdc4 --- /dev/null +++ b/internal/apps/openflare/config_version/proxy_cache_path_test.go @@ -0,0 +1,18 @@ +// Copyright 2026 Arctel.net +// SPDX-License-Identifier: Apache-2.0 + +package config_version + +import ( + "testing" + + openrestyrender "github.com/Rain-kl/Wavelet/pkg/render/openresty" + "github.com/stretchr/testify/assert" +) + +func TestNormalizeProxyCachePathForSnapshot(t *testing.T) { + assert.Equal(t, "/var/cache/openresty", normalizeProxyCachePathForSnapshot(false, "/var/cache/openresty")) + assert.Equal(t, openrestyrender.ProxyCachePathPlaceholder, normalizeProxyCachePathForSnapshot(true, "/var/cache/openresty")) + assert.Equal(t, openrestyrender.ProxyCachePathPlaceholder, normalizeProxyCachePathForSnapshot(true, "")) + assert.Equal(t, "/data/var/cache/custom", normalizeProxyCachePathForSnapshot(true, "/data/var/cache/custom")) +} diff --git a/internal/apps/openflare/config_version/snapshot.go b/internal/apps/openflare/config_version/snapshot.go index 5db49239..9486f978 100644 --- a/internal/apps/openflare/config_version/snapshot.go +++ b/internal/apps/openflare/config_version/snapshot.go @@ -495,7 +495,7 @@ func buildOpenRestyConfigSnapshot(ctx context.Context) openRestyConfigSnapshot { return config.Value } - return openRestyConfigSnapshot{ + snapshot := openRestyConfigSnapshot{ DefaultServerReturnStatus: getIntConfig(model.ConfigKeyOpenRestyDefaultServerReturnStatus, defaultOpenRestyReturnStatus), WorkerProcesses: getStringConfig(model.ConfigKeyOpenRestyWorkerProcesses, "auto"), WorkerConnections: getIntConfig(model.ConfigKeyOpenRestyWorkerConnections, defaultOpenRestyWorkerConns), @@ -534,6 +534,19 @@ func buildOpenRestyConfigSnapshot(ctx context.Context) openRestyConfigSnapshot { CacheUseStale: getStringConfig(model.ConfigKeyOpenRestyCacheUseStale, "error timeout updating http_500 http_502 http_503 http_504"), MainConfigTemplate: getStringConfig(model.ConfigKeyOpenRestyMainConfigTemplate, model.DefaultOpenRestyMainConfigTemplate), } + snapshot.CachePath = normalizeProxyCachePathForSnapshot(snapshot.CacheEnabled, snapshot.CachePath) + return snapshot +} + +func normalizeProxyCachePathForSnapshot(cacheEnabled bool, cachePath string) string { + if !cacheEnabled { + return strings.TrimSpace(cachePath) + } + trimmed := strings.TrimSpace(cachePath) + if trimmed == "" || strings.HasPrefix(trimmed, "/var/") { + return openrestyrender.ProxyCachePathPlaceholder + } + return trimmed } func buildCertificateSupportFiles(ctx context.Context, routes []snapshotRoute) ([]SupportFile, error) { diff --git a/pkg/render/openresty/render.go b/pkg/render/openresty/render.go index 6f8c99b0..5042645c 100644 --- a/pkg/render/openresty/render.go +++ b/pkg/render/openresty/render.go @@ -292,8 +292,12 @@ func renderOpenRestyCacheTemplateBlock(cfg ConfigSnapshot) string { lines = append(lines, renderOpenRestyObservabilityTemplateBlock()) return strings.Join(lines, "") } + cachePath := strings.TrimSpace(cfg.CachePath) + if cachePath == "" || strings.HasPrefix(cachePath, "/var/") { + cachePath = ProxyCachePathPlaceholder + } lines = append(lines, strings.Join([]string{ - fmt.Sprintf(" proxy_cache_path %s levels=%s keys_zone=openflare_cache:10m inactive=%s max_size=%s;", cfg.CachePath, cfg.CacheLevels, cfg.CacheInactive, cfg.CacheMaxSize), + fmt.Sprintf(" proxy_cache_path %s levels=%s keys_zone=openflare_cache:10m inactive=%s max_size=%s;", cachePath, cfg.CacheLevels, cfg.CacheInactive, cfg.CacheMaxSize), fmt.Sprintf(" proxy_cache_key \"%s\";", cfg.CacheKeyTemplate), fmt.Sprintf(" proxy_cache_lock %s;", onOff(cfg.CacheLockEnabled)), fmt.Sprintf(" proxy_cache_lock_timeout %s;", cfg.CacheLockTimeout), diff --git a/pkg/render/openresty/types.go b/pkg/render/openresty/types.go index 92f5166d..8ad77538 100644 --- a/pkg/render/openresty/types.go +++ b/pkg/render/openresty/types.go @@ -9,6 +9,7 @@ const ( ErrorLogPlaceholder = "__OPENFLARE_ERROR_LOG__" PIDPathPlaceholder = "__OPENFLARE_PID_PATH__" NginxCacheDirPlaceholder = "__OPENFLARE_NGINX_CACHE_DIR__" + ProxyCachePathPlaceholder = "__OPENFLARE_PROXY_CACHE_PATH__" LuaDirPlaceholder = "__OPENFLARE_LUA_DIR__" ObservabilityListenPlaceholder = "__OPENFLARE_OBSERVABILITY_LISTEN__" ObservabilityPortPlaceholder = "__OPENFLARE_OBSERVABILITY_PORT__"