mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-09-01 16:48:19 +00:00
feat: add cors support
optionally control cross origin requests by origin and method
This commit is contained in:
+1
-1
@@ -267,7 +267,7 @@ func main() {
|
||||
}()
|
||||
|
||||
// ── HTTP server ──────────────────────────────────────────────────────────
|
||||
r := router.New(h, reader, []*ingest.Worker{broker1, broker2}, maxConnsPerIP)
|
||||
r := router.New(h, reader, []*ingest.Worker{broker1, broker2}, maxConnsPerIP, cfg.CORS)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: addr,
|
||||
|
||||
@@ -75,6 +75,18 @@ packets:
|
||||
websocket:
|
||||
max_connections_per_ip: 5 # default: 5
|
||||
|
||||
# CORS configuration.
|
||||
# Controls which origins, methods and headers are allowed for cross-origin requests.
|
||||
# Defaults to allowing all origins with GET/HEAD/OPTIONS if omitted — appropriate
|
||||
# for a public read-only API. Restrict allowed_origins if you expose write endpoints.
|
||||
#cors:
|
||||
# allowed_origins:
|
||||
# - "https://app.example.com"
|
||||
# allowed_methods: [GET, HEAD, OPTIONS, POST]
|
||||
# allowed_headers: [Accept, Authorization, Content-Type]
|
||||
# allow_credentials: false
|
||||
# max_age: 300
|
||||
|
||||
# Redis caching layer.
|
||||
# If REDIS_ADDR is not set, caching is disabled and all reads go directly to PostgreSQL.
|
||||
# TTLs are specified as duration strings e.g. "30m", "1h", "2h".
|
||||
|
||||
@@ -19,6 +19,7 @@ require (
|
||||
filippo.io/edwards25519 v1.2.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/go-chi/cors v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/spec v0.20.6 // indirect
|
||||
|
||||
@@ -14,6 +14,8 @@ github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2I
|
||||
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
|
||||
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
|
||||
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
|
||||
@@ -12,10 +12,12 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/api"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/api/handlers"
|
||||
mw "github.com/MeshCore-Beacon/beacon-server/internal/api/middleware"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/config"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/hub"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/ingest"
|
||||
"github.com/MeshCore-Beacon/beacon-server/internal/ws"
|
||||
@@ -39,9 +41,34 @@ import (
|
||||
//
|
||||
// The private group is stubbed and ready for the auth middleware drop-in
|
||||
// described in Future Features → Admin authentication.
|
||||
func New(h *hub.Hub, reader api.Reader, workers []*ingest.Worker, maxConnsPerIP int) http.Handler {
|
||||
func New(h *hub.Hub, reader api.Reader, workers []*ingest.Worker, maxConnsPerIP int, corsCfg config.CORSConfig) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
|
||||
// ── CORS ─────────────────────────────────────────────────────────────────
|
||||
allowedOrigins := corsCfg.AllowedOrigins
|
||||
if len(allowedOrigins) == 0 {
|
||||
allowedOrigins = []string{"*"}
|
||||
}
|
||||
allowedMethods := corsCfg.AllowedMethods
|
||||
if len(allowedMethods) == 0 {
|
||||
allowedMethods = []string{"GET", "HEAD", "OPTIONS"}
|
||||
}
|
||||
allowedHeaders := corsCfg.AllowedHeaders
|
||||
if len(allowedHeaders) == 0 {
|
||||
allowedHeaders = []string{"Accept", "Authorization", "Content-Type"}
|
||||
}
|
||||
maxAge := corsCfg.MaxAge
|
||||
if maxAge == 0 {
|
||||
maxAge = 300
|
||||
}
|
||||
r.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: allowedOrigins,
|
||||
AllowedMethods: allowedMethods,
|
||||
AllowedHeaders: allowedHeaders,
|
||||
AllowCredentials: corsCfg.AllowCredentials,
|
||||
MaxAge: maxAge,
|
||||
}))
|
||||
|
||||
// ── Global middleware ────────────────────────────────────────────────────
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(middleware.RealIP)
|
||||
|
||||
@@ -23,6 +23,33 @@ type Config struct {
|
||||
Ingest IngestFilterConfig `yaml:"ingest"`
|
||||
Scopes []ScopeConfig `yaml:"scopes"`
|
||||
Cache CacheConfig `yaml:"cache"`
|
||||
CORS CORSConfig `yaml:"cors"`
|
||||
}
|
||||
|
||||
// CORSConfig controls Cross-Origin Resource Sharing behaviour.
|
||||
// If omitted, Beacon defaults to allowing all origins, which is appropriate
|
||||
// for a public read-only API. Operators exposing write endpoints should
|
||||
// restrict AllowedOrigins to known frontends.
|
||||
type CORSConfig struct {
|
||||
// AllowedOrigins is the list of origins permitted to make cross-origin
|
||||
// requests. Use ["*"] to allow all origins (default if omitted).
|
||||
AllowedOrigins []string `yaml:"allowed_origins"`
|
||||
|
||||
// AllowedMethods is the list of HTTP methods allowed in CORS requests.
|
||||
// Defaults to [GET, HEAD, OPTIONS] if omitted.
|
||||
AllowedMethods []string `yaml:"allowed_methods"`
|
||||
|
||||
// AllowedHeaders is the list of request headers allowed in CORS requests.
|
||||
// Defaults to [Accept, Authorization, Content-Type] if omitted.
|
||||
AllowedHeaders []string `yaml:"allowed_headers"`
|
||||
|
||||
// AllowCredentials indicates whether the request can include user
|
||||
// credentials (cookies, HTTP authentication). Defaults to false.
|
||||
AllowCredentials bool `yaml:"allow_credentials"`
|
||||
|
||||
// MaxAge is the number of seconds the browser may cache a preflight
|
||||
// response. Defaults to 300 if omitted.
|
||||
MaxAge int `yaml:"max_age"`
|
||||
}
|
||||
|
||||
// CacheConfig controls Redis caching behaviour.
|
||||
|
||||
Reference in New Issue
Block a user