-
Notifications
You must be signed in to change notification settings - Fork 89
/
api.go
149 lines (123 loc) · 4.27 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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package api
import (
"context"
"net/http"
"regexp"
jwt "github.com/dgrijalva/jwt-go"
"github.com/go-chi/chi"
"github.com/netlify/git-gateway/conf"
"github.com/netlify/git-gateway/storage"
"github.com/netlify/git-gateway/storage/dial"
"github.com/netlify/netlify-commons/graceful"
"github.com/rs/cors"
"github.com/sebest/xff"
"github.com/sirupsen/logrus"
)
const (
audHeaderName = "X-JWT-AUD"
defaultVersion = "unknown version"
)
var bearerRegexp = regexp.MustCompile(`^(?:B|b)earer (\S+$)`)
// API is the main REST API
type API struct {
handler http.Handler
db storage.Connection
config *conf.GlobalConfiguration
version string
}
type GatewayClaims struct {
jwt.StandardClaims
Email string `json:"email"`
AppMetaData map[string]interface{} `json:"app_metadata"`
UserMetaData map[string]interface{} `json:"user_metadata"`
}
// ListenAndServe starts the REST API
func (a *API) ListenAndServe(hostAndPort string) {
log := logrus.WithField("component", "api")
server := graceful.NewGracefulServer(a.handler, log)
if err := server.Bind(hostAndPort); err != nil {
log.WithError(err).Fatal("http server bind failed")
}
if err := server.Listen(); err != nil {
log.WithError(err).Fatal("http server listen failed")
}
}
// NewAPI instantiates a new REST API
func NewAPI(globalConfig *conf.GlobalConfiguration, db storage.Connection) *API {
return NewAPIWithVersion(context.Background(), globalConfig, db, defaultVersion)
}
// NewAPIWithVersion creates a new REST API using the specified version
func NewAPIWithVersion(ctx context.Context, globalConfig *conf.GlobalConfiguration, db storage.Connection, version string) *API {
api := &API{config: globalConfig, db: db, version: version}
xffmw, _ := xff.Default()
r := newRouter()
r.UseBypass(xffmw.Handler)
r.Use(addRequestID)
r.UseBypass(newStructuredLogger(logrus.StandardLogger()))
r.Use(recoverer)
r.Get("/health", api.HealthCheck)
r.Route("/", func(r *router) {
if globalConfig.MultiInstanceMode {
r.Use(api.loadJWSSignatureHeader)
r.Use(api.loadInstanceConfig)
}
r.With(api.requireAuthentication).Mount("/github", NewGitHubGateway())
r.With(api.requireAuthentication).Mount("/gitlab", NewGitLabGateway())
r.With(api.requireAuthentication).Mount("/bitbucket", NewBitBucketGateway())
r.With(api.requireAuthentication).Get("/settings", api.Settings)
})
if globalConfig.MultiInstanceMode {
// Operator microservice API
r.With(api.verifyOperatorRequest).Get("/", api.GetAppManifest)
r.Route("/instances", func(r *router) {
r.Use(api.verifyOperatorRequest)
r.Post("/", api.CreateInstance)
r.Route("/{instance_id}", func(r *router) {
r.Use(api.loadInstance)
r.Get("/", api.GetInstance)
r.Put("/", api.UpdateInstance)
r.Delete("/", api.DeleteInstance)
})
})
}
corsHandler := cors.New(cors.Options{
AllowedMethods: []string{http.MethodGet, http.MethodHead, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch},
AllowedHeaders: []string{"Accept", "Authorization", "Private-Token", "Content-Type", audHeaderName},
AllowCredentials: true,
})
api.handler = corsHandler.Handler(chi.ServerBaseContext(r, ctx))
return api
}
// NewAPIFromConfigFile creates a new REST API using the provided configuration file.
func NewAPIFromConfigFile(filename string, version string) (*API, error) {
globalConfig, err := conf.LoadGlobal(filename)
if err != nil {
return nil, err
}
config, err := conf.LoadConfig(filename)
if err != nil {
return nil, err
}
db, err := dial.Dial(globalConfig)
if err != nil {
return nil, err
}
logrus.Infof("Config is: %v", config)
ctx, err := WithInstanceConfig(context.Background(), config, "")
if err != nil {
logrus.Fatalf("Error loading instance config: %+v", err)
}
return NewAPIWithVersion(ctx, globalConfig, db, version), nil
}
func (a *API) HealthCheck(w http.ResponseWriter, r *http.Request) error {
return sendJSON(w, http.StatusOK, map[string]string{
"version": a.version,
"name": "git-gateway",
"description": "git-gateway is a user registration and authentication API",
})
}
func WithInstanceConfig(ctx context.Context, config *conf.Configuration, instanceID string) (context.Context, error) {
ctx = withConfig(ctx, config)
ctx = withInstanceID(ctx, instanceID)
return ctx, nil
}