583 lines
26 KiB
Go
583 lines
26 KiB
Go
package api
|
|
|
|
import (
|
|
"RenewCMS/api/handlers/activity"
|
|
"RenewCMS/api/handlers/apikey"
|
|
"RenewCMS/api/handlers/article"
|
|
"RenewCMS/api/handlers/auth"
|
|
"RenewCMS/api/handlers/author"
|
|
"RenewCMS/api/handlers/builtintype"
|
|
"RenewCMS/api/handlers/contenttype"
|
|
"RenewCMS/api/handlers/entry"
|
|
"RenewCMS/api/handlers/frontend"
|
|
"RenewCMS/api/handlers/imagevariant"
|
|
"RenewCMS/api/handlers/llms"
|
|
"RenewCMS/api/handlers/media"
|
|
"RenewCMS/api/handlers/ownership"
|
|
"RenewCMS/api/handlers/permission"
|
|
rolehandler "RenewCMS/api/handlers/role"
|
|
"RenewCMS/api/handlers/settings"
|
|
"RenewCMS/api/handlers/site"
|
|
"RenewCMS/api/handlers/staticimages"
|
|
"RenewCMS/api/handlers/update"
|
|
"RenewCMS/api/handlers/user"
|
|
"RenewCMS/api/middleware"
|
|
"RenewCMS/api/pkg/authtoken"
|
|
"RenewCMS/internal/domain/role"
|
|
"RenewCMS/internal/infrastructure/config"
|
|
"RenewCMS/internal/infrastructure/llmdocs"
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
|
)
|
|
|
|
func enableLoggingIfDev(r *chi.Mux, routerName string, environment string) {
|
|
if environment == "development" {
|
|
r.Use(chimiddleware.RequestLogger(&chimiddleware.DefaultLogFormatter{
|
|
Logger: log.New(os.Stdout, "["+routerName+"] ", log.LstdFlags),
|
|
}))
|
|
}
|
|
}
|
|
|
|
func GetHelloWorld(w http.ResponseWriter, _ *http.Request) {
|
|
msg, _ := json.Marshal(map[string]string{"message": "Hello World"})
|
|
_, _ = w.Write(msg)
|
|
}
|
|
|
|
const (
|
|
apiKeyMaxRequests = 120
|
|
apiKeyWindow = time.Minute
|
|
)
|
|
|
|
func (c *UseCases) apiKeyAuth() func(http.Handler) http.Handler {
|
|
c.apiKeyAuthOnce.Do(func() {
|
|
c.apiKeyAuthMiddleware = middleware.APIKeyAuth(c.AuthenticateAPIKeyUseCase, apiKeyMaxRequests, apiKeyWindow)
|
|
})
|
|
return c.apiKeyAuthMiddleware
|
|
}
|
|
|
|
func (c *UseCases) require(key string) func(http.Handler) http.Handler {
|
|
return middleware.RequirePermission(c.GetUserUseCase, c.CheckPermissionUseCase, key)
|
|
}
|
|
|
|
func (c *UseCases) requireTyped(key string, typeKey func(*http.Request) string) func(http.Handler) http.Handler {
|
|
return middleware.RequireTypedPermission(c.GetUserUseCase, c.CheckPermissionUseCase, key, typeKey)
|
|
}
|
|
|
|
func (c *UseCases) requireArticle(key string) func(http.Handler) http.Handler {
|
|
return c.requireTyped(key, middleware.ArticleTypeKey)
|
|
}
|
|
|
|
func (c *UseCases) requireEntry(key string) func(http.Handler) http.Handler {
|
|
return c.requireTyped(key, middleware.PathTypeKey)
|
|
}
|
|
|
|
func (c *UseCases) requireAll(key string) func(http.Handler) http.Handler {
|
|
return middleware.RequireFullScope(c.GetUserUseCase, c.CheckPermissionUseCase, key)
|
|
}
|
|
|
|
func NewArticleRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
|
|
articleHandler := article.NewHandler(container.CreateArticleUseCase, container.GetArticleUseCase, container.ListArticlesUseCase, container.DeleteArticleUseCase, container.UpdateArticleUseCase)
|
|
|
|
seoHandler := article.NewSEOHandler(container.ResolveArticleSEOUseCase, container.CheckArticleSEOUseCase)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.OptionalAccess(container.GetUserUseCase, container.CheckPermissionUseCase))
|
|
r.Get("/", articleHandler.ListArticles)
|
|
r.Get("/{id}", articleHandler.GetArticle)
|
|
r.Get("/{id}/seo", seoHandler.Resolve)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.requireArticle(role.KeyContentCreate)).Post("/", articleHandler.PostArticle)
|
|
r.With(container.requireArticle(role.KeyContentDelete)).Delete("/{id}", articleHandler.DeleteArticle)
|
|
r.With(container.requireArticle(role.KeyContentUpdate)).Patch("/{id}", articleHandler.UpdateArticleBody)
|
|
r.With(container.requireArticle(role.KeyContentPublish)).Patch("/{id}/publish", articleHandler.PublishArticle)
|
|
r.With(container.requireArticle(role.KeyContentPublish)).Patch("/{id}/unpublish", articleHandler.UnpublishArticle)
|
|
r.With(container.requireArticle(role.KeyContentRead)).Post("/{id}/seo/check", seoHandler.Check)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewAuthRouter(container *UseCases, isProduction bool, rateLimit config.AuthRateLimitSettings, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
|
|
loginHandler := auth.NewLoginHandler(container.Token, container.CreateUserUseCase, container.AuthenticateUserUseCase, isProduction)
|
|
verificationHandler := auth.NewVerificationHandler(container.VerifyUserUseCase, container.CancelRegistrationUseCase, isProduction)
|
|
meHandler := auth.NewMeHandler(container.GetUserUseCase, container.GetMyAuthorUseCase, container.GetMyAccessUseCase)
|
|
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(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Post("/verify", verificationHandler.Verify)
|
|
r.Delete("/register", verificationHandler.CancelRegistration)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
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)
|
|
lookupHandler := user.NewLookupHandler(container.LookupUserNamesUseCase)
|
|
inviteHandler := user.NewInviteHandler(container.CreateInviteUseCase, container.CancelInviteUseCase, container.RenewInviteUseCase, container.ListUsersAndInvitesUseCase)
|
|
roleHandler := user.NewRoleHandler(container.ChangeUserRoleUseCase)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Patch("/me", userHandler.UpdateMe)
|
|
r.With(container.require(role.KeyUsersView)).Get("/", inviteHandler.ListUsers)
|
|
r.Get("/lookup", lookupHandler.Lookup)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(container.require(role.KeyUsersInvite))
|
|
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)
|
|
})
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Use(container.require(role.KeyUsersChangeRole))
|
|
r.Put("/{id}/role", roleHandler.Change)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewSettingsRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
settingsHandler := settings.NewHandler(container.GetSettingsUseCase, container.GetActivityRetentionUseCase, container.UpdateActivityRetentionUseCase)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Use(container.require(role.KeySettingsView))
|
|
r.Get("/", settingsHandler.GetSettings)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Use(container.require(role.KeySystemEditRetention))
|
|
r.Patch("/", settingsHandler.UpdateSettings)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewSiteRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
siteHandler := site.NewHandler(container.ListSitesUseCase, container.CreateSiteUseCase, container.UpdateSiteUseCase, container.SetDefaultSiteUseCase, container.DeleteSiteUseCase)
|
|
|
|
sitemapHandler := site.NewSitemapHandler(container.ListSitemapUseCase)
|
|
|
|
ownershipHandler := ownership.NewHandler(container.TransferOwnershipUseCase)
|
|
|
|
r.Get("/{key}/sitemap", sitemapHandler.Sitemap)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Get("/", siteHandler.List)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.require(role.KeySettingsManageSites)).Post("/", siteHandler.Create)
|
|
r.With(container.require(role.KeySettingsManageSites)).Patch("/{id}", siteHandler.Update)
|
|
r.With(container.require(role.KeySettingsManageSites)).Patch("/{id}/default", siteHandler.SetDefault)
|
|
r.With(container.require(role.KeySettingsDeleteSite)).Delete("/{id}", siteHandler.Delete)
|
|
r.With(container.require(role.KeySystemTransferOwnership)).Post("/transfer-ownership", ownershipHandler.Transfer)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewRoleRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
roleHandler := rolehandler.NewHandler(container.ListRolesUseCase, container.GetRoleUseCase, container.CreateRoleUseCase, container.UpdateRoleUseCase, container.DeleteRoleUseCase, container.ReassignRoleMembersUseCase)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Use(container.require(role.KeyRolesView))
|
|
r.Get("/", roleHandler.List)
|
|
r.Get("/{id}", roleHandler.Get)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Use(container.require(role.KeyRolesManage))
|
|
r.Post("/", roleHandler.Create)
|
|
r.Put("/{id}", roleHandler.Update)
|
|
r.Delete("/{id}", roleHandler.Delete)
|
|
r.Post("/{id}/reassign", roleHandler.Reassign)
|
|
r.Post("/{id}/reset", roleHandler.Reset)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewAuthorRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
authorHandler := author.NewHandler(container.ListAuthorsUseCase, container.CreateAuthorUseCase, container.CreateMyAuthorUseCase, container.UpdateAuthorUseCase, container.DeleteAuthorUseCase)
|
|
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Use(container.require(role.KeyAuthorsView))
|
|
r.Get("/", authorHandler.List)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Post("/me", authorHandler.CreateMine)
|
|
r.With(container.require(role.KeyAuthorsEdit)).Patch("/{id}", authorHandler.Update)
|
|
r.With(container.require(role.KeyAuthorsCreate)).Post("/", authorHandler.Create)
|
|
r.With(container.require(role.KeyAuthorsDelete)).Delete("/{id}", authorHandler.Delete)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewContentTypeRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
contentTypeHandler := contenttype.NewHandler(container.CreateContentTypeUseCase, container.ListContentTypesUseCase, container.GetContentTypeUseCase, container.UpdateContentTypeUseCase, container.DeleteContentTypeUseCase)
|
|
|
|
entryHandler := entry.NewHandler(container.CreateEntryUseCase, container.GetEntryUseCase, container.ListEntriesUseCase, container.UpdateEntryUseCase, container.DeleteEntryUseCase)
|
|
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.require(role.KeySchemaView)).Get("/", contentTypeHandler.List)
|
|
r.With(container.require(role.KeySchemaView)).Get("/{key}", contentTypeHandler.Get)
|
|
r.With(container.require(role.KeySchemaEdit)).Post("/", contentTypeHandler.Create)
|
|
r.With(container.require(role.KeySchemaEdit)).Put("/{key}", contentTypeHandler.Update)
|
|
r.With(container.require(role.KeySchemaDelete)).Delete("/{key}", contentTypeHandler.Delete)
|
|
r.With(container.requireEntry(role.KeyContentRead)).Get("/{key}/entry", entryHandler.List)
|
|
r.With(container.requireEntry(role.KeyContentCreate)).Post("/{key}/entry", entryHandler.Create)
|
|
r.With(container.requireEntry(role.KeyContentRead)).Get("/{key}/entry/{id}", entryHandler.Get)
|
|
r.With(container.requireEntry(role.KeyContentUpdate)).Put("/{key}/entry/{id}", entryHandler.Update)
|
|
r.With(container.requireEntry(role.KeyContentDelete)).Delete("/{key}/entry/{id}", entryHandler.Delete)
|
|
return r
|
|
}
|
|
|
|
func NewBuiltinTypeRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
builtinTypeHandler := builtintype.NewHandler(container.ListBuiltinTypesUseCase, container.GetBuiltinTypeUseCase)
|
|
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Use(container.require(role.KeySchemaView))
|
|
r.Get("/", builtinTypeHandler.List)
|
|
r.Get("/{key}", builtinTypeHandler.Get)
|
|
return r
|
|
}
|
|
|
|
func NewActivityRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
activityHandler := activity.NewHandler(container.ListActivityUseCase, container.GetActivitySummaryUseCase, container.ExportActivityUseCase)
|
|
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.require(role.KeyActivityView)).Get("/", activityHandler.List)
|
|
r.With(container.requireAll(role.KeyActivityView)).Get("/summary", activityHandler.Summary)
|
|
r.With(container.require(role.KeyActivityExport), container.require(role.KeyActivityView)).Get("/export", activityHandler.Export)
|
|
return r
|
|
}
|
|
|
|
const (
|
|
updateCheckMaxAttempts = 5
|
|
updateCheckWindow = time.Minute
|
|
updateDownloadMaxAttempts = 5
|
|
updateDownloadWindow = time.Minute
|
|
)
|
|
|
|
func NewSystemRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
updateHandler := update.NewHandler(container.GetUpdateStatusUseCase, container.CheckForUpdateUseCase, container.StartUpdateDownloadUseCase, container.RequestRestartUseCase)
|
|
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.require(role.KeySettingsView)).Get("/update", updateHandler.GetStatus)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(container.require(role.KeySystemApplyUpdates))
|
|
r.Use(middleware.NewUserRateLimiter(updateCheckMaxAttempts, updateCheckWindow))
|
|
r.Post("/update/check", updateHandler.Check)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(container.require(role.KeySystemApplyUpdates))
|
|
r.Use(middleware.NewUserRateLimiter(updateDownloadMaxAttempts, updateDownloadWindow))
|
|
r.Post("/update/download", updateHandler.Download)
|
|
})
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(container.require(role.KeySystemApplyUpdates))
|
|
r.Post("/update/restart", updateHandler.Restart)
|
|
})
|
|
return r
|
|
}
|
|
|
|
func NewAPIKeyRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
apiKeyHandler := apikey.NewHandler(container.CreateAPIKeyUseCase, container.ListAPIKeysUseCase, container.RevokeAPIKeyUseCase)
|
|
|
|
r.Use(middleware.RequireSession)
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.require(role.KeyAPIKeysCreate)).Post("/", apiKeyHandler.Create)
|
|
r.With(container.require(role.KeyAPIKeysView)).Get("/", apiKeyHandler.List)
|
|
r.With(container.require(role.KeyAPIKeysRevoke)).Delete("/{id}", apiKeyHandler.Revoke)
|
|
return r
|
|
}
|
|
|
|
func NewPermissionRouter(container *UseCases) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
permissionHandler := permission.NewHandler(container.ListPermissionsUseCase)
|
|
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.Get("/", permissionHandler.List)
|
|
return r
|
|
}
|
|
|
|
func NewMediaRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
mediaHandler := media.NewHandler(container.CreateImageUseCase, container.ListMediaUseCase, container.RenameMediaUseCase, container.DeleteMediaUseCase, container.MergeMediaUseCase, container.GetMediaUsageUseCase, container.GetPurgeSettingsUseCase, container.UpdatePurgeSettingsUseCase, container.PreviewPurgeUseCase, container.RunPurgeUseCase, container.ListImageVariantsUseCase, container.GenerateImageVariantUseCase, container.GenerateAllImageVariantsUseCase, container.PurgeImageVariantUseCase, container.GenerateVariantsOnUploadUseCase)
|
|
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(container.apiKeyAuth())
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.require(role.KeySettingsView)).Get("/purge-settings", mediaHandler.GetPurgeSettings)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Patch("/purge-settings", mediaHandler.UpdatePurgeSettings)
|
|
r.With(container.requireAll(role.KeyMediaDelete)).Get("/purge-preview", mediaHandler.PreviewPurge)
|
|
r.With(container.requireAll(role.KeyMediaDelete)).Post("/purge", mediaHandler.RunPurge)
|
|
r.Group(func(r chi.Router) {
|
|
r.Use(middleware.RejectAPIKey)
|
|
r.With(container.require(role.KeyMediaView)).Get("/{id}/variants", mediaHandler.ListVariants)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Post("/{id}/variants", mediaHandler.GenerateAllVariants)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Post("/{id}/variants/{name}", mediaHandler.GenerateVariant)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Delete("/{id}/variants/{name}", mediaHandler.PurgeVariant)
|
|
})
|
|
r.With(container.require(role.KeyMediaView)).Get("/", mediaHandler.List)
|
|
r.With(container.require(role.KeyMediaUpload)).Post("/", mediaHandler.Upload)
|
|
r.With(container.requireAll(role.KeyMediaDelete)).Post("/merge", mediaHandler.Merge)
|
|
r.With(container.require(role.KeyMediaUpload)).Patch("/{id}", mediaHandler.Rename)
|
|
r.With(container.requireAll(role.KeyMediaDelete)).Delete("/{id}", mediaHandler.Delete)
|
|
r.With(container.require(role.KeyMediaView)).Get("/{id}", mediaHandler.Get)
|
|
r.With(container.require(role.KeyMediaView)).Get("/{id}/usage", mediaHandler.Usage)
|
|
return r
|
|
}
|
|
|
|
func NewImageVariantRouter(container *UseCases, trustedProxies []string) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
variantHandler := imagevariant.NewHandler(container.ListVariantsUseCase, container.CreateVariantUseCase, container.UpdateVariantUseCase, container.SetVariantEnabledUseCase, container.DeleteVariantUseCase, container.PurgeVariantUseCase, container.GetVariantCacheStatsUseCase, container.PurgeVariantCacheUseCase)
|
|
|
|
r.Use(authtoken.Verifier(container.Token))
|
|
r.Use(middleware.AuditContext(container.GetUserUseCase, trustedProxies))
|
|
r.Use(middleware.RequireSignedIn)
|
|
r.Use(authtoken.Authenticator)
|
|
r.Use(middleware.RequireCurrentToken(container.GetUserUseCase))
|
|
r.Use(middleware.RequireVerifiedUser(container.GetUserUseCase))
|
|
r.With(container.require(role.KeyMediaView)).Get("/", variantHandler.List)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Post("/", variantHandler.Create)
|
|
r.With(container.require(role.KeySettingsView)).Get("/cache", variantHandler.CacheStats)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Post("/cache/purge", variantHandler.PurgeCache)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Put("/{name}", variantHandler.Update)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Patch("/{name}", variantHandler.SetEnabled)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Delete("/{name}", variantHandler.Delete)
|
|
r.With(container.require(role.KeySettingsEditGeneral)).Post("/{name}/purge", variantHandler.Purge)
|
|
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(authtoken.Verifier(container.Token))
|
|
r.Mount("/article", NewArticleRouter(container, settings.TrustedProxies))
|
|
})
|
|
r.Mount("/user", NewUserRouter(container, settings.AuthRateLimit, settings.TrustedProxies))
|
|
r.Mount("/settings", NewSettingsRouter(container, settings.TrustedProxies))
|
|
r.Mount("/site", NewSiteRouter(container, settings.TrustedProxies))
|
|
r.Mount("/role", NewRoleRouter(container, settings.TrustedProxies))
|
|
r.Mount("/author", NewAuthorRouter(container, settings.TrustedProxies))
|
|
r.Mount("/content-type", NewContentTypeRouter(container, settings.TrustedProxies))
|
|
r.Mount("/builtin-type", NewBuiltinTypeRouter(container, settings.TrustedProxies))
|
|
r.Mount("/activity", NewActivityRouter(container, settings.TrustedProxies))
|
|
r.Mount("/media", NewMediaRouter(container, settings.TrustedProxies))
|
|
r.Mount("/image-variants", NewImageVariantRouter(container, settings.TrustedProxies))
|
|
r.Mount("/api-key", NewAPIKeyRouter(container, settings.TrustedProxies))
|
|
r.Mount("/permission", NewPermissionRouter(container))
|
|
r.Mount("/system", NewSystemRouter(container, settings.TrustedProxies))
|
|
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(middleware.CORS(origins))
|
|
}
|
|
|
|
func InitRoutes(container *UseCases, settings config.Settings) *chi.Mux {
|
|
backend := InitBackendRoutes(container, settings)
|
|
frontend := InitFrontendRoutes(settings.Environment)
|
|
|
|
apiRouter := chi.NewRouter()
|
|
apiRouter.Use(middleware.SecurityHeaders(settings.IsProduction()))
|
|
enableCorsIfConfigured(apiRouter, settings.CorsAllowedOrigins)
|
|
apiRouter.Use(middleware.CrossOriginProtection(settings.PublicURL))
|
|
apiRouter.Use(chimiddleware.GetHead)
|
|
apiRouter.Get("/healthz", GetHealth)
|
|
llms.NewHandler(llmdocs.Docs, settings.Version, settings.PublicURL).Register(apiRouter)
|
|
apiRouter.Mount("/v1", backend)
|
|
apiRouter.Mount("/static/uploadedImages", staticimages.NewHandler(container.OpenMediaUseCase, container.ServeVariantUseCase))
|
|
apiRouter.Mount("/", frontend)
|
|
|
|
return apiRouter
|
|
}
|