taskcafe/internal/route/route.go
Jordan Knott c2ef8a7d56 feat: replace config system with viper based system
allows for config settings to be easily set through ENV variables,
config files, or CLI flags

adds flag to run migration on web server start (fixes #29)
2020-08-12 22:30:53 -05:00

114 lines
3.3 KiB
Go

package route
import (
"net/http"
"time"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/cors"
"github.com/jmoiron/sqlx"
log "github.com/sirupsen/logrus"
"github.com/jordanknott/taskcafe/internal/db"
"github.com/jordanknott/taskcafe/internal/frontend"
"github.com/jordanknott/taskcafe/internal/graph"
"github.com/jordanknott/taskcafe/internal/logger"
"os"
"path/filepath"
)
// spaHandler implements the http.Handler interface, so we can use it
// to respond to HTTP requests. The path to the static directory and
// path to the index file within that static directory are used to
// serve the SPA in the given static directory.
type FrontendHandler struct {
staticPath string
indexPath string
}
func IsDir(f http.File) bool {
fi, err := f.Stat()
if err != nil {
return false
}
return fi.IsDir()
}
func (h FrontendHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path, err := filepath.Abs(r.URL.Path)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
f, err := frontend.Frontend.Open(path)
if os.IsNotExist(err) || IsDir(f) {
index, err := frontend.Frontend.Open("index.html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.ServeContent(w, r, "index.html", time.Now(), index)
return
} else if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.ServeContent(w, r, path, time.Now(), f)
}
type TaskcafeHandler struct {
repo db.Repository
}
func NewRouter(dbConnection *sqlx.DB) (chi.Router, error) {
formatter := new(log.TextFormatter)
formatter.TimestampFormat = "02-01-2006 15:04:05"
formatter.FullTimestamp = true
routerLogger := log.New()
routerLogger.SetLevel(log.InfoLevel)
routerLogger.Formatter = formatter
r := chi.NewRouter()
cors := cors.New(cors.Options{
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
AllowedOrigins: []string{"*"},
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token", "Cookie"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300, // Maximum value not ignored by any of major browsers
})
r.Use(cors.Handler)
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(logger.NewStructuredLogger(routerLogger))
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(60 * time.Second))
repository := db.NewRepository(dbConnection)
taskcafeHandler := TaskcafeHandler{*repository}
var imgServer = http.FileServer(http.Dir("./uploads/"))
r.Group(func(mux chi.Router) {
mux.Mount("/auth", authResource{}.Routes(taskcafeHandler))
mux.Handle("/__graphql", graph.NewPlaygroundHandler("/graphql"))
mux.Mount("/uploads/", http.StripPrefix("/uploads/", imgServer))
})
r.Group(func(mux chi.Router) {
mux.Use(AuthenticationMiddleware)
mux.Post("/users/me/avatar", taskcafeHandler.ProfileImageUpload)
mux.Post("/auth/install", taskcafeHandler.InstallHandler)
mux.Handle("/graphql", graph.NewHandler(*repository))
})
frontend := FrontendHandler{staticPath: "build", indexPath: "index.html"}
r.Handle("/*", frontend)
return r, nil
}