-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathservice.go
159 lines (136 loc) · 4.45 KB
/
service.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
150
151
152
153
154
155
156
157
158
159
package server
import (
"context"
"encoding/base64"
"fmt"
"net/http"
"time"
"github.com/dghubble/gologin/v2"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
_ "github.com/lib/pq"
"github.com/rs/zerolog"
httpswagger "github.com/swaggo/http-swagger"
"golang.org/x/oauth2"
githuboauth2 "golang.org/x/oauth2/github"
"gorm.io/driver/postgres"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
"github.com/cosmos/atlas/config"
"github.com/cosmos/atlas/docs/api"
v1 "github.com/cosmos/atlas/server/router/v1"
)
// @securityDefinitions.apikey APIKeyAuth
// @in header
// @name Authorization
// Service implements the encapsulating Atlas service. It wraps a router which is
// responsible for handling all versioned API requests with a given Router that
// interacts the with Atlas data model. The Service is responsible for establishing
// a database connection and managing session cookies.
type Service struct {
logger zerolog.Logger
cfg config.Config
db *gorm.DB
cookieCfg gologin.CookieConfig
sessionStore *sessions.CookieStore
oauth2Cfg *oauth2.Config
router *mux.Router
server *http.Server
}
func NewService(logger zerolog.Logger, cfg config.Config) (*Service, error) {
dbLogger := NewDBLogger(logger).LogMode(gormlogger.Silent)
sessionKey, err := base64.StdEncoding.DecodeString(cfg.String(config.SessionKey))
if err != nil {
return nil, fmt.Errorf("failed to base64 decode session key: %w", err)
}
cookieCfg := gologin.DefaultCookieConfig
sessionStore := sessions.NewCookieStore(sessionKey, nil)
sessionStore.Options.HttpOnly = true
sessionStore.Options.Secure = true
sessionStore.Options.MaxAge = 3600 * 24 * 7 // 1 week
if cfg.Bool(config.Dev) {
dbLogger = dbLogger.LogMode(gormlogger.Info)
cookieCfg = gologin.DebugOnlyCookieConfig
sessionStore.Options.Secure = false
}
db, err := gorm.Open(postgres.Open(cfg.String(config.DatabaseURL)), &gorm.Config{Logger: dbLogger})
if err != nil {
return nil, err
}
service := &Service{
logger: logger.With().Str("module", "server").Logger(),
cfg: cfg,
db: db,
cookieCfg: cookieCfg,
sessionStore: sessionStore,
router: mux.NewRouter(),
oauth2Cfg: &oauth2.Config{
ClientID: cfg.String(config.GHClientID),
ClientSecret: cfg.String(config.GHClientSecret),
Endpoint: githuboauth2.Endpoint,
},
}
v1Router, err := v1.NewRouter(
service.logger,
cfg, service.db,
service.cookieCfg,
service.sessionStore,
service.oauth2Cfg,
func(token string) v1.GitHubClientI {
return v1.NewGitHubClient(token)
},
)
if err != nil {
return nil, err
}
// mount api docs
service.registerSwagger(cfg)
// register v1 API routes
v1Router.Register(service.router, v1.V1APIPathPrefix)
return service, nil
}
// Start starts the atlas service as a blocking process.
func (s *Service) Start() error {
s.server = &http.Server{
Handler: s.router,
Addr: s.cfg.String(config.ListenAddr),
WriteTimeout: s.cfg.Duration(config.HTTPReadTimeout),
ReadTimeout: s.cfg.Duration(config.HTTPWriteTimeout),
}
s.logger.Info().Str("address", s.server.Addr).Msg("starting atlas server...")
return s.server.ListenAndServe()
}
// Cleanup performs server cleanup. If the internal HTTP server is non-nil, the
// server will be shutdown after a grace period deadline.
func (s *Service) Cleanup() {
if s.server != nil {
// create a deadline to wait for all existing requests to finish
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
// Do not block if no connections exist, but otherwise, we will wait until
// the timeout deadline.
_ = s.server.Shutdown(ctx)
}
}
// GetDB returns the underlying database.
//
// FIXME: We should consider creating the database outside of the server package
// as an injected dependency.
func (s *Service) GetDB() *gorm.DB {
return s.db
}
func (s *Service) registerSwagger(cfg config.Config) {
api.SwaggerInfo.Title = "Atlas API"
api.SwaggerInfo.Description = "Atlas Cosmos SDK module registry API documentation."
api.SwaggerInfo.Version = "1.0"
api.SwaggerInfo.BasePath = v1.V1APIPathPrefix
if cfg.Bool(config.Dev) {
api.SwaggerInfo.Host = s.cfg.String(config.ListenAddr)
api.SwaggerInfo.Schemes = []string{"http"}
} else {
api.SwaggerInfo.Host = "api.atlas.cosmos.network"
api.SwaggerInfo.Schemes = []string{"https"}
}
// mount swagger API documentation
s.router.PathPrefix("/api/docs").Handler(httpswagger.WrapHandler)
}