-
Notifications
You must be signed in to change notification settings - Fork 215
/
Copy pathapi.go
99 lines (83 loc) · 2.67 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
package api
import (
"fmt"
"net/http"
"regexp"
"time"
"golang.org/x/net/context"
jwt "github.com/dgrijalva/jwt-go"
"github.com/guregu/kami"
"github.com/jinzhu/gorm"
"github.com/netlify/netlify-commerce/conf"
"github.com/netlify/netlify-commerce/mailer"
"github.com/rs/cors"
)
var bearerRegexp = regexp.MustCompile(`^(?:B|b)earer (\S+$)`)
// API is the main REST API
type API struct {
handler http.Handler
db *gorm.DB
config *conf.Configuration
mailer *mailer.Mailer
httpClient *http.Client
}
type JWTClaims struct {
ID string `json:"id"`
Email string `json:"email"`
*jwt.StandardClaims
}
func (a *API) withConfig(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context {
return context.WithValue(ctx, "config", a.config)
}
func (a *API) withToken(ctx context.Context, w http.ResponseWriter, r *http.Request) context.Context {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
return ctx
}
matches := bearerRegexp.FindStringSubmatch(authHeader)
if len(matches) != 2 {
UnauthorizedError(w, "Bad authentication header")
return nil
}
token, err := jwt.ParseWithClaims(matches[1], &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
if token.Header["alg"] != "HS256" {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return []byte(getConfig(ctx).JWT.Secret), nil
})
if err != nil {
UnauthorizedError(w, fmt.Sprintf("Invalid token: %v", err))
return nil
}
claims := token.Claims.(*JWTClaims)
if claims.StandardClaims.ExpiresAt < time.Now().Unix() {
UnauthorizedError(w, fmt.Sprintf("Expired token: %v", err))
return nil
}
return context.WithValue(ctx, "jwt", token)
}
// ListenAndServe starts the REST API
func (a *API) ListenAndServe(hostAndPort string) error {
return http.ListenAndServe(hostAndPort, a.handler)
}
// NewAPI instantiates a new REST API
func NewAPI(config *conf.Configuration, db *gorm.DB, mailer *mailer.Mailer) *API {
api := &API{config: config, db: db, mailer: mailer, httpClient: &http.Client{}}
mux := kami.New()
mux.Use("/", api.withConfig)
mux.Use("/", api.withToken)
mux.Get("/", api.Index)
mux.Get("/orders", api.OrderList)
mux.Post("/orders", api.OrderCreate)
mux.Get("/orders/:id", api.OrderView)
mux.Get("/orders/:order_id/payments", api.PaymentList)
mux.Post("/orders/:order_id/payments", api.PaymentCreate)
mux.Get("/vatnumbers/:number", api.VatnumberLookup)
corsHandler := cors.New(cors.Options{
AllowedMethods: []string{"GET", "POST", "PATCH", "PUT", "DELETE"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type"},
AllowCredentials: true,
})
api.handler = corsHandler.Handler(mux)
return api
}