-
-
Notifications
You must be signed in to change notification settings - Fork 229
/
Copy pathapi.go
122 lines (104 loc) · 3.31 KB
/
api.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Package api configures an http server for administration and application resources.
package api
import (
"net/http"
"os"
"path"
"strings"
"time"
"github.com/dhax/go-base/api/admin"
"github.com/dhax/go-base/api/app"
"github.com/dhax/go-base/auth/jwt"
"github.com/dhax/go-base/auth/pwdless"
"github.com/dhax/go-base/database"
"github.com/dhax/go-base/email"
"github.com/dhax/go-base/logging"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"github.com/go-chi/render"
)
// New configures application resources and routes.
func New(enableCORS bool) (*chi.Mux, error) {
logger := logging.NewLogger()
db, err := database.DBConn()
if err != nil {
logger.WithField("module", "database").Error(err)
return nil, err
}
mailer, err := email.NewMailer()
if err != nil {
logger.WithField("module", "email").Error(err)
return nil, err
}
authStore := database.NewAuthStore(db)
authResource, err := pwdless.NewResource(authStore, mailer)
if err != nil {
logger.WithField("module", "auth").Error(err)
return nil, err
}
adminAPI, err := admin.NewAPI(db)
if err != nil {
logger.WithField("module", "admin").Error(err)
return nil, err
}
appAPI, err := app.NewAPI(db)
if err != nil {
logger.WithField("module", "app").Error(err)
return nil, err
}
r := chi.NewRouter()
r.Use(middleware.Recoverer)
r.Use(middleware.RequestID)
// r.Use(middleware.RealIP)
r.Use(middleware.Timeout(15 * time.Second))
r.Use(logging.NewStructuredLogger(logger))
r.Use(render.SetContentType(render.ContentTypeJSON))
// use CORS middleware if client is not served by this api, e.g. from other domain or CDN
if enableCORS {
r.Use(corsConfig().Handler)
}
r.Mount("/auth", authResource.Router())
r.Group(func(r chi.Router) {
r.Use(authResource.TokenAuth.Verifier())
r.Use(jwt.Authenticator)
r.Mount("/admin", adminAPI.Router())
r.Mount("/api", appAPI.Router())
})
r.Get("/ping", func(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte("pong"))
})
r.Get("/*", SPAHandler("public"))
return r, nil
}
func corsConfig() *cors.Cors {
// Basic CORS
// for more ideas, see: https://developer.github.com/v3/#cross-origin-resource-sharing
return 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"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 86400, // Maximum value not ignored by any of major browsers
})
}
// SPAHandler serves the public Single Page Application.
func SPAHandler(publicDir string) http.HandlerFunc {
handler := http.FileServer(http.Dir(publicDir))
return func(w http.ResponseWriter, r *http.Request) {
indexPage := path.Join(publicDir, "index.html")
serviceWorker := path.Join(publicDir, "service-worker.js")
requestedAsset := path.Join(publicDir, r.URL.Path)
if strings.Contains(requestedAsset, "service-worker.js") {
requestedAsset = serviceWorker
}
if _, err := os.Stat(requestedAsset); err != nil {
http.ServeFile(w, r, indexPage)
return
}
handler.ServeHTTP(w, r)
}
}