feat: initial commit
This commit is contained in:
+210
@@ -0,0 +1,210 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"expvar"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/FernandoVideira/LK_API_Temp/docs" // This is required to autogenerate swagger docs
|
||||
"github.com/FernandoVideira/LK_API_Temp/internal/auth"
|
||||
"github.com/FernandoVideira/LK_API_Temp/internal/mailer"
|
||||
"github.com/FernandoVideira/LK_API_Temp/internal/ratelimiter"
|
||||
"github.com/FernandoVideira/LK_API_Temp/internal/store"
|
||||
"github.com/FernandoVideira/LK_API_Temp/internal/store/cache"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/cors"
|
||||
httpSwagger "github.com/swaggo/http-swagger/v2" // http-swagger middleware
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type api struct {
|
||||
config config
|
||||
store store.Storage
|
||||
cacheStore cache.Storage
|
||||
logger *zap.SugaredLogger
|
||||
mailer mailer.Client
|
||||
authenticator auth.Authenticator
|
||||
rateLimiter ratelimiter.Limiter
|
||||
}
|
||||
|
||||
type config struct {
|
||||
addr string
|
||||
db dbConfig
|
||||
env string
|
||||
apiURL string
|
||||
mail mailConfig
|
||||
frontEndURL string
|
||||
auth authConfig
|
||||
redisCfg redisConfig
|
||||
rateLimiter ratelimiter.Config
|
||||
}
|
||||
|
||||
type mailConfig struct {
|
||||
exp time.Duration
|
||||
fromEmail string
|
||||
sendGrid sendGridConfig
|
||||
}
|
||||
|
||||
type authConfig struct {
|
||||
basic basicConfig
|
||||
token tokenConfig
|
||||
}
|
||||
|
||||
type basicConfig struct {
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
type tokenConfig struct {
|
||||
secret string
|
||||
exp time.Duration
|
||||
iss string
|
||||
aud string
|
||||
}
|
||||
|
||||
type redisConfig struct {
|
||||
addr string
|
||||
pw string
|
||||
db int
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// TODO: Switch to previously made SMTP mailer
|
||||
type sendGridConfig struct {
|
||||
apiKey string
|
||||
}
|
||||
|
||||
type dbConfig struct {
|
||||
addr string
|
||||
maxOpenConns int
|
||||
maxIdleConns int
|
||||
maxIdleTime time.Duration
|
||||
}
|
||||
|
||||
func (api *api) mount() http.Handler {
|
||||
r := chi.NewRouter()
|
||||
// *CORS
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(middleware.RealIP)
|
||||
r.Use(middleware.Logger)
|
||||
r.Use(middleware.Recoverer)
|
||||
|
||||
r.Use(cors.Handler(cors.Options{
|
||||
AllowedOrigins: []string{"https://*", "http://*"}, //* Use this for development
|
||||
//AllowedOrigins: []string{env.GetList("ALLOWED_ORIGINS", []string{"https://lk-api-temp.onrender.com"})}, //* Use this for production
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
||||
ExposedHeaders: []string{"Link"},
|
||||
AllowCredentials: false,
|
||||
MaxAge: 300,
|
||||
}))
|
||||
r.Use(api.RateLimiterMiddleware)
|
||||
|
||||
r.Use(middleware.Timeout(60 * time.Second))
|
||||
|
||||
r.Route("/v1", func(r chi.Router) {
|
||||
|
||||
//r.With(api.BasicAuthMiddleware()).Get("/health", api.healthCheckHandler)
|
||||
r.Get("/health", api.healthCheckHandler)
|
||||
r.With(api.BasicAuthMiddleware()).Get("/debug/vars", expvar.Handler().ServeHTTP)
|
||||
|
||||
docsURL := fmt.Sprintf("%s/swagger/doc.json", api.config.addr)
|
||||
r.Get("/swagger/*", httpSwagger.Handler(httpSwagger.URL(docsURL)))
|
||||
|
||||
r.Route("/posts", func(r chi.Router) {
|
||||
r.Use(api.AuthTokenMiddleware)
|
||||
r.Post("/", api.createPostHandler)
|
||||
|
||||
r.Route("/{id}", func(r chi.Router) {
|
||||
r.Use(api.postsContextMiddleware)
|
||||
|
||||
r.Get("/", api.getPostHandler)
|
||||
r.Patch("/", api.checkPostOwnership("moderator", api.updatePostHandler))
|
||||
r.Delete("/", api.checkPostOwnership("admin", api.deletePostHandler))
|
||||
r.Post("/comments", api.createCommentHandler)
|
||||
})
|
||||
})
|
||||
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Put("/activate/{token}", api.activateUserHandler)
|
||||
|
||||
r.Route("/{id}", func(r chi.Router) {
|
||||
r.Use(api.AuthTokenMiddleware)
|
||||
|
||||
r.Get("/", api.getUserHandler)
|
||||
r.Patch("/", api.checkProfileOwnership(api.updateUserHandler))
|
||||
r.Patch("/password", api.checkProfileOwnership(api.updatePasswordHandler))
|
||||
r.Put(("/follow"), api.followUserHandler)
|
||||
r.Put(("/unfollow"), api.unfollowUserHandler)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(api.AuthTokenMiddleware)
|
||||
r.Get("/feed", api.getUserFeedHandler)
|
||||
})
|
||||
})
|
||||
|
||||
r.Route("/authentication", func(r chi.Router) {
|
||||
r.Post("/user", api.registerUserHandler)
|
||||
r.Post("/token", api.createTokenHandler)
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (api *api) run(mux http.Handler) error {
|
||||
|
||||
//Docs
|
||||
docs.SwaggerInfo.Version = "0.0.1"
|
||||
docs.SwaggerInfo.Host = api.config.apiURL
|
||||
docs.SwaggerInfo.BasePath = "/v1"
|
||||
|
||||
srv := http.Server{
|
||||
Addr: api.config.addr,
|
||||
Handler: mux,
|
||||
WriteTimeout: 30 * time.Second,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
IdleTimeout: time.Minute,
|
||||
}
|
||||
|
||||
shutdown := make(chan error)
|
||||
|
||||
go func() {
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
s := <-quit
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
||||
defer cancel()
|
||||
|
||||
api.logger.Infow("shutting down server", "signal", s.String())
|
||||
|
||||
shutdown <- srv.Shutdown(ctx)
|
||||
|
||||
}()
|
||||
|
||||
api.logger.Infow("server has started", "addr", api.config.addr, "env", api.config.env)
|
||||
|
||||
err := srv.ListenAndServe()
|
||||
if !errors.Is(err, http.ErrServerClosed) {
|
||||
return err
|
||||
}
|
||||
|
||||
err = <-shutdown
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
api.logger.Infow("server has been shutdown")
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user