From ed9bbd01e2c5d6a8a71c43d91c5a4409a45aaae3 Mon Sep 17 00:00:00 2001 From: zhangwenjian Date: Mon, 7 Sep 2026 15:07:07 +0800 Subject: [PATCH] =?UTF-8?q?feat=E2=9C=A8:=20warn=20at=20start-up=20when=20?= =?UTF-8?q?the=20generator=20can=20write=20to=20this=20host?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gate in the previous commit is decided by application.mode, and the shipped configuration says dev. So the deployment most likely to be serving the writing endpoints is the one that changed nothing, and that is also the one least likely to go looking for them. A gate whose default is open needs to say so. Nothing is said in demo mode. The routes are registered there, but DemoEvn refuses all three by name, so a warning would describe an exposure that is not present. The decision is split from the logging so it can be tested. Three counter-proofs: warning in demo as well fails mode=demo; a warning that never fires fails mode=dev, which is what shows the line can be reached at all; and one that always fires fails every mode but dev. --- cmd/api/gen_warning_test.go | 38 +++++++++++++++++++++++++++++++++++++ cmd/api/server.go | 32 +++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 cmd/api/gen_warning_test.go diff --git a/cmd/api/gen_warning_test.go b/cmd/api/gen_warning_test.go new file mode 100644 index 00000000..52bd07b7 --- /dev/null +++ b/cmd/api/gen_warning_test.go @@ -0,0 +1,38 @@ +package api + +import ( + "testing" + + "github.com/go-admin-team/go-admin-core/v2/sdk/config" +) + +// The warning fires exactly where the generator's writing endpoints are served +// and nothing else refuses them. +// +// dev is the case the warning exists for: it is the shipped default, so it is +// the mode a deployment that changed nothing is running in. demo serves the +// routes too, but DemoEvn refuses all three by name, so warning there would +// describe an exposure that is not there. +func TestGeneratorWriteRoutesWarningFiresWhereTheExposureIs(t *testing.T) { + for _, tc := range []struct { + mode string + want bool + why string + }{ + {"dev", true, "shipped default, endpoints served and not refused"}, + {"demo", false, "served, but DemoEvn refuses all three"}, + {"test", false, "not served"}, + {"prod", false, "not served"}, + {"", false, "not served"}, + } { + t.Run("mode="+tc.mode, func(t *testing.T) { + previous := config.ApplicationConfig.Mode + t.Cleanup(func() { config.ApplicationConfig.Mode = previous }) + config.ApplicationConfig.Mode = tc.mode + + if got := generatorWriteRoutesNeedWarning(); got != tc.want { + t.Errorf("mode %q: warning = %v, want %v (%s)", tc.mode, got, tc.want, tc.why) + } + }) + } +} diff --git a/cmd/api/server.go b/cmd/api/server.go index e084e49b..7d000d48 100644 --- a/cmd/api/server.go +++ b/cmd/api/server.go @@ -177,6 +177,7 @@ func run() error { gin.SetMode(gin.ReleaseMode) } buildRouter() + reportGeneratorWriteRoutes() srv := &http.Server{ Addr: fmt.Sprintf("%s:%d", config.ApplicationConfig.Host, config.ApplicationConfig.Port), @@ -356,6 +357,37 @@ const ( kubernetesGraceSeconds = 30 ) +// reportGeneratorWriteRoutes says whether this process serves the code +// generator's writing endpoints, and to whom. +// +// The endpoints are gated on the mode, and the shipped configuration says dev - +// so the deployment most likely to be exposed is the one that changed nothing, +// and the one least likely to go looking. Silence there would leave the gate +// technically correct and practically useless. +// +// Nothing is said in demo mode. The routes are registered, but DemoEvn refuses +// all three by name, so a warning would describe an exposure that is not there. +func reportGeneratorWriteRoutes() { + if !generatorWriteRoutesNeedWarning() { + return + } + log.Warnf("the code generator's writing endpoints are served in mode %q: "+ + "/api/v1/gen/{toproject,apitofile,todb} write Go and Vue source onto this host and rows "+ + "into this database, and they are in CasbinExclude, so any account that can log in may "+ + "call them. Set application.mode to prod or test on anything that is not a workstation.", + config.ApplicationConfig.Mode) +} + +// generatorWriteRoutesNeedWarning reports whether there is an exposure to warn +// about: the endpoints are served, and nothing else is refusing them. +// +// Split from the logging so the decision can be tested. A warning nobody can +// make fire is indistinguishable from no warning at all, and this one exists +// precisely for the case nobody is looking at. +func generatorWriteRoutesNeedWarning() bool { + return otherrouter.GenWriteRoutesEnabled() && config.ApplicationConfig.Mode != "demo" +} + // reportShutdownBudget states what a shutdown will spend and whether it fits. // // The sum is taken from the resolved values, not from the configuration file: