mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-03-20 06:21:06 +02:00
139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
|
package apis
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/pocketbase/pocketbase/core"
|
||
|
"github.com/pocketbase/pocketbase/tools/hook"
|
||
|
"github.com/pocketbase/pocketbase/tools/router"
|
||
|
)
|
||
|
|
||
|
const installerParam = "pbinstal"
|
||
|
|
||
|
var wildcardPlaceholderRegex = regexp.MustCompile(`/{.+\.\.\.}$`)
|
||
|
|
||
|
func stripWildcard(pattern string) string {
|
||
|
return wildcardPlaceholderRegex.ReplaceAllString(pattern, "")
|
||
|
}
|
||
|
|
||
|
// installerRedirect redirects the user to the installer dashboard UI page
|
||
|
// when the application needs some preliminary configurations to be done.
|
||
|
func installerRedirect(app core.App, cpPath string) hook.HandlerFunc[*core.RequestEvent] {
|
||
|
// note: to avoid locks contention it is not concurrent safe but it
|
||
|
// is expected to be updated only once during initialization
|
||
|
var hasSuperuser bool
|
||
|
|
||
|
// strip named wildcard
|
||
|
cpPath = stripWildcard(cpPath)
|
||
|
|
||
|
updateHasSuperuser := func(app core.App) error {
|
||
|
total, err := app.CountRecords(core.CollectionNameSuperusers)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
hasSuperuser = total > 0
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// load initial state on app init
|
||
|
app.OnBootstrap().BindFunc(func(e *core.BootstrapEvent) error {
|
||
|
err := e.Next()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
err = updateHasSuperuser(e.App)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("failed to check for existing superuser: %w", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
// update on superuser create
|
||
|
app.OnRecordCreateRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
|
||
|
err := e.Next()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if !hasSuperuser {
|
||
|
hasSuperuser = true
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
return func(e *core.RequestEvent) error {
|
||
|
if hasSuperuser {
|
||
|
return e.Next()
|
||
|
}
|
||
|
|
||
|
isAPI := strings.HasPrefix(e.Request.URL.Path, "/api/")
|
||
|
isControlPanel := strings.HasPrefix(e.Request.URL.Path, cpPath)
|
||
|
wildcard := e.Request.PathValue(StaticWildcardParam)
|
||
|
|
||
|
// skip redirect checks for API and non-root level dashboard index.html requests (css, images, etc.)
|
||
|
if isAPI || (isControlPanel && wildcard != "" && wildcard != router.IndexPage) {
|
||
|
return e.Next()
|
||
|
}
|
||
|
|
||
|
// check again in case the superuser was created by some other process
|
||
|
if err := updateHasSuperuser(e.App); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if hasSuperuser {
|
||
|
return e.Next()
|
||
|
}
|
||
|
|
||
|
_, hasInstallerParam := e.Request.URL.Query()[installerParam]
|
||
|
|
||
|
// redirect to the installer page
|
||
|
if !hasInstallerParam {
|
||
|
return e.Redirect(http.StatusTemporaryRedirect, cpPath+"?"+installerParam+"#")
|
||
|
}
|
||
|
|
||
|
return e.Next()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// dashboardRemoveInstallerParam redirects to a non-installer
|
||
|
// query param in case there is already a superuser created.
|
||
|
//
|
||
|
// Note: intended to be registered only for the dashboard route
|
||
|
// to prevent excessive checks for every other route in installerRedirect.
|
||
|
func dashboardRemoveInstallerParam() hook.HandlerFunc[*core.RequestEvent] {
|
||
|
return func(e *core.RequestEvent) error {
|
||
|
_, hasInstallerParam := e.Request.URL.Query()[installerParam]
|
||
|
if !hasInstallerParam {
|
||
|
return e.Next() // nothing to remove
|
||
|
}
|
||
|
|
||
|
// clear installer param
|
||
|
total, _ := e.App.CountRecords(core.CollectionNameSuperusers)
|
||
|
if total > 0 {
|
||
|
return e.Redirect(http.StatusTemporaryRedirect, "?")
|
||
|
}
|
||
|
|
||
|
return e.Next()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// dashboardCacheControl adds default Cache-Control header for all
|
||
|
// dashboard UI resources (ignoring the root index.html path)
|
||
|
func dashboardCacheControl() hook.HandlerFunc[*core.RequestEvent] {
|
||
|
return func(e *core.RequestEvent) error {
|
||
|
if e.Request.PathValue(StaticWildcardParam) != "" {
|
||
|
e.Response.Header().Set("Cache-Control", "max-age=1209600, stale-while-revalidate=86400")
|
||
|
}
|
||
|
|
||
|
return e.Next()
|
||
|
}
|
||
|
}
|