Initial bootrap of application
- Creates scaffold for router and server - Main program set up for environment, OS signal listener - Makefile generic commands
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
package http
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrUnknownError = errors.New("unknown error occurred")
|
||||
ErrUnrecognizedCode = errors.New("unrecognized status code")
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package http
|
||||
|
||||
import "net/http"
|
||||
|
||||
func (s *server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
|
||||
func (s *server) handleReadiness(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// server holds configurations and services used by the HTTP server and handlers.
|
||||
type server struct {
|
||||
port int
|
||||
logger *logrus.Logger
|
||||
environment string
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
// NewServer initializes a new server with the required configurations.
|
||||
func NewServer(port int, logger *logrus.Logger, environment string) *server {
|
||||
return &server{
|
||||
port: port,
|
||||
logger: logger,
|
||||
environment: environment,
|
||||
server: &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", port),
|
||||
ReadTimeout: 60 * time.Second,
|
||||
WriteTimeout: 60 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Run builds the routes and starts the server listening on the configured port.
|
||||
func (s *server) Run() error {
|
||||
s.server.Handler = s.buildRoutes()
|
||||
s.logger.Infof("starting server on port: %d", s.port)
|
||||
return s.server.ListenAndServe()
|
||||
}
|
||||
|
||||
// Shutdown calls Shutdown on the http.Server which attempts to gracefully shutdown. If the context deadline is exceeded any cotnext errors are returned.
|
||||
func (s *server) Shutdown(ctx context.Context) error {
|
||||
return s.server.Shutdown(ctx)
|
||||
}
|
||||
|
||||
// buildRoutes configures a router with middleware, headers, and routes.
|
||||
func (s *server) buildRoutes() http.Handler {
|
||||
r := s.configureRouter()
|
||||
|
||||
r.Get("/health", s.handleHealth)
|
||||
r.Get("/ready", s.handleReadiness)
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Route("/v1", func(r chi.Router) {
|
||||
// r.Group(s.produceGroup)
|
||||
})
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// configureRouter returns a configured chi.Router with sane defaults.
|
||||
func (s *server) configureRouter() chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(middleware.RealIP)
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(setResponseHeaders(map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"Allow-Access-Control-Origin": "*",
|
||||
"Allow-Access-Control-Method": "OPTIONS, GET, POST, DELETE",
|
||||
"Allow-Access-Control-Headers": "Origin, Content-Type, Accept",
|
||||
"Access-Control-Max-Age": "600",
|
||||
}))
|
||||
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package http
|
||||
|
||||
import "net/http"
|
||||
|
||||
// setResponseHeaders is a middleware that accepts a map[string]string of headers:values and sets them for all responses.
|
||||
func setResponseHeaders(headers map[string]string) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
for header, value := range headers {
|
||||
w.Header().Set(header, value)
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (s *server) writeSuccess(ctx context.Context, w http.ResponseWriter, data interface{}, status int) error {
|
||||
if status != 0 {
|
||||
w.WriteHeader(status)
|
||||
}
|
||||
|
||||
if data != nil {
|
||||
err := json.NewEncoder(w).Encode(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) writeError(ctx context.Context, w http.ResponseWriter, err error, code int) error {
|
||||
if http.StatusText(code) == "" {
|
||||
return ErrUnrecognizedCode
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = ErrUnknownError
|
||||
}
|
||||
|
||||
s.logger.Errorf("request failed: %v", err.Error())
|
||||
w.WriteHeader(code)
|
||||
|
||||
res := struct {
|
||||
Message string `json:"message"`
|
||||
}{
|
||||
Message: http.StatusText(code),
|
||||
}
|
||||
|
||||
return json.NewEncoder(w).Encode(res)
|
||||
}
|
||||
Reference in New Issue
Block a user