From 37170ae81a17ef9a802cc049ac51e709ebf924a3 Mon Sep 17 00:00:00 2001 From: Kadxy Date: Tue, 3 Nov 2026 18:58:24 +0800 Subject: [PATCH] fix(openresty): report brotli as pending when the http.d include can be added (#14285) Installs that enabled ngx_brotli before v2.3.0 have no http.d include in nginx.conf, because the include is only inserted when a module is toggled or built. The settings page reported this as "cannot write the managed configuration" and skipped the brotli save, so the save path that inserts the include was never reached, even on a stock nginx.conf. The settings response now tells the two cases apart with an in-memory insertion check that writes nothing: - include missing but insertable, or managed file missing: managedPending, the page explains the values take effect after saving and saves normally - no safe insertion point: managedUnavailable, unchanged The save path now inserts the include through ensureNginxHTTPIncludeActive, the same function module activation uses, so both produce identical results. A sentinel error keeps "no insertion point" mapped to ErrBrotliUnsupported while I/O failures are still returned as-is. --- agent/app/dto/response/nginx.go | 6 +- agent/app/service/nginx_http_config.go | 18 +++- agent/app/service/nginx_http_config_test.go | 9 +- agent/app/service/nginx_module_runtime.go | 60 ++++++++------ .../app/service/nginx_module_runtime_test.go | 82 +++++++++++++++++++ frontend/src/api/interface/nginx.ts | 1 + frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/es-es.ts | 1 + frontend/src/lang/modules/fa.ts | 1 + frontend/src/lang/modules/ja.ts | 1 + frontend/src/lang/modules/ko.ts | 1 + frontend/src/lang/modules/lo.ts | 1 + frontend/src/lang/modules/ms.ts | 1 + frontend/src/lang/modules/pt-br.ts | 1 + frontend/src/lang/modules/ru.ts | 1 + frontend/src/lang/modules/tr.ts | 1 + frontend/src/lang/modules/zh-Hant.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + .../website/nginx/performance/index.vue | 8 ++ 19 files changed, 169 insertions(+), 27 deletions(-) diff --git a/agent/app/dto/response/nginx.go b/agent/app/dto/response/nginx.go index 73468c339..649259eb3 100644 --- a/agent/app/dto/response/nginx.go +++ b/agent/app/dto/response/nginx.go @@ -20,11 +20,15 @@ type NginxParam struct { // NginxBrotliRes carries the brotli settings together with where they live. // ManagedExternally is true when the user defined brotli by hand, in which // case the panel only reports the values and must not write its own copy. -// ManagedUnavailable is true when the panel could not wire the managed +// ManagedPending is true when nginx does not load the managed configuration +// yet but saving will wire it in, so the reported values are not in effect +// until then. +// ManagedUnavailable is true when the panel cannot wire the managed // configuration into nginx.conf at all, so the reported values are inert. type NginxBrotliRes struct { Params []NginxParam `json:"params"` ManagedExternally bool `json:"managedExternally"` + ManagedPending bool `json:"managedPending"` ManagedUnavailable bool `json:"managedUnavailable"` } diff --git a/agent/app/service/nginx_http_config.go b/agent/app/service/nginx_http_config.go index f6474f9f7..6413bbc1a 100644 --- a/agent/app/service/nginx_http_config.go +++ b/agent/app/service/nginx_http_config.go @@ -45,6 +45,10 @@ var ( // nginxHTTPBlockStartRe locates the http block opening, the fallback // insertion point, and captures its indentation. nginxHTTPBlockStartRe = regexp.MustCompile(`(?m)^([ \t]*)http[ \t]*\{[ \t]*\r?$`) + + // errNginxHTTPIncludeNoInsertionPoint tells a config the panel cannot edit + // safely apart from an I/O failure while editing it. + errNginxHTTPIncludeNoInsertionPoint = errors.New("no insertion point for the http.d include in nginx.conf") ) // nginxHTTPIncludePresent reports whether nginx.conf already loads http.d. @@ -96,7 +100,19 @@ func insertNginxHTTPInclude(content string) (string, error) { indent := content[m[2]:m[3]] + " " return content[:m[1]] + eol + indent + nginxHTTPIncludeDirective + content[m[1]:], nil } - return "", errors.New("no insertion point for the http.d include in nginx.conf") + return "", errNginxHTTPIncludeNoInsertionPoint +} + +// nginxHTTPIncludeInsertable reports whether nginx.conf already loads http.d +// or the include can be added automatically. Nothing is written: the +// insertion is computed in memory and discarded. +func nginxHTTPIncludeInsertable(install model.AppInstall) bool { + content, err := os.ReadFile(nginxMainConfigPath(install)) + if err != nil { + return false + } + _, err = insertNginxHTTPInclude(string(content)) + return err == nil } // ensureNginxHTTPIncludeActive makes nginx.conf load http.d, inserting the diff --git a/agent/app/service/nginx_http_config_test.go b/agent/app/service/nginx_http_config_test.go index 1071b4dfc..5726048cd 100644 --- a/agent/app/service/nginx_http_config_test.go +++ b/agent/app/service/nginx_http_config_test.go @@ -1,6 +1,7 @@ package service import ( + "errors" "strings" "testing" ) @@ -83,9 +84,15 @@ func TestInsertNginxHTTPIncludeFallsBackToHTTPBlock(t *testing.T) { } func TestInsertNginxHTTPIncludeRejectsConfigWithoutHTTPBlock(t *testing.T) { - if _, err := insertNginxHTTPInclude("events {}\n"); err == nil { + _, err := insertNginxHTTPInclude("events {}\n") + if err == nil { t.Fatal("a config without an http block must be rejected so callers can degrade") } + // The save path maps this error to an actionable message and must be able + // to tell it apart from an I/O failure. + if !errors.Is(err, errNginxHTTPIncludeNoInsertionPoint) { + t.Fatalf("unexpected error: %v", err) + } } func TestInsertNginxHTTPIncludeIgnoresCommentedIncludes(t *testing.T) { diff --git a/agent/app/service/nginx_module_runtime.go b/agent/app/service/nginx_module_runtime.go index ad4ff6957..c001ba4f9 100644 --- a/agent/app/service/nginx_module_runtime.go +++ b/agent/app/service/nginx_module_runtime.go @@ -224,12 +224,9 @@ func getNginxBrotliParams() (*response.NginxBrotliRes, error) { fileName := nginxHTTPConfigFileName(nginxModuleRuntimeOrder(nginxBrotliModuleName), nginxBrotliModuleName) current = readNginxHTTPDirectives(path.Join(nginxHTTPConfigDir(install), fileName)) } - res := &response.NginxBrotliRes{ - ManagedExternally: managedExternally, - // Without the include, values the panel would write would never reach - // nginx, so they are reported as unavailable rather than shown as if - // they were in effect. - ManagedUnavailable: !managedExternally && !nginxHTTPIncludePresent(install), + res := &response.NginxBrotliRes{ManagedExternally: managedExternally} + if !managedExternally { + res.ManagedPending, res.ManagedUnavailable = nginxBrotliManagedState(install) } for _, directive := range mergeNginxRuntimeDirectives(nginxModuleRuntimeDefaults[nginxBrotliModuleName], current) { res.Params = append(res.Params, response.NginxParam{Name: directive.Name, Params: directive.Params}) @@ -237,6 +234,28 @@ func getNginxBrotliParams() (*response.NginxBrotliRes, error) { return res, nil } +// nginxBrotliManagedState reports whether the managed brotli file is missing +// from what nginx loads, and if so whether saving from the settings page can +// fix that. Either way the reported values are not in effect, so they must +// not be presented as if they were. +// +// A missing include is the common case for installs that enabled brotli +// before the panel managed its directives, and the save path inserts it. Only +// a config without a safe insertion point is reported as unavailable. +func nginxBrotliManagedState(install model.AppInstall) (pending, unavailable bool) { + if !nginxHTTPIncludePresent(install) { + if nginxHTTPIncludeInsertable(install) { + return true, false + } + return false, true + } + fileName := nginxHTTPConfigFileName(nginxModuleRuntimeOrder(nginxBrotliModuleName), nginxBrotliModuleName) + if _, err := os.Stat(path.Join(nginxHTTPConfigDir(install), fileName)); os.IsNotExist(err) { + return true, false + } + return false, false +} + // readNginxUserBrotliDirectives collects the brotli directives the user wrote // in any of the files nginx loads them from. func readNginxUserBrotliDirectives(install model.AppInstall) map[string][]string { @@ -313,26 +332,19 @@ func updateNginxBrotliParams(params []dto.NginxParam) error { return updateUserNginxBrotliParams(install, values) } // A managed write needs the include. Installations missing it are - // upgraded in place here; when nginx.conf cannot be edited safely the - // write is refused with an actionable error instead of writing values - // nginx would never load. - if !nginxHTTPIncludePresent(install) { - configPath := nginxMainConfigPath(install) - content, readErr := os.ReadFile(configPath) - if readErr != nil { - return readErr - } - updated, insErr := insertNginxHTTPInclude(string(content)) - if insErr != nil { + // upgraded in place here, through the same path that enabling the + // module takes; when nginx.conf cannot be edited safely the write is + // refused with an actionable error instead of writing values nginx + // would never load. + _, confSnapshot, includeErr := ensureNginxHTTPIncludeActive(install) + if includeErr != nil { + if errors.Is(includeErr, errNginxHTTPIncludeNoInsertionPoint) { return buserr.New("ErrBrotliUnsupported") } - if err = writeNginxFileAtomic(configPath, []byte(updated)); err != nil { - return err - } - if err = os.MkdirAll(nginxHTTPConfigDir(install), constant.DirPerm); err != nil { - return err - } - if err = nginxCheckAndReload(string(content), configPath, install.ContainerName); err != nil { + return includeErr + } + if confSnapshot != nil { + if err = nginxCheckAndReload(string(confSnapshot), nginxMainConfigPath(install), install.ContainerName); err != nil { return err } } diff --git a/agent/app/service/nginx_module_runtime_test.go b/agent/app/service/nginx_module_runtime_test.go index 8be49c973..94614ee01 100644 --- a/agent/app/service/nginx_module_runtime_test.go +++ b/agent/app/service/nginx_module_runtime_test.go @@ -229,3 +229,85 @@ func TestValidateNginxBrotliValues(t *testing.T) { } } } + +// newBrotliStateInstall lays out an OpenResty install whose nginx.conf holds +// conf, or that has no nginx.conf at all when conf is empty. +func newBrotliStateInstall(t *testing.T, conf string) model.AppInstall { + t.Helper() + installRoot := t.TempDir() + confDir := path.Join(installRoot, "openresty", "openresty", "conf") + if err := os.MkdirAll(confDir, 0o755); err != nil { + t.Fatal(err) + } + if conf != "" { + if err := os.WriteFile(path.Join(confDir, "nginx.conf"), []byte(conf), 0o644); err != nil { + t.Fatal(err) + } + } + previous := global.Dir.AppInstallDir + global.Dir.AppInstallDir = installRoot + t.Cleanup(func() { global.Dir.AppInstallDir = previous }) + install := model.AppInstall{Name: "openresty"} + install.App.Key = constant.AppOpenresty + return install +} + +// Installs that enabled brotli before the panel managed its directives have +// no http.d include. Saving inserts it, so the settings page must report the +// values as pending rather than as something the panel can never apply. +func TestNginxBrotliManagedState(t *testing.T) { + withInclude, err := insertNginxHTTPInclude(plainNginxConf) + if err != nil { + t.Fatal(err) + } + managedFile := nginxHTTPConfigFileName(nginxModuleRuntimeOrder(nginxBrotliModuleName), nginxBrotliModuleName) + cases := []struct { + name string + conf string + managedFile bool + wantPending bool + wantUnavailable bool + }{ + {"include missing but insertable", plainNginxConf, false, true, false}, + {"no insertion point", "events {}\n", false, false, true}, + {"nginx.conf unreadable", "", false, false, true}, + {"include present, managed file missing", withInclude, false, true, false}, + {"include present and managed file written", withInclude, true, false, false}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + install := newBrotliStateInstall(t, tc.conf) + if tc.managedFile { + configDir := nginxHTTPConfigDir(install) + if err := os.MkdirAll(configDir, 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path.Join(configDir, managedFile), []byte("brotli on;\n"), 0o644); err != nil { + t.Fatal(err) + } + } + pending, unavailable := nginxBrotliManagedState(install) + if pending != tc.wantPending || unavailable != tc.wantUnavailable { + t.Fatalf("expected pending=%v unavailable=%v, got pending=%v unavailable=%v", + tc.wantPending, tc.wantUnavailable, pending, unavailable) + } + }) + } +} + +// The state is computed on every visit to the settings page; only an explicit +// save may change the configuration. +func TestNginxBrotliManagedStateDoesNotWrite(t *testing.T) { + install := newBrotliStateInstall(t, plainNginxConf) + nginxBrotliManagedState(install) + content, err := os.ReadFile(nginxMainConfigPath(install)) + if err != nil { + t.Fatal(err) + } + if string(content) != plainNginxConf { + t.Fatal("checking the state must not edit nginx.conf") + } + if _, err := os.Stat(nginxHTTPConfigDir(install)); !os.IsNotExist(err) { + t.Fatal("checking the state must not create the http.d directory") + } +} diff --git a/frontend/src/api/interface/nginx.ts b/frontend/src/api/interface/nginx.ts index c2d08b967..f02a6f35a 100644 --- a/frontend/src/api/interface/nginx.ts +++ b/frontend/src/api/interface/nginx.ts @@ -10,6 +10,7 @@ export namespace Nginx { export interface NginxBrotliRes { params: NginxParam[]; managedExternally: boolean; + managedPending: boolean; managedUnavailable: boolean; } diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index c88f78a2f..9ed13b34e 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -4591,6 +4591,7 @@ const message = { brotliCompLevelHelper: 'Brotli compression rate, 0 to 11', brotliManagedExternallyHelper: 'Brotli is configured manually in nginx.conf; the panel shows the values in effect and will not overwrite them.', + brotliManagedPendingHelper: 'Brotli settings are not in effect yet; they take effect after you save.', brotliManagedUnavailableHelper: 'The panel could not add the managed brotli configuration to nginx.conf automatically; the values below will not take effect.', brotliMinLengthHelper: 'Minimum response size to brotli-compress', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 736bca55e..790ead4a3 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -4650,6 +4650,7 @@ const message = { brotliCompLevelHelper: 'Tasa de compresión brotli, de 0 a 11', brotliManagedExternallyHelper: 'Brotli está configurado manualmente en nginx.conf; el panel muestra los valores en vigor y no los sobrescribe.', + brotliManagedPendingHelper: 'La configuración de brotli aún no está en vigor; se aplicará al guardar.', brotliManagedUnavailableHelper: 'El panel no pudo añadir automáticamente la configuración brotli gestionada a nginx.conf; los valores siguientes no tendrán efecto.', brotliMinLengthHelper: 'Tamaño mínimo de respuesta para comprimir con brotli', diff --git a/frontend/src/lang/modules/fa.ts b/frontend/src/lang/modules/fa.ts index 4e24d7856..031552165 100644 --- a/frontend/src/lang/modules/fa.ts +++ b/frontend/src/lang/modules/fa.ts @@ -4553,6 +4553,7 @@ const message = { brotliCompLevelHelper: 'نرخ فشرده‌سازی brotli، از 0 تا 11', brotliManagedExternallyHelper: 'brotli به‌صورت دستی در nginx.conf پیکربندی شده است؛ پنل فقط مقادیر در حال اجرا را نشان می‌دهد و آن‌ها را بازنویسی نمی‌کند.', + brotliManagedPendingHelper: 'تنظیمات brotli هنوز اعمال نشده است؛ پس از ذخیره اعمال می‌شود.', brotliManagedUnavailableHelper: 'پنل نتوانست پیکربندی مدیریت‌شده brotli را به‌صورت خودکار به nginx.conf اضافه کند؛ مقادیر زیر اعمال نخواهند شد.', brotliMinLengthHelper: 'حداقل اندازه پاسخ برای فشرده‌سازی با brotli', diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 3eb926db3..41c347494 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -4574,6 +4574,7 @@ const message = { brotliCompLevelHelper: 'brotli 圧縮率、0 から 11', brotliManagedExternallyHelper: 'brotli は nginx.conf で手動設定されています。パネルは現在の有効な値を表示するだけで、上書きしません。', + brotliManagedPendingHelper: 'brotli 設定はまだ有効になっていません。保存すると有効になります。', brotliManagedUnavailableHelper: 'パネルは管理対象の brotli 設定を nginx.conf に自動で追加できませんでした。以下の設定は有効になりません。', brotliMinLengthHelper: 'brotli で圧縮する最小レスポンスサイズ', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index c3ecd596d..cd2d2b190 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -4492,6 +4492,7 @@ const message = { brotliCompLevelHelper: 'brotli 압축률, 0에서 11까지', brotliManagedExternallyHelper: 'brotli가 nginx.conf에 수동으로 구성되어 있습니다. 패널은 적용 중인 값만 표시하며 덮어쓰지 않습니다.', + brotliManagedPendingHelper: 'brotli 설정이 아직 적용되지 않았습니다. 저장하면 적용됩니다.', brotliManagedUnavailableHelper: '패널이 관리되는 brotli 구성을 nginx.conf에 자동으로 추가할 수 없습니다. 아래 설정은 적용되지 않습니다.', brotliMinLengthHelper: 'brotli로 압축할 최소 응답 크기', diff --git a/frontend/src/lang/modules/lo.ts b/frontend/src/lang/modules/lo.ts index ba6d05677..40915108d 100644 --- a/frontend/src/lang/modules/lo.ts +++ b/frontend/src/lang/modules/lo.ts @@ -4465,6 +4465,7 @@ const message = { brotliCompLevelHelper: 'ອັດຕາການບີບອັດ brotli, 0 ຫາ 11', brotliManagedExternallyHelper: 'brotli ຖືກຕັ້ງຄ່າດ້ວຍມືໃນ nginx.conf; ແຜງຄວບຄຸມຈະສະແດງຄ່າທີ່ກຳລັງໃຊ້ຢູ່ ແລະ ຈະບໍ່ຂຽບທັບ.', + brotliManagedPendingHelper: 'ການຕັ້ງຄ່າ brotli ຍັງບໍ່ມີຜົນ; ຈະມີຜົນຫຼັງຈາກບັນທຶກ.', brotliManagedUnavailableHelper: 'ແຜງຄວບຄຸມບໍ່ສາມາດເພີ່ມການຕັ້ງຄ່າ brotli ທີ່ຈັດການໄປຍັງ nginx.conf ໂດຍອັດຕະໂນມັດໄດ້; ຄ່າຂ້າງລຸ່ມນີ້ຈະບໍ່ມີຜົນ.', brotliMinLengthHelper: 'ຂະໜາດຕອບສະໜອງຂັ້ນຕ່ຳທີ່ຈະບີບອັດດ້ວຍ brotli', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 17bb7cd39..1bd6de626 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -4659,6 +4659,7 @@ const message = { brotliCompLevelHelper: 'Kadar pemampatan brotli, 0 hingga 11', brotliManagedExternallyHelper: 'Brotli dikonfigurasikan secara manual dalam nginx.conf; panel memaparkan nilai yang berkuat kuasa dan tidak akan menimpanya.', + brotliManagedPendingHelper: 'Tetapan brotli belum berkuat kuasa; ia akan berkuat kuasa selepas disimpan.', brotliManagedUnavailableHelper: 'Panel tidak dapat menambahkan konfigurasi brotli terurus ke nginx.conf secara automatik; nilai di bawah tidak akan berkuat kuasa.', brotliMinLengthHelper: 'Saiz respons minimum untuk dimampatkan dengan brotli', diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index 7b421c348..38da9dd51 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -4678,6 +4678,7 @@ const message = { brotliCompLevelHelper: 'Taxa de compressão brotli, de 0 a 11', brotliManagedExternallyHelper: 'O brotli está configurado manualmente no nginx.conf; o painel mostra os valores em vigor e não os sobrescreve.', + brotliManagedPendingHelper: 'As configurações brotli ainda não estão em vigor; serão aplicadas ao salvar.', brotliManagedUnavailableHelper: 'O painel não conseguiu adicionar automaticamente a configuração brotli gerenciada ao nginx.conf; os valores abaixo não terão efeito.', brotliMinLengthHelper: 'Tamanho mínimo da resposta para compressão brotli', diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index af750ce03..a204355f9 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -4649,6 +4649,7 @@ const message = { brotliCompLevelHelper: 'Степень сжатия brotli, от 0 до 11', brotliManagedExternallyHelper: 'Brotli настроен вручную в nginx.conf; панель показывает действующие значения и не перезаписывает их.', + brotliManagedPendingHelper: 'Настройки brotli ещё не действуют; они вступят в силу после сохранения.', brotliManagedUnavailableHelper: 'Панель не смогла автоматически добавить управляемую конфигурацию brotli в nginx.conf; значения ниже не вступят в силу.', brotliMinLengthHelper: 'Минимальный размер ответа для сжатия brotli', diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 0980d1d2f..9c261cb86 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -4664,6 +4664,7 @@ const message = { brotliCompLevelHelper: 'Brotli sıkıştırma oranı, 0 ile 11 arası', brotliManagedExternallyHelper: 'Brotli, nginx.conf dosyasında elle yapılandırıldı; panel yalnızca geçerli değerleri gösterir ve üzerine yazmaz.', + brotliManagedPendingHelper: 'Brotli ayarları henüz etkin değil; kaydettikten sonra etkinleşir.', brotliManagedUnavailableHelper: 'Panel, yönetilen brotli yapılandırmasını nginx.conf dosyasına otomatik olarak ekleyemedi; aşağıdaki değerler etkin olmayacak.', brotliMinLengthHelper: 'brotli ile sıkıştırılacak minimum yanıt boyutu', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 2534135db..1da913a9b 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -4292,6 +4292,7 @@ const message = { brotliHelper: '開啟 brotli 壓縮,通常比 gzip 體積更小', brotliCompLevelHelper: 'brotli 壓縮率,取值 0 到 11', brotliManagedExternallyHelper: 'brotli 已在 nginx.conf 中手動設定,面板僅顯示目前生效值,不會覆寫。', + brotliManagedPendingHelper: 'brotli 設定尚未生效,點擊儲存後生效。', brotliManagedUnavailableHelper: '面板無法自動向 nginx.conf 寫入託管的 brotli 設定,以下設定不會生效。', brotliMinLengthHelper: 'brotli 壓縮的最小回應大小', brotliSaveFailed: 'brotli 設定儲存失敗,上方的 gzip 設定已套用', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index eb3686edd..cfc4729d9 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -4332,6 +4332,7 @@ const message = { brotliHelper: '开启 brotli 压缩,通常比 gzip 体积更小', brotliCompLevelHelper: 'brotli 压缩率,取值 0 到 11', brotliManagedExternallyHelper: 'brotli 已在 nginx.conf 中手动配置,面板仅显示当前生效值,不会覆盖。', + brotliManagedPendingHelper: 'brotli 配置尚未生效,点击保存后生效。', brotliManagedUnavailableHelper: '面板无法自动向 nginx.conf 写入托管的 brotli 配置,以下设置不会生效。', brotliMinLengthHelper: 'brotli 压缩的最小响应大小', brotliSaveFailed: 'brotli 设置保存失败,上方的 gzip 设置已应用', diff --git a/frontend/src/views/website/website/nginx/performance/index.vue b/frontend/src/views/website/website/nginx/performance/index.vue index 98a0074d8..cabeaf45b 100644 --- a/frontend/src/views/website/website/nginx/performance/index.vue +++ b/frontend/src/views/website/website/nginx/performance/index.vue @@ -54,6 +54,11 @@ {{ $t('nginx.brotliManagedExternallyHelper') }} + + + {{ $t('nginx.brotliManagedPendingHelper') }} + + {{ $t('nginx.brotliManagedUnavailableHelper') }} @@ -127,6 +132,8 @@ let loading = ref(false); // disappear together with the module. const brotliAvailable = ref(false); const brotliManagedExternally = ref(false); +// Pending: nginx does not load the managed file yet, and saving wires it in. +const brotliManagedPending = ref(false); const brotliManagedUnavailable = ref(false); const variablesRules = reactive({ @@ -201,6 +208,7 @@ const getBrotliParams = async () => { } const res = await getNginxConfigByScope({ scope: 'brotli' }); brotliManagedExternally.value = !!res.data?.managedExternally; + brotliManagedPending.value = !!res.data?.managedPending; brotliManagedUnavailable.value = !!res.data?.managedUnavailable; for (const param of res.data?.params ?? []) { if (param.params.length === 0) {