2020-07-05 01:02:57 +02:00
|
|
|
package route
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
2021-10-31 00:20:41 +02:00
|
|
|
"github.com/RichardKnop/machinery/v1"
|
2020-07-05 01:02:57 +02:00
|
|
|
"github.com/go-chi/chi"
|
|
|
|
"github.com/go-chi/chi/middleware"
|
2021-04-29 04:32:19 +02:00
|
|
|
"github.com/go-chi/cors"
|
2021-11-18 00:11:28 +01:00
|
|
|
"github.com/go-redis/redis/v8"
|
2020-07-05 01:02:57 +02:00
|
|
|
"github.com/jmoiron/sqlx"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
2021-10-27 05:10:29 +02:00
|
|
|
"github.com/jordanknott/taskcafe/internal/config"
|
2020-08-07 03:50:35 +02:00
|
|
|
"github.com/jordanknott/taskcafe/internal/db"
|
|
|
|
"github.com/jordanknott/taskcafe/internal/frontend"
|
|
|
|
"github.com/jordanknott/taskcafe/internal/graph"
|
2021-11-18 00:11:28 +01:00
|
|
|
"github.com/jordanknott/taskcafe/internal/jobs"
|
2020-08-07 03:50:35 +02:00
|
|
|
"github.com/jordanknott/taskcafe/internal/logger"
|
2020-07-05 01:02:57 +02:00
|
|
|
)
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// FrontendHandler serves an embed React client through chi
|
2020-07-16 01:20:08 +02:00
|
|
|
type FrontendHandler struct {
|
|
|
|
staticPath string
|
|
|
|
indexPath string
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// IsDir checks if the given file is a directory
|
2020-07-16 01:20:08 +02:00
|
|
|
func IsDir(f http.File) bool {
|
|
|
|
fi, err := f.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return fi.IsDir()
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// ServeHTTP attempts to serve a requested file for the embedded React client
|
2020-07-16 01:20:08 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// TaskcafeHandler contains all the route handlers
|
2020-08-07 03:50:35 +02:00
|
|
|
type TaskcafeHandler struct {
|
2021-10-27 05:10:29 +02:00
|
|
|
repo db.Repository
|
|
|
|
AppConfig config.AppConfig
|
2020-07-05 01:02:57 +02:00
|
|
|
}
|
|
|
|
|
2020-08-21 01:11:24 +02:00
|
|
|
// NewRouter creates a new router for chi
|
2021-11-18 00:11:28 +01:00
|
|
|
func NewRouter(dbConnection *sqlx.DB, redisClient *redis.Client, jobServer *machinery.Server, appConfig config.AppConfig) (chi.Router, error) {
|
2020-07-05 01:02:57 +02:00
|
|
|
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()
|
|
|
|
r.Use(middleware.RequestID)
|
|
|
|
r.Use(middleware.RealIP)
|
|
|
|
r.Use(logger.NewStructuredLogger(routerLogger))
|
|
|
|
r.Use(middleware.Recoverer)
|
|
|
|
r.Use(middleware.Timeout(60 * time.Second))
|
|
|
|
|
2021-04-29 04:32:19 +02:00
|
|
|
r.Use(cors.Handler(cors.Options{
|
|
|
|
// AllowedOrigins: []string{"https://foo.com"}, // Use this to allow specific origin hosts
|
|
|
|
AllowedOrigins: []string{"https://*", "http://*"},
|
|
|
|
// AllowOriginFunc: func(r *http.Request, origin string) bool { return true },
|
|
|
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
|
|
AllowedHeaders: []string{"Accept", "Authorization", "Cookie", "Content-Type", "X-CSRF-Token"},
|
|
|
|
ExposedHeaders: []string{"Link"},
|
|
|
|
AllowCredentials: true,
|
|
|
|
MaxAge: 300, // Maximum value not ignored by any of major browsers
|
|
|
|
}))
|
|
|
|
|
2020-07-05 01:02:57 +02:00
|
|
|
repository := db.NewRepository(dbConnection)
|
2021-10-27 05:10:29 +02:00
|
|
|
taskcafeHandler := TaskcafeHandler{*repository, appConfig}
|
2020-07-05 01:02:57 +02:00
|
|
|
|
|
|
|
var imgServer = http.FileServer(http.Dir("./uploads/"))
|
|
|
|
r.Group(func(mux chi.Router) {
|
2020-08-07 03:50:35 +02:00
|
|
|
mux.Mount("/auth", authResource{}.Routes(taskcafeHandler))
|
2020-07-05 01:02:57 +02:00
|
|
|
mux.Handle("/__graphql", graph.NewPlaygroundHandler("/graphql"))
|
|
|
|
mux.Mount("/uploads/", http.StripPrefix("/uploads/", imgServer))
|
2020-10-21 01:52:09 +02:00
|
|
|
mux.Post("/auth/confirm", taskcafeHandler.ConfirmUser)
|
|
|
|
mux.Post("/auth/register", taskcafeHandler.RegisterUser)
|
2021-10-06 21:20:36 +02:00
|
|
|
mux.Get("/settings", taskcafeHandler.PublicSettings)
|
2021-10-26 04:03:22 +02:00
|
|
|
mux.Post("/logger", taskcafeHandler.HandleClientLog)
|
2020-07-05 01:02:57 +02:00
|
|
|
})
|
2021-04-29 04:32:19 +02:00
|
|
|
auth := AuthenticationMiddleware{*repository}
|
2021-11-18 00:11:28 +01:00
|
|
|
jobQueue := jobs.JobQueue{
|
|
|
|
Repository: *repository,
|
|
|
|
AppConfig: appConfig,
|
|
|
|
Server: jobServer,
|
|
|
|
}
|
2020-07-05 01:02:57 +02:00
|
|
|
r.Group(func(mux chi.Router) {
|
2020-09-13 01:03:17 +02:00
|
|
|
mux.Use(auth.Middleware)
|
2020-08-07 03:50:35 +02:00
|
|
|
mux.Post("/users/me/avatar", taskcafeHandler.ProfileImageUpload)
|
2021-11-18 00:11:28 +01:00
|
|
|
mux.Mount("/graphql", graph.NewHandler(*repository, appConfig, jobQueue, redisClient))
|
2020-07-05 01:02:57 +02:00
|
|
|
})
|
|
|
|
|
2020-07-16 01:20:08 +02:00
|
|
|
frontend := FrontendHandler{staticPath: "build", indexPath: "index.html"}
|
|
|
|
r.Handle("/*", frontend)
|
|
|
|
|
2020-07-05 01:02:57 +02:00
|
|
|
return r, nil
|
|
|
|
}
|