fix(mcp): CORS-aware scaffold, cors.wildcard rule, server-not-appended FP; v1.1.2

This commit is contained in:
2026-08-08 00:55:39 -06:00
parent 81233311d9
commit 6b4d9be141
9 changed files with 198 additions and 39 deletions
+38 -20
View File
@@ -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()
}