213 lines
7.8 KiB
Go
213 lines
7.8 KiB
Go
package api
|
|
|
|
import (
|
|
"RenewCMS/api/handlers/article"
|
|
"RenewCMS/api/handlers/auth"
|
|
"RenewCMS/api/handlers/frontend"
|
|
"RenewCMS/api/handlers/media"
|
|
"RenewCMS/api/handlers/settings"
|
|
"RenewCMS/api/handlers/staticimages"
|
|
"RenewCMS/api/handlers/user"
|
|
"RenewCMS/api/middleware"
|
|
"RenewCMS/internal/infrastructure/config"
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/MadAppGang/httplog"
|
|
"github.com/go-chi/chi/v5"
|
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
|
"github.com/go-chi/cors"
|
|
"github.com/go-chi/jwtauth/v5"
|
|
)
|
|
|
|
func enableLoggingIfDev(r *chi.Mux, routerName string, environment string) {
|
|
if environment == "development" {
|
|
r.Use(httplog.LoggerWithName(routerName))
|
|
}
|
|
}
|
|
|
|
func GetHelloWorld(w http.ResponseWriter, _ *http.Request) {
|
|
msg, _ := json.Marshal(map[string]string{"message": "Hello World"})
|
|
_, _ = w.Write(msg)
|
|
}
|
|
|
|
func NewArticleRouter(container *UseCases) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
articleHandler := article.NewHandler(container.CreateArticleUseCase, container.GetArticleUseCase, container.ListArticlesUseCase, container.DeleteArticleUseCase, container.UpdateArticleUseCase, container.GetUserUseCase)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Get("/", articleHandler.ListArticles)
|
|
r.Get("/{id}", articleHandler.GetArticle)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(jwtauth.Authenticator(container.Token))
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Post("/", articleHandler.PostArticle)
|
|
r.Delete("/{id}", articleHandler.DeleteArticle)
|
|
r.Patch("/{id}", articleHandler.UpdateArticleBody)
|
|
r.Patch("/{id}/publish", articleHandler.PublishArticle)
|
|
r.Patch("/{id}/unpublish", articleHandler.UnpublishArticle)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewAuthRouter(container *UseCases, isProduction bool, rateLimit config.AuthRateLimitSettings, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
loginHandler := auth.NewLoginHandler(container.Token, container.CreateUserUseCase, container.AuthenticateUserUseCase, isProduction)
|
|
verificationHandler := auth.NewVerificationHandler(container.VerifyUserUseCase, container.CancelRegistrationUseCase, isProduction)
|
|
meHandler := auth.NewMeHandler(container.GetUserUseCase)
|
|
passwordResetHandler := auth.NewPasswordResetHandler(container.RequestPasswordResetUseCase, container.ResetPasswordUseCase)
|
|
acceptInviteHandler := auth.NewAcceptInviteHandler(container.GetInviteUseCase, container.AcceptInviteUseCase, container.Token, isProduction)
|
|
|
|
r.Post("/register", loginHandler.Register)
|
|
r.Get("/registration", loginHandler.RegistrationStatus)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.TrustedProxyRealIP(trustedProxies))
|
|
r.Use(middleware.NewRateLimiter(rateLimit.MaxAttempts, rateLimit.Window))
|
|
r.Post("/login", loginHandler.Login)
|
|
})
|
|
r.Post("/logout", loginHandler.Logout)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.TrustedProxyRealIP(trustedProxies))
|
|
r.Use(middleware.NewRateLimiter(rateLimit.MaxAttempts, rateLimit.Window))
|
|
r.Post("/password-reset", passwordResetHandler.RequestPasswordReset)
|
|
})
|
|
r.Post("/password-reset/confirm", passwordResetHandler.ResetPassword)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.TrustedProxyRealIP(trustedProxies))
|
|
r.Use(middleware.NewRateLimiter(rateLimit.MaxAttempts, rateLimit.Window))
|
|
r.Get("/invite", acceptInviteHandler.GetInvite)
|
|
r.Post("/invite/accept", acceptInviteHandler.AcceptInvite)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(jwtauth.Verifier(container.Token))
|
|
r.Use(jwtauth.Authenticator(container.Token))
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Post("/verify", verificationHandler.Verify)
|
|
r.Delete("/register", verificationHandler.CancelRegistration)
|
|
r.Get("/me", meHandler.Me)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewUserRouter(container *UseCases, rateLimit config.AuthRateLimitSettings, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
userHandler := user.NewHandler(container.UpdateUserProfileUseCase)
|
|
inviteHandler := user.NewInviteHandler(container.CreateInviteUseCase, container.CancelInviteUseCase, container.RenewInviteUseCase, container.ListUsersAndInvitesUseCase)
|
|
|
|
r.Use(jwtauth.Verifier(container.Token))
|
|
r.Use(jwtauth.Authenticator(container.Token))
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Patch("/me", userHandler.UpdateMe)
|
|
r.Get("/", inviteHandler.ListUsers)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireOwner(container.GetUserUseCase))
|
|
r.Use(middleware.TrustedProxyRealIP(trustedProxies))
|
|
r.Use(middleware.NewRateLimiter(rateLimit.MaxAttempts, rateLimit.Window))
|
|
r.Post("/invite", inviteHandler.CreateInvite)
|
|
r.Delete("/invite/{id}", inviteHandler.CancelInvite)
|
|
r.Post("/invite/{id}/renew", inviteHandler.RenewInvite)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewSettingsRouter(container *UseCases) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
settingsHandler := settings.NewHandler(container.GetSettingsUseCase)
|
|
|
|
r.Use(jwtauth.Verifier(container.Token))
|
|
r.Use(jwtauth.Authenticator(container.Token))
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Get("/", settingsHandler.GetSettings)
|
|
return r
|
|
}
|
|
|
|
func NewMediaRouter(container *UseCases) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
mediaHandler := media.NewHandler(container.CreateImageUseCase, container.ListMediaUseCase, container.RenameMediaUseCase, container.DeleteMediaUseCase, container.MergeMediaUseCase, container.GetMediaUsageUseCase)
|
|
|
|
r.Use(jwtauth.Verifier(container.Token))
|
|
r.Use(jwtauth.Authenticator(container.Token))
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Get("/", mediaHandler.List)
|
|
r.Post("/", mediaHandler.Upload)
|
|
r.Post("/merge", mediaHandler.Merge)
|
|
r.Patch("/{id}", mediaHandler.Rename)
|
|
r.Delete("/{id}", mediaHandler.Delete)
|
|
r.Get("/{id}/usage", mediaHandler.Usage)
|
|
return r
|
|
}
|
|
|
|
func InitBackendRoutes(container *UseCases, settings config.Settings) *chi.Mux {
|
|
r := chi.NewRouter()
|
|
|
|
enableLoggingIfDev(r, "backend", settings.Environment)
|
|
|
|
r.Use(chimiddleware.GetHead)
|
|
r.Use(middleware.JsonContentTypeMiddleware)
|
|
r.Get("/", GetHelloWorld)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(jwtauth.Verifier(container.Token))
|
|
r.Mount("/article", NewArticleRouter(container))
|
|
})
|
|
r.Mount("/user", NewUserRouter(container, settings.AuthRateLimit, settings.TrustedProxies))
|
|
r.Mount("/settings", NewSettingsRouter(container))
|
|
r.Mount("/media", NewMediaRouter(container))
|
|
r.Mount("/auth", NewAuthRouter(container, settings.IsProduction(), settings.AuthRateLimit, settings.TrustedProxies))
|
|
|
|
return r
|
|
}
|
|
|
|
func InitFrontendRoutes(environment string) *chi.Mux {
|
|
r := chi.NewRouter()
|
|
|
|
enableLoggingIfDev(r, "frontend", environment)
|
|
|
|
r.Mount("/", frontend.NewFrontendRouter())
|
|
|
|
return r
|
|
}
|
|
|
|
func GetHealth(w http.ResponseWriter, _ *http.Request) {
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
_, _ = w.Write([]byte("ok"))
|
|
}
|
|
|
|
func enableCorsIfConfigured(r *chi.Mux, origins []string) {
|
|
if len(origins) == 0 {
|
|
return
|
|
}
|
|
r.Use(cors.Handler(cors.Options{
|
|
AllowedOrigins: origins,
|
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
|
|
ExposedHeaders: []string{"Link"},
|
|
AllowCredentials: false,
|
|
MaxAge: 300,
|
|
}))
|
|
}
|
|
|
|
func InitRoutes(container *UseCases, settings config.Settings) *chi.Mux {
|
|
backend := InitBackendRoutes(container, settings)
|
|
frontend := InitFrontendRoutes(settings.Environment)
|
|
|
|
apiRouter := chi.NewRouter()
|
|
enableCorsIfConfigured(apiRouter, settings.CorsAllowedOrigins)
|
|
apiRouter.Use(chimiddleware.GetHead)
|
|
apiRouter.Get("/healthz", GetHealth)
|
|
apiRouter.Mount("/v1", backend)
|
|
apiRouter.Mount("/static/uploadedImages", staticimages.NewHandler(settings.UploadDir))
|
|
apiRouter.Mount("/", frontend)
|
|
|
|
return apiRouter
|
|
}
|