feat: add a new REST endpoint - GET /api/v1/user

This commit is contained in:
Dawid Wysokiński 2022-11-20 11:04:25 +01:00
parent 037619cae2
commit 87da75847d
Signed by: Kichiyaki
GPG Key ID: B5445E357FB8B892
5 changed files with 78 additions and 5 deletions

View File

@ -15,6 +15,9 @@ import (
"gitea.dwysokinski.me/twhelp/sessions/cmd/sessions/internal"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb"
"gitea.dwysokinski.me/twhelp/sessions/internal/router/meta"
"gitea.dwysokinski.me/twhelp/sessions/internal/router/rest"
"gitea.dwysokinski.me/twhelp/sessions/internal/service"
"github.com/kelseyhightower/envconfig"
"github.com/uptrace/bun"
"github.com/urfave/cli/v2"
@ -80,10 +83,38 @@ type serverConfig struct {
}
func newServer(cfg serverConfig) (*http.Server, error) {
apiCfg, err := newAPIConfig()
if err != nil {
return nil, fmt.Errorf("newAPIConfig: %w", err)
}
// repos
userRepo := bundb.NewUser(cfg.db)
apiKeyRepo := bundb.NewAPIKey(cfg.db)
// services
userSvc := service.NewUser(userRepo)
apiKeySvc := service.NewAPIKey(apiKeyRepo, userSvc)
// router
r := chi.NewRouter()
r.Use(getChiMiddlewares(cfg.logger)...)
r.Mount(metaEndpointsPrefix, meta.NewRouter([]meta.Checker{bundb.NewChecker(cfg.db)}))
r.Mount("/api", rest.NewRouter(rest.RouterConfig{
APIKeyVerifier: apiKeySvc,
CORS: rest.CORSConfig{
Enabled: apiCfg.CORSEnabled,
AllowedOrigins: apiCfg.CORSAllowedOrigins,
AllowedMethods: apiCfg.CORSAllowedMethods,
AllowCredentials: apiCfg.CORSAllowCredentials,
MaxAge: int(apiCfg.CORSMaxAge),
},
Swagger: rest.SwaggerConfig{
Enabled: apiCfg.SwaggerEnabled,
Host: apiCfg.SwaggerHost,
Schemes: apiCfg.SwaggerSchemes,
},
}))
return &http.Server{
Addr: ":" + defaultPort,
@ -95,6 +126,25 @@ func newServer(cfg serverConfig) (*http.Server, error) {
}, nil
}
type apiConfig struct {
SwaggerEnabled bool `envconfig:"SWAGGER_ENABLED" default:"true"`
SwaggerHost string `envconfig:"SWAGGER_HOST" default:""`
SwaggerSchemes []string `envconfig:"SWAGGER_SCHEMES" default:"http,https"`
CORSEnabled bool `envconfig:"CORS_ENABLED" default:"false"`
CORSAllowedOrigins []string `envconfig:"CORS_ALLOWED_ORIGINS" default:""`
CORSAllowCredentials bool `envconfig:"CORS_ALLOW_CREDENTIALS" default:"false"`
CORSAllowedMethods []string `envconfig:"CORS_ALLOWED_METHODS" default:"HEAD,GET"`
CORSMaxAge uint32 `envconfig:"CORS_MAX_AGE" default:"300"`
}
func newAPIConfig() (apiConfig, error) {
var cfg apiConfig
if err := envconfig.Process("API", &cfg); err != nil {
return apiConfig{}, fmt.Errorf("envconfig.Process: %w", err)
}
return cfg, nil
}
func getChiMiddlewares(logger *zap.Logger) chi.Middlewares {
return chi.Middlewares{
middleware.RealIP,

View File

@ -1,7 +1,7 @@
package model
type APIError struct {
Code string `json:"code" enums:"entity-not-found,route-not-found,method-not-allowed,validation-error,already-exists,internal-server-error"`
Code string `json:"code" enums:"entity-not-found,route-not-found,method-not-allowed,validation-error,unauthorized,already-exists,internal-server-error"`
Message string `json:"message"`
} // @name ApiError

View File

@ -12,3 +12,7 @@ package rest
// @BasePath /api/v1
// @query.collection.format multi
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name X-Api-Key

View File

@ -44,8 +44,6 @@ type RouterConfig struct {
func NewRouter(cfg RouterConfig) *chi.Mux {
router := chi.NewRouter()
router.Use(authMiddleware(cfg.APIKeyVerifier))
if cfg.CORS.Enabled {
router.Use(cors.Handler(cors.Options{
AllowedOrigins: cfg.CORS.AllowedOrigins,
@ -65,6 +63,14 @@ func NewRouter(cfg RouterConfig) *chi.Mux {
docs.SwaggerInfo.Schemes = cfg.Swagger.Schemes
r.Get("/swagger/*", httpSwagger.Handler())
}
r.Route("/", func(r chi.Router) {
r.Use(authMiddleware(cfg.APIKeyVerifier))
r.Get("/user", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
})
})
})
return router
@ -93,17 +99,28 @@ func authMiddleware(verifier APIKeyVerifier) func(http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
key := r.Header.Get("X-API-Key")
if key == "" {
next.ServeHTTP(w, r)
renderJSON(w, http.StatusUnauthorized, model.ErrorResp{
Error: model.APIError{
Code: "unauthorized",
Message: "invalid API key",
},
})
return
}
user, err := verifier.Verify(r.Context(), key)
if err != nil {
next.ServeHTTP(w, r)
renderJSON(w, http.StatusUnauthorized, model.ErrorResp{
Error: model.APIError{
Code: "unauthorized",
Message: "invalid API key",
},
})
return
}
fmt.Println(user)
next.ServeHTTP(w, r)
})
}
}

View File

@ -30,6 +30,8 @@ spec:
value: "10"
- name: DB_MAX_IDLE_CONNECTIONS
value: "5"
- name: API_SWAGGER_ENABLED
value: "true"
livenessProbe:
httpGet:
path: /_meta/livez