123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- package fiber
- import (
- "html/template"
- "log"
- "strconv"
- "time"
- "github.com/gofiber/fiber/v2"
- "github.com/gofiber/fiber/v2/middleware/basicauth"
- "github.com/gofiber/fiber/v2/middleware/cors"
- "github.com/gofiber/fiber/v2/middleware/limiter"
- "github.com/gofiber/fiber/v2/middleware/monitor"
- "github.com/gofiber/fiber/v2/middleware/recover"
- jwt "github.com/gofiber/jwt/v3"
- "github.com/gofiber/template/html"
- "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/repositories"
- authService "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/auth"
- cacheService "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/cache"
- envService "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/env"
- "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/handler"
- adminHandler "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/handler/admin"
- mailService "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/mailer"
- )
- const (
- appName = "dmitriygnatenko"
- templatesPath = "./../../internal/templates"
- staticPath = "../../web"
- metricsURI = "/metrics"
- loginRateLimiterMaxRequests = 10
- loginRateLimiterExpiration = 30 * time.Second
- )
- type (
- ServiceProvider interface {
- EnvService() *envService.Service
- MailerService() *mailService.Service
- AuthService() *authService.Service
- CacheService() *cacheService.Service
- ArticleRepository() *repositories.ArticleRepository
- TagRepository() *repositories.TagRepository
- ArticleTagRepository() *repositories.ArticleTagRepository
- UserRepository() *repositories.UserRepository
- }
- EnvService interface {
- StaticVersion() int
- GAKey() string
- }
- )
- func Init(sp ServiceProvider) (*fiber.App, error) {
- fiberApp := fiber.New(getConfig(sp))
- // Configure web root
- fiberApp.Static("/", staticPath)
- // Configure CORS middleware
- fiberApp.Use(cors.New(getCORSConfig(sp)))
- // Configure recover middleware
- fiberApp.Use(recover.New())
- // Configure JWT auth
- jwtAuth := jwt.New(getJWTConfig(sp))
- // Configure Basic auth
- basicAuth := basicauth.New(basicauth.Config{
- Users: map[string]string{
- sp.EnvService().BasicAuthUser(): sp.EnvService().BasicAuthPassword(),
- },
- })
- // Metrics
- fiberApp.Get(metricsURI, basicAuth, monitor.New(getMetricsConfig()))
- // Public handlers
- fiberApp.Get(
- "/",
- handler.MainPageHandler(
- sp.CacheService(),
- sp.ArticleRepository(),
- ),
- )
- fiberApp.Get(
- "/tag/:tag", handler.TagHandler(
- sp.CacheService(),
- sp.ArticleRepository(),
- sp.TagRepository(),
- ),
- )
- fiberApp.Get(
- "/article/:article",
- handler.ArticleHandler(
- sp.CacheService(),
- sp.ArticleRepository(),
- sp.TagRepository(),
- ),
- )
- // Protected handlers
- admin := fiberApp.Group("/admin", jwtAuth)
- admin.Get(
- "/",
- adminHandler.ArticleHandler(sp.ArticleRepository()),
- )
- admin.All("/login", limiter.New(limiter.Config{
- Max: loginRateLimiterMaxRequests,
- Expiration: loginRateLimiterExpiration,
- }), adminHandler.LoginHandler(
- sp.EnvService(),
- sp.AuthService(),
- sp.UserRepository(),
- ))
- admin.All(
- "/logout",
- adminHandler.LogoutHandler(sp.EnvService()),
- )
- admin.All(
- "/user/change-password",
- adminHandler.ChangePassword(
- sp.AuthService(),
- sp.UserRepository(),
- ),
- )
- admin.All(
- "/article/add",
- adminHandler.AddArticleHandler(
- sp.ArticleRepository(),
- sp.TagRepository(),
- sp.ArticleTagRepository(),
- sp.CacheService(),
- ),
- )
- admin.All(
- "/article/edit/:id<int>",
- adminHandler.EditArticleHandler(
- sp.ArticleRepository(),
- sp.TagRepository(),
- sp.ArticleTagRepository(),
- sp.CacheService(),
- ),
- )
- admin.All(
- "/article/delete/:id<int>",
- adminHandler.DeleteArticleHandler(
- sp.ArticleRepository(),
- sp.ArticleTagRepository(),
- sp.CacheService(),
- ),
- )
- admin.Get(
- "/tag",
- adminHandler.TagHandler(sp.TagRepository()),
- )
- admin.All(
- "/tag/add",
- adminHandler.AddTagHandler(
- sp.TagRepository(),
- sp.CacheService(),
- ),
- )
- admin.All(
- "/tag/edit/:id<int>",
- adminHandler.EditTagHandler(
- sp.TagRepository(),
- sp.CacheService(),
- ),
- )
- admin.All(
- "/tag/delete/:id<int>",
- adminHandler.DeleteTagHandler(
- sp.TagRepository(),
- sp.CacheService(),
- ),
- )
- return fiberApp, nil
- }
- func getConfig(sp ServiceProvider) fiber.Config {
- return fiber.Config{
- AppName: appName,
- DisableStartupMessage: true,
- Views: getViewsEngine(sp.EnvService()),
- ErrorHandler: getErrorHandler(sp),
- }
- }
- // nolint
- func getJWTConfig(sp ServiceProvider) jwt.Config {
- return jwt.Config{
- SigningKey: []byte(sp.EnvService().JWTSecretKey()),
- TokenLookup: "cookie:" + sp.EnvService().JWTCookie(),
- ErrorHandler: func(fctx *fiber.Ctx, err error) error {
- return fctx.Redirect("/admin/login")
- },
- Filter: func(fctx *fiber.Ctx) bool {
- method := fctx.Method()
- path := fctx.Path()
- if method != fiber.MethodGet && method != fiber.MethodPost &&
- method != fiber.MethodPut && method != fiber.MethodDelete {
- return true
- }
- if path == "/admin/login" {
- return true
- }
- return false
- },
- }
- }
- func getMetricsConfig() monitor.Config {
- return monitor.Config{
- Title: "dmitriygnatenko.ru metrics",
- }
- }
- func getCORSConfig(sp ServiceProvider) cors.Config {
- return cors.Config{
- AllowOrigins: sp.EnvService().CORSAllowOrigins(),
- AllowMethods: sp.EnvService().CORSAllowMethods(),
- }
- }
- func getViewsEngine(env EnvService) *html.Engine {
- engine := html.New(templatesPath, ".html")
- // nolint:gocritic
- engine.AddFunc("now", func() time.Time {
- return time.Now()
- })
- // nolint:gosec
- engine.AddFunc("noescape", func(str string) template.HTML {
- return template.HTML(str)
- })
- engine.AddFunc("gridsep", func(i, l int) bool {
- i++
- return i%3 == 0 && i != l
- })
- engine.AddFunc("version", func() string {
- return strconv.Itoa(env.StaticVersion())
- })
- engine.AddFunc("ga", func() string {
- return env.GAKey()
- })
- return engine
- }
- func getErrorHandler(sp ServiceProvider) fiber.ErrorHandler {
- return func(fctx *fiber.Ctx, err error) error {
- errCode := fiber.StatusInternalServerError
- if e, ok := err.(*fiber.Error); ok {
- errCode = e.Code
- }
- if err.Error() != "" {
- errorsEmail := sp.EnvService().ErrorsEmail()
- if errCode == fiber.StatusInternalServerError && errorsEmail != "" {
- log.Println(err)
- // nolint
- sp.MailerService().Send(
- errorsEmail,
- "AUTO - dmitriygnatenko.ru error",
- err.Error(),
- )
- }
- }
- var renderData fiber.Map
- if errCode == fiber.StatusNotFound {
- renderData = fiber.Map{
- "pageTitle": "Страница не найдена",
- "code": fiber.StatusNotFound,
- "text": "Запрашиваемая вами страница не найдена",
- }
- } else {
- renderData = fiber.Map{
- "pageTitle": "Внутренняя ошибка",
- "code": fiber.StatusInternalServerError,
- "text": "Внутренняя ошибка сервера, идем исправлять...",
- }
- }
- renderData["headTitle"] = "От слона к суслику - статьи про PHP, Go, алгоритмы"
- return fctx.Render("error", renderData, "_layout")
- }
- }
|