2022-07-06 23:19:05 +02:00
|
|
|
package apis
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"github.com/labstack/echo/v5"
|
|
|
|
"github.com/pocketbase/pocketbase/core"
|
|
|
|
"github.com/pocketbase/pocketbase/forms"
|
|
|
|
"github.com/pocketbase/pocketbase/models"
|
|
|
|
"github.com/pocketbase/pocketbase/tokens"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/rest"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/routine"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/search"
|
|
|
|
)
|
|
|
|
|
|
|
|
// BindAdminApi registers the admin api endpoints and the corresponding handlers.
|
|
|
|
func BindAdminApi(app core.App, rg *echo.Group) {
|
|
|
|
api := adminApi{app: app}
|
|
|
|
|
|
|
|
subGroup := rg.Group("/admins", ActivityLogger(app))
|
|
|
|
subGroup.POST("/auth-via-email", api.emailAuth, RequireGuestOnly())
|
|
|
|
subGroup.POST("/request-password-reset", api.requestPasswordReset)
|
|
|
|
subGroup.POST("/confirm-password-reset", api.confirmPasswordReset)
|
|
|
|
subGroup.POST("/refresh", api.refresh, RequireAdminAuth())
|
|
|
|
subGroup.GET("", api.list, RequireAdminAuth())
|
2022-07-10 10:46:21 +02:00
|
|
|
subGroup.POST("", api.create, RequireAdminAuthOnlyIfAny(app))
|
2022-07-06 23:19:05 +02:00
|
|
|
subGroup.GET("/:id", api.view, RequireAdminAuth())
|
|
|
|
subGroup.PATCH("/:id", api.update, RequireAdminAuth())
|
|
|
|
subGroup.DELETE("/:id", api.delete, RequireAdminAuth())
|
|
|
|
}
|
|
|
|
|
|
|
|
type adminApi struct {
|
|
|
|
app core.App
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) authResponse(c echo.Context, admin *models.Admin) error {
|
|
|
|
token, tokenErr := tokens.NewAdminAuthToken(api.app, admin)
|
|
|
|
if tokenErr != nil {
|
|
|
|
return rest.NewBadRequestError("Failed to create auth token.", tokenErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
event := &core.AdminAuthEvent{
|
|
|
|
HttpContext: c,
|
|
|
|
Admin: admin,
|
|
|
|
Token: token,
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.app.OnAdminAuthRequest().Trigger(event, func(e *core.AdminAuthEvent) error {
|
|
|
|
return e.HttpContext.JSON(200, map[string]any{
|
|
|
|
"token": e.Token,
|
|
|
|
"admin": e.Admin,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) refresh(c echo.Context) error {
|
|
|
|
admin, _ := c.Get(ContextAdminKey).(*models.Admin)
|
|
|
|
if admin == nil {
|
|
|
|
return rest.NewNotFoundError("Missing auth admin context.", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.authResponse(c, admin)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) emailAuth(c echo.Context) error {
|
|
|
|
form := forms.NewAdminLogin(api.app)
|
|
|
|
if readErr := c.Bind(form); readErr != nil {
|
2022-07-09 16:17:41 +02:00
|
|
|
return rest.NewBadRequestError("An error occurred while reading the submitted data.", readErr)
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
admin, submitErr := form.Submit()
|
|
|
|
if submitErr != nil {
|
|
|
|
return rest.NewBadRequestError("Failed to authenticate.", submitErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.authResponse(c, admin)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) requestPasswordReset(c echo.Context) error {
|
|
|
|
form := forms.NewAdminPasswordResetRequest(api.app)
|
|
|
|
if err := c.Bind(form); err != nil {
|
2022-07-09 16:17:41 +02:00
|
|
|
return rest.NewBadRequestError("An error occurred while reading the submitted data.", err)
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := form.Validate(); err != nil {
|
2022-07-09 16:17:41 +02:00
|
|
|
return rest.NewBadRequestError("An error occurred while validating the form.", err)
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// run in background because we don't need to show the result
|
|
|
|
// (prevents admins enumeration)
|
|
|
|
routine.FireAndForget(func() {
|
|
|
|
if err := form.Submit(); err != nil && api.app.IsDebug() {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return c.NoContent(http.StatusNoContent)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) confirmPasswordReset(c echo.Context) error {
|
|
|
|
form := forms.NewAdminPasswordResetConfirm(api.app)
|
|
|
|
if readErr := c.Bind(form); readErr != nil {
|
2022-07-09 16:17:41 +02:00
|
|
|
return rest.NewBadRequestError("An error occurred while reading the submitted data.", readErr)
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
admin, submitErr := form.Submit()
|
|
|
|
if submitErr != nil {
|
|
|
|
return rest.NewBadRequestError("Failed to set new password.", submitErr)
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.authResponse(c, admin)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) list(c echo.Context) error {
|
|
|
|
fieldResolver := search.NewSimpleFieldResolver(
|
|
|
|
"id", "created", "updated", "name", "email",
|
|
|
|
)
|
|
|
|
|
|
|
|
admins := []*models.Admin{}
|
|
|
|
|
|
|
|
result, err := search.NewProvider(fieldResolver).
|
|
|
|
Query(api.app.Dao().AdminQuery()).
|
|
|
|
ParseAndExec(c.QueryString(), &admins)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return rest.NewBadRequestError("", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
event := &core.AdminsListEvent{
|
|
|
|
HttpContext: c,
|
|
|
|
Admins: admins,
|
|
|
|
Result: result,
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.app.OnAdminsListRequest().Trigger(event, func(e *core.AdminsListEvent) error {
|
|
|
|
return e.HttpContext.JSON(http.StatusOK, e.Result)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) view(c echo.Context) error {
|
|
|
|
id := c.PathParam("id")
|
|
|
|
if id == "" {
|
|
|
|
return rest.NewNotFoundError("", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
admin, err := api.app.Dao().FindAdminById(id)
|
|
|
|
if err != nil || admin == nil {
|
|
|
|
return rest.NewNotFoundError("", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
event := &core.AdminViewEvent{
|
|
|
|
HttpContext: c,
|
|
|
|
Admin: admin,
|
|
|
|
}
|
|
|
|
|
|
|
|
return api.app.OnAdminViewRequest().Trigger(event, func(e *core.AdminViewEvent) error {
|
|
|
|
return e.HttpContext.JSON(http.StatusOK, e.Admin)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) create(c echo.Context) error {
|
|
|
|
admin := &models.Admin{}
|
|
|
|
|
|
|
|
form := forms.NewAdminUpsert(api.app, admin)
|
|
|
|
|
|
|
|
// load request
|
|
|
|
if err := c.Bind(form); err != nil {
|
2022-07-12 12:42:06 +02:00
|
|
|
return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err)
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
event := &core.AdminCreateEvent{
|
|
|
|
HttpContext: c,
|
|
|
|
Admin: admin,
|
|
|
|
}
|
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
// create the admin
|
|
|
|
submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc {
|
|
|
|
return func() error {
|
|
|
|
return api.app.OnAdminBeforeCreateRequest().Trigger(event, func(e *core.AdminCreateEvent) error {
|
|
|
|
if err := next(); err != nil {
|
|
|
|
return rest.NewBadRequestError("Failed to create admin.", err)
|
|
|
|
}
|
2022-07-06 23:19:05 +02:00
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
return e.HttpContext.JSON(http.StatusOK, e.Admin)
|
|
|
|
})
|
|
|
|
}
|
2022-07-06 23:19:05 +02:00
|
|
|
})
|
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
if submitErr == nil {
|
2022-07-06 23:19:05 +02:00
|
|
|
api.app.OnAdminAfterCreateRequest().Trigger(event)
|
|
|
|
}
|
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
return submitErr
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) update(c echo.Context) error {
|
|
|
|
id := c.PathParam("id")
|
|
|
|
if id == "" {
|
|
|
|
return rest.NewNotFoundError("", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
admin, err := api.app.Dao().FindAdminById(id)
|
|
|
|
if err != nil || admin == nil {
|
|
|
|
return rest.NewNotFoundError("", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
form := forms.NewAdminUpsert(api.app, admin)
|
|
|
|
|
|
|
|
// load request
|
|
|
|
if err := c.Bind(form); err != nil {
|
2022-07-12 12:42:06 +02:00
|
|
|
return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err)
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
event := &core.AdminUpdateEvent{
|
|
|
|
HttpContext: c,
|
|
|
|
Admin: admin,
|
|
|
|
}
|
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
// update the admin
|
|
|
|
submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc {
|
|
|
|
return func() error {
|
|
|
|
return api.app.OnAdminBeforeUpdateRequest().Trigger(event, func(e *core.AdminUpdateEvent) error {
|
|
|
|
if err := next(); err != nil {
|
|
|
|
return rest.NewBadRequestError("Failed to update admin.", err)
|
|
|
|
}
|
2022-07-06 23:19:05 +02:00
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
return e.HttpContext.JSON(http.StatusOK, e.Admin)
|
|
|
|
})
|
|
|
|
}
|
2022-07-06 23:19:05 +02:00
|
|
|
})
|
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
if submitErr == nil {
|
2022-07-06 23:19:05 +02:00
|
|
|
api.app.OnAdminAfterUpdateRequest().Trigger(event)
|
|
|
|
}
|
|
|
|
|
2022-07-12 12:42:06 +02:00
|
|
|
return submitErr
|
2022-07-06 23:19:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (api *adminApi) delete(c echo.Context) error {
|
|
|
|
id := c.PathParam("id")
|
|
|
|
if id == "" {
|
|
|
|
return rest.NewNotFoundError("", nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
admin, err := api.app.Dao().FindAdminById(id)
|
|
|
|
if err != nil || admin == nil {
|
|
|
|
return rest.NewNotFoundError("", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
event := &core.AdminDeleteEvent{
|
|
|
|
HttpContext: c,
|
|
|
|
Admin: admin,
|
|
|
|
}
|
|
|
|
|
|
|
|
handlerErr := api.app.OnAdminBeforeDeleteRequest().Trigger(event, func(e *core.AdminDeleteEvent) error {
|
|
|
|
if err := api.app.Dao().DeleteAdmin(e.Admin); err != nil {
|
|
|
|
return rest.NewBadRequestError("Failed to delete admin.", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return e.HttpContext.NoContent(http.StatusNoContent)
|
|
|
|
})
|
|
|
|
|
|
|
|
if handlerErr == nil {
|
|
|
|
api.app.OnAdminAfterDeleteRequest().Trigger(event)
|
|
|
|
}
|
|
|
|
|
|
|
|
return handlerErr
|
|
|
|
}
|