fix(mcp): CORS-aware scaffold, cors.wildcard rule, server-not-appended FP; v1.1.2
This commit is contained in:
+38
-20
@@ -100,6 +100,9 @@ func main() {
|
||||
const tplWire = `package wire
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"code.nochebuena.dev/einherjar/core/launcher"
|
||||
@@ -127,19 +130,29 @@ func Run() error {
|
||||
logger := logz.New(logCfg)
|
||||
|
||||
db := postgres.New(logger, cfg.PG)
|
||||
|
||||
// CORS: allow-all in local dev; explicit origins elsewhere. mw.CORS panics on
|
||||
// "*", so never pass a wildcard through APP_CORS_ORIGINS outside local.
|
||||
var corsMW func(http.Handler) http.Handler
|
||||
if strings.EqualFold(cfg.AppEnv, "local") {
|
||||
corsMW = mw.CORSAllowAll()
|
||||
} else {
|
||||
corsMW = mw.CORS(cfg.CORSOrigins)
|
||||
}
|
||||
|
||||
srv := server.New(logger, cfg.Server,
|
||||
server.WithMiddleware(
|
||||
mw.RequestID(uuid.NewString),
|
||||
mw.Recover(logger),
|
||||
mw.CORS(cfg.CORSOrigins),
|
||||
corsMW,
|
||||
mw.RequestLogger(logger),
|
||||
),
|
||||
)
|
||||
|
||||
lc := launcher.New(logger)
|
||||
lc := launcher.New(logger, cfg.Launcher)
|
||||
lc.Append(db, srv)
|
||||
|
||||
withHealth(lc, srv)
|
||||
withHealth(lc, srv, logger, cfg.Health, db)
|
||||
// … one withFeature(lc, srv, …) call per feature in your domain.
|
||||
|
||||
return lc.Run()
|
||||
@@ -149,22 +162,19 @@ func Run() error {
|
||||
const tplHealth = `package wire
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.nochebuena.dev/einherjar/contracts/logging"
|
||||
"code.nochebuena.dev/einherjar/contracts/observability"
|
||||
"code.nochebuena.dev/einherjar/core/launcher"
|
||||
"code.nochebuena.dev/einherjar/web/health"
|
||||
"code.nochebuena.dev/einherjar/web/server"
|
||||
)
|
||||
|
||||
// withHealth registers a liveness endpoint — the simplest with<Feature> hook.
|
||||
// A real feature hook takes its deps (logging.Logger, postgres.Provider, …) and
|
||||
// wires repo→service→handler inside the closure; see get_example("wire").
|
||||
func withHealth(lc launcher.Launcher, srv server.Server) {
|
||||
// withHealth wires a concurrent health endpoint from the app's Checkable components
|
||||
// (db, cache, …). health.NewHandlerWithConfig honors EINHERJAR_HEALTH_CHECK_TIMEOUT
|
||||
// through cfg. Add more with<Feature> hooks the same way; see get_example("wire").
|
||||
func withHealth(lc launcher.Launcher, srv server.Server, logger logging.Logger, cfg health.Config, checks ...observability.Checkable) {
|
||||
lc.BeforeStart(func() error {
|
||||
srv.Get("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte(` + "`" + `{"status":"ok"}` + "`" + `))
|
||||
})
|
||||
srv.Get("/health", health.NewHandlerWithConfig(logger, cfg, checks...).ServeHTTP)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -181,23 +191,27 @@ package config
|
||||
import (
|
||||
"github.com/caarlos0/env/v11"
|
||||
|
||||
"code.nochebuena.dev/einherjar/core/launcher"
|
||||
"code.nochebuena.dev/einherjar/core/logz"
|
||||
"code.nochebuena.dev/einherjar/db-postgres"
|
||||
"code.nochebuena.dev/einherjar/web/health"
|
||||
"code.nochebuena.dev/einherjar/web/server"
|
||||
)
|
||||
|
||||
// Config is the fully-resolved startup configuration. caarlos0/env recurses into
|
||||
// the nested framework configs, populating their EINHERJAR_LOG_* / EINHERJAR_SERVER_* /
|
||||
// EINHERJAR_PG_* tags from the environment next to the app-owned APP_* fields.
|
||||
// the nested framework configs, populating their EINHERJAR_* tags from the
|
||||
// environment next to the app-owned APP_* fields.
|
||||
type Config struct {
|
||||
AppEnv string ` + "`" + `env:"APP_ENV" envDefault:"local"` + "`" + `
|
||||
CORSOrigins []string ` + "`" + `env:"APP_CORS_ORIGINS" envSeparator:","` + "`" + `
|
||||
|
||||
// Framework component configs — composed verbatim; their EINHERJAR_* tags
|
||||
// load through this same env.Parse call.
|
||||
Log logz.Config // EINHERJAR_LOG_*
|
||||
Server server.Config // EINHERJAR_SERVER_*
|
||||
PG postgres.Config // EINHERJAR_PG_*
|
||||
Launcher launcher.Config // EINHERJAR_COMPONENT_STOP_TIMEOUT
|
||||
Log logz.Config // EINHERJAR_LOG_*
|
||||
Server server.Config // EINHERJAR_SERVER_*
|
||||
Health health.Config // EINHERJAR_HEALTH_CHECK_TIMEOUT
|
||||
PG postgres.Config // EINHERJAR_PG_*
|
||||
}
|
||||
|
||||
func Load() (Config, error) {
|
||||
@@ -223,10 +237,14 @@ func renderEnvExample(idx *index.Index, app string) string {
|
||||
|
||||
b.WriteString("# ── App (APP_*) ────────────────────────────────────────────────────────────\n")
|
||||
b.WriteString("APP_ENV=local\n")
|
||||
b.WriteString("APP_CORS_ORIGINS=*\n\n")
|
||||
b.WriteString("# APP_CORS_ORIGINS — explicit origins for non-local envs (comma-separated).\n")
|
||||
b.WriteString("# Local uses mw.CORSAllowAll() and ignores this; \"*\" is rejected by mw.CORS — never use it.\n")
|
||||
b.WriteString("APP_CORS_ORIGINS=\n\n")
|
||||
|
||||
writeEnvSection(&b, "Einherjar: launcher", envspec.FindStruct(idx, "core", "launcher", "Config"), app)
|
||||
writeEnvSection(&b, "Einherjar: logging", envspec.FindStruct(idx, "core", "logz", "Config"), app)
|
||||
writeEnvSection(&b, "Einherjar: HTTP server", envspec.FindStruct(idx, "web", "server", "Config"), app)
|
||||
writeEnvSection(&b, "Einherjar: health", envspec.FindStruct(idx, "web", "health", "Config"), app)
|
||||
writeEnvSection(&b, "Einherjar: PostgreSQL", envspec.FindStruct(idx, "db-postgres", "", "Config"), app)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user