Config. environment (throughenv var 'ENVIRONMENT')
This commit is contained in:
@@ -6,20 +6,21 @@ import (
|
||||
|
||||
type (
|
||||
Options struct {
|
||||
ActionLog options.ActionLogOpt
|
||||
SMTP options.SMTPOpt
|
||||
Auth options.AuthOpt
|
||||
HTTPClient options.HTTPClientOpt
|
||||
DB options.DBOpt
|
||||
Upgrade options.UpgradeOpt
|
||||
Provision options.ProvisionOpt
|
||||
Sentry options.SentryOpt
|
||||
ObjStore options.ObjectStoreOpt
|
||||
Corredor options.CorredorOpt
|
||||
Monitor options.MonitorOpt
|
||||
WaitFor options.WaitForOpt
|
||||
HTTPServer options.HTTPServerOpt
|
||||
Websocket options.WebsocketOpt
|
||||
Environment options.EnvironmentOpt
|
||||
ActionLog options.ActionLogOpt
|
||||
SMTP options.SMTPOpt
|
||||
Auth options.AuthOpt
|
||||
HTTPClient options.HTTPClientOpt
|
||||
DB options.DBOpt
|
||||
Upgrade options.UpgradeOpt
|
||||
Provision options.ProvisionOpt
|
||||
Sentry options.SentryOpt
|
||||
ObjStore options.ObjectStoreOpt
|
||||
Corredor options.CorredorOpt
|
||||
Monitor options.MonitorOpt
|
||||
WaitFor options.WaitForOpt
|
||||
HTTPServer options.HTTPServerOpt
|
||||
Websocket options.WebsocketOpt
|
||||
}
|
||||
)
|
||||
|
||||
@@ -29,20 +30,23 @@ func NewOptions(prefix ...string) *Options {
|
||||
p = prefix[0]
|
||||
}
|
||||
|
||||
// @todo remover prefixes on opt constructors and
|
||||
// pass in EnvironmentOpt so we can have environment-dependant defaults
|
||||
return &Options{
|
||||
ActionLog: *options.ActionLog(),
|
||||
Auth: *options.Auth(),
|
||||
SMTP: *options.SMTP(p),
|
||||
HTTPClient: *options.HttpClient(p),
|
||||
DB: *options.DB(p),
|
||||
Upgrade: *options.Upgrade(p),
|
||||
Provision: *options.Provision(p),
|
||||
Sentry: *options.Sentry(p),
|
||||
ObjStore: *options.ObjectStore(p),
|
||||
Corredor: *options.Corredor(),
|
||||
Monitor: *options.Monitor(p),
|
||||
WaitFor: *options.WaitFor(p),
|
||||
HTTPServer: *options.HTTP(p),
|
||||
Websocket: *options.Websocket(p),
|
||||
Environment: *options.Environment(),
|
||||
ActionLog: *options.ActionLog(),
|
||||
Auth: *options.Auth(),
|
||||
SMTP: *options.SMTP(p),
|
||||
HTTPClient: *options.HttpClient(p),
|
||||
DB: *options.DB(p),
|
||||
Upgrade: *options.Upgrade(p),
|
||||
Provision: *options.Provision(p),
|
||||
Sentry: *options.Sentry(p),
|
||||
ObjStore: *options.ObjectStore(p),
|
||||
Corredor: *options.Corredor(),
|
||||
Monitor: *options.Monitor(p),
|
||||
WaitFor: *options.WaitFor(p),
|
||||
HTTPServer: *options.HTTP(p),
|
||||
Websocket: *options.Websocket(p),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func (app *CortezaApp) Serve(ctx context.Context) (err error) {
|
||||
|
||||
{
|
||||
// @todo refactor wait-for out of HTTP API server.
|
||||
app.HttpServer = server.New(app.Log, app.Opt.HTTPServer, app.Opt.WaitFor)
|
||||
app.HttpServer = server.New(app.Log, app.Opt.Environment, app.Opt.HTTPServer, app.Opt.WaitFor)
|
||||
app.HttpServer.MountRoutes(app.mountHttpRoutes)
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
@@ -9,20 +9,16 @@ import (
|
||||
type ctxKeyDebug struct{}
|
||||
|
||||
// Packs remote address to context
|
||||
func DebugToContext(next http.Handler) http.Handler {
|
||||
if true {
|
||||
// debug disabled
|
||||
return next
|
||||
func DebugToContext(production bool) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
next.ServeHTTP(w, req.WithContext(context.WithValue(req.Context(), ctxKeyDebug{}, !production)))
|
||||
})
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
next.ServeHTTP(w, req.WithContext(context.WithValue(req.Context(), ctxKeyDebug{}, true)))
|
||||
})
|
||||
}
|
||||
|
||||
// DebugFromContext returns remote IP address from context
|
||||
func DebugFromContext(ctx context.Context) bool {
|
||||
return true
|
||||
debug, ok := ctx.Value(ctxKeyDebug{}).(bool)
|
||||
return ok && debug
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func encode(w http.ResponseWriter, r *http.Request, payload interface{}) {
|
||||
_ = err.Apply(errors.StackTrimAtFn("http.HandlerFunc.ServeHTTP"))
|
||||
}
|
||||
|
||||
errors.ServeHTTP(w, r, err, DebugFromContext(r.Context()))
|
||||
errors.ServeHTTP(w, r, err, !DebugFromContext(r.Context()))
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
@@ -2,24 +2,22 @@ package server
|
||||
|
||||
import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/api"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/getsentry/sentry-go/http"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/getsentry/sentry-go/http"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
)
|
||||
|
||||
func BaseMiddleware(log *zap.Logger) []func(http.Handler) http.Handler {
|
||||
func BaseMiddleware(isProduction bool, log *zap.Logger) []func(http.Handler) http.Handler {
|
||||
return []func(http.Handler) http.Handler{
|
||||
handleCORS,
|
||||
middleware.RealIP,
|
||||
api.RemoteAddrToContext,
|
||||
middleware.RequestID,
|
||||
api.DebugToContext(isProduction),
|
||||
contextLogger(log),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,22 @@ import (
|
||||
|
||||
type (
|
||||
server struct {
|
||||
log *zap.Logger
|
||||
httpOpt options.HTTPServerOpt
|
||||
waitForOpt options.WaitForOpt
|
||||
endpoints []func(r chi.Router)
|
||||
log *zap.Logger
|
||||
httpOpt options.HTTPServerOpt
|
||||
waitForOpt options.WaitForOpt
|
||||
environmentOpt options.EnvironmentOpt
|
||||
endpoints []func(r chi.Router)
|
||||
}
|
||||
)
|
||||
|
||||
func New(log *zap.Logger, httpOpt options.HTTPServerOpt, waitForOpt options.WaitForOpt) *server {
|
||||
func New(log *zap.Logger, envOpt options.EnvironmentOpt, httpOpt options.HTTPServerOpt, waitForOpt options.WaitForOpt) *server {
|
||||
return &server{
|
||||
endpoints: make([]func(r chi.Router), 0),
|
||||
log: log.Named("http"),
|
||||
httpOpt: httpOpt,
|
||||
waitForOpt: waitForOpt,
|
||||
endpoints: make([]func(r chi.Router), 0),
|
||||
log: log.Named("http"),
|
||||
|
||||
environmentOpt: envOpt,
|
||||
httpOpt: httpOpt,
|
||||
waitForOpt: waitForOpt,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +51,7 @@ func (s server) Serve(ctx context.Context) {
|
||||
router := chi.NewRouter()
|
||||
|
||||
// Base middleware, CORS, RealIP, RequestID, context-logger
|
||||
router.Use(BaseMiddleware(s.log)...)
|
||||
router.Use(BaseMiddleware(s.environmentOpt.IsProduction(), s.log)...)
|
||||
|
||||
router.Group(func(r chi.Router) {
|
||||
s.bindMiscRoutes(r)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// ServeHTTP Prepares and encodes given error for HTTP transport
|
||||
//
|
||||
// mask arg hides extra/debug info
|
||||
func ServeHTTP(w http.ResponseWriter, r *http.Request, err error, debug bool) {
|
||||
func ServeHTTP(w http.ResponseWriter, r *http.Request, err error, mask bool) {
|
||||
var (
|
||||
// Very naive approach on parsing accept headers
|
||||
acceptsJson = strings.Contains(r.Header.Get("accept"), "application/json")
|
||||
@@ -31,12 +31,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, err error, debug bool) {
|
||||
|
||||
w.WriteHeader(code)
|
||||
|
||||
if !debug {
|
||||
// trim error details when not debugging
|
||||
err = fmt.Errorf(err.Error())
|
||||
}
|
||||
|
||||
if debug && !acceptsJson {
|
||||
if !mask && !acceptsJson {
|
||||
// Prettify error for plain text debug output
|
||||
w.Header().Set("Content-Type", "plain/text")
|
||||
writeHttpPlain(w, err)
|
||||
@@ -46,7 +41,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request, err error, debug bool) {
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
writeHttpJSON(w, err)
|
||||
writeHttpJSON(w, err, mask)
|
||||
}
|
||||
|
||||
func writeHttpPlain(w io.Writer, err error) {
|
||||
@@ -97,14 +92,20 @@ func writeHttpPlain(w io.Writer, err error) {
|
||||
|
||||
}
|
||||
|
||||
func writeHttpJSON(w io.Writer, err error) {
|
||||
func writeHttpJSON(w io.Writer, err error, mask bool) {
|
||||
var (
|
||||
wrap = struct {
|
||||
Error interface{} `json:"error"`
|
||||
}{}
|
||||
)
|
||||
|
||||
if c, is := err.(*Error); is {
|
||||
if se, is := err.(interface{ Safe() bool }); !is || !se.Safe() {
|
||||
// trim error details when not debugging or error is not safe
|
||||
err = fmt.Errorf(err.Error())
|
||||
}
|
||||
|
||||
if c, is := err.(json.Marshaler); is {
|
||||
// take advantage of JSON marshaller on error
|
||||
wrap.Error = c
|
||||
} else {
|
||||
wrap.Error = map[string]string{"message": err.Error()}
|
||||
|
||||
@@ -11,12 +11,10 @@ func ExampleSimpleErrorAsText() {
|
||||
// Output:
|
||||
// Error: dummy error
|
||||
// --------------------------------------------------------------------------------
|
||||
// Note: you are seeing this because system is running in development mode
|
||||
// and HTTP request is made without "Accept: .../json" headers
|
||||
}
|
||||
|
||||
func ExampleSimpleErrorAsJson() {
|
||||
writeHttpJSON(os.Stdout, fmt.Errorf("dummy error"))
|
||||
writeHttpJSON(os.Stdout, fmt.Errorf("dummy error"), true)
|
||||
|
||||
// Output:
|
||||
// {"error":{"message":"dummy error"}}
|
||||
@@ -31,14 +29,12 @@ func ExampleErrorAsText() {
|
||||
// --------------------------------------------------------------------------------
|
||||
// a: b
|
||||
// --------------------------------------------------------------------------------
|
||||
// Note: you are seeing this because system is running in development mode
|
||||
// and HTTP request is made without "Accept: .../json" headers
|
||||
}
|
||||
|
||||
func ExampleErrorAsJson() {
|
||||
err := New(0, "dummy error", Meta("a", "b"), Meta(&Error{}, "nope"))
|
||||
err.stack = nil // will not test the stack as file path & line numbers might change
|
||||
writeHttpJSON(os.Stdout, err)
|
||||
writeHttpJSON(os.Stdout, err, false)
|
||||
|
||||
// Output:
|
||||
// {"error":{"message":"dummy error","meta":{"a":"b"}}}
|
||||
|
||||
32
pkg/options/environment.go
Normal file
32
pkg/options/environment.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package options
|
||||
|
||||
import "strings"
|
||||
|
||||
type (
|
||||
EnvironmentOpt struct {
|
||||
Environment string `env:"ENVIRONMENT"`
|
||||
}
|
||||
)
|
||||
|
||||
func Environment() (o *EnvironmentOpt) {
|
||||
o = &EnvironmentOpt{
|
||||
Environment: "production",
|
||||
}
|
||||
|
||||
fill(o)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (e EnvironmentOpt) IsDevelopment() bool {
|
||||
return strings.HasPrefix(e.Environment, "dev")
|
||||
}
|
||||
|
||||
func (e EnvironmentOpt) IsTest() bool {
|
||||
return strings.HasPrefix(e.Environment, "test")
|
||||
}
|
||||
|
||||
func (e EnvironmentOpt) IsProduction() bool {
|
||||
return !e.IsDevelopment() &&
|
||||
!e.IsTest()
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func InitTestApp() {
|
||||
|
||||
if r == nil {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(logger.Default())...)
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func InitTestApp() {
|
||||
|
||||
if r == nil {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(logger.Default())...)
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func InitTestApp() {
|
||||
|
||||
if r == nil {
|
||||
r = chi.NewRouter()
|
||||
r.Use(server.BaseMiddleware(logger.Default())...)
|
||||
r.Use(server.BaseMiddleware(false, logger.Default())...)
|
||||
helpers.BindAuthMiddleware(r)
|
||||
rest.MountRoutes(r)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user