refactored installer and removed RequireSuperuserAuthOnlyIfAny
This commit is contained in:
@@ -18,10 +18,10 @@ func bindBackupApi(app core.App, rg *router.RouterGroup[*core.RequestEvent]) {
|
||||
sub := rg.Group("/backups")
|
||||
sub.GET("", backupsList).Bind(RequireSuperuserAuth())
|
||||
sub.POST("", backupCreate).Bind(RequireSuperuserAuth())
|
||||
sub.POST("/upload", backupUpload).Bind(RequireSuperuserAuthOnlyIfAny())
|
||||
sub.POST("/upload", backupUpload).Bind(RequireSuperuserAuth())
|
||||
sub.GET("/{key}", backupDownload) // relies on superuser file token
|
||||
sub.DELETE("/{key}", backupDelete).Bind(RequireSuperuserAuth())
|
||||
sub.POST("/{key}/restore", backupRestore).Bind(RequireSuperuserAuthOnlyIfAny())
|
||||
sub.POST("/{key}/restore", backupRestore).Bind(RequireSuperuserAuth())
|
||||
}
|
||||
|
||||
type backupFileInfo struct {
|
||||
|
||||
@@ -346,30 +346,6 @@ func TestBackupUpload(t *testing.T) {
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "unauthorized with 0 superusers (valid file)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups/upload",
|
||||
Body: bodies[5].buffer,
|
||||
Headers: map[string]string{
|
||||
"Content-Type": bodies[5].contentType,
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
files, _ := getBackupFiles(app)
|
||||
if total := len(files); total != 1 {
|
||||
t.Fatalf("Expected %d backup file, got %d", 1, total)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
@@ -780,25 +756,6 @@ func TestBackupsRestore(t *testing.T) {
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "unauthorized with no superusers (checks only access)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups/missing.zip/restore",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := createTestBackups(app); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"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) func(*core.RequestEvent) error {
|
||||
// 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() func(*core.RequestEvent) error {
|
||||
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() func(*core.RequestEvent) error {
|
||||
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()
|
||||
}
|
||||
}
|
||||
128
apis/installer.go
Normal file
128
apis/installer.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
const installerEmail = "__pbinstaller@example.com"
|
||||
const installerHookId = "__pbinstallerHook"
|
||||
|
||||
func loadInstaller(app core.App, hostURL string) error {
|
||||
if !needInstallerSuperuser(app) {
|
||||
return nil
|
||||
}
|
||||
|
||||
installerRecord, err := findOrCreateInstallerSuperuser(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token, err := installerRecord.NewStaticAuthToken(30 * time.Minute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// prevent sending password reset emails to the installer address
|
||||
app.OnMailerRecordPasswordResetSend(core.CollectionNameSuperusers).Bind(&hook.Handler[*core.MailerRecordEvent]{
|
||||
Id: installerHookId,
|
||||
Func: func(e *core.MailerRecordEvent) error {
|
||||
if e.Record.Email() == installerEmail {
|
||||
return errors.New("cannot reset the password for the installer account")
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
},
|
||||
})
|
||||
|
||||
// cleanup the installer account after the first superuser creation
|
||||
app.OnRecordCreate(core.CollectionNameSuperusers).Bind(&hook.Handler[*core.RecordEvent]{
|
||||
Id: installerHookId,
|
||||
Func: func(e *core.RecordEvent) error {
|
||||
if err := e.Next(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
color.Green("Successfully created superuser %s! This message will no longer show on the next startup.\n\n", e.Record.Email())
|
||||
|
||||
if err = e.App.Delete(installerRecord); err != nil {
|
||||
e.App.Logger().Error("Failed to remove installer superuser", "error", err)
|
||||
}
|
||||
|
||||
app.OnRecordCreate().Unbind(installerHookId)
|
||||
app.OnMailerRecordPasswordResetSend().Unbind(installerHookId)
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
// launch url (ignore errors and always print a help text as fallback)
|
||||
url := fmt.Sprintf("%s/_/#/pbinstal/%s", hostURL, token)
|
||||
_ = launchURL(url)
|
||||
color.Magenta("\n(!) Launch the URL below in the browser if it hasn't been open already to create your first superuser account:")
|
||||
color.New(color.Bold).Add(color.FgCyan).Println(url)
|
||||
color.New(color.FgHiBlack, color.Italic).Printf("(you can also create your first superuser account by running '%s superuser upsert test@example.com yourpass' and restart the server)\n", os.Args[0])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func needInstallerSuperuser(app core.App) bool {
|
||||
total, err := app.CountRecords(core.CollectionNameSuperusers, dbx.Not(dbx.HashExp{
|
||||
"email": installerEmail,
|
||||
}))
|
||||
return err == nil && total == 0
|
||||
}
|
||||
|
||||
func findOrCreateInstallerSuperuser(app core.App) (*core.Record, error) {
|
||||
col, err := app.FindCachedCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record, err := app.FindAuthRecordByEmail(col, installerEmail)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record = core.NewRecord(col)
|
||||
record.SetEmail(installerEmail)
|
||||
record.SetPassword(security.RandomString(30))
|
||||
|
||||
err = app.Save(record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func launchURL(url string) error {
|
||||
if err := is.URL.Validate(url); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return exec.Command("open", url).Start()
|
||||
case "windows":
|
||||
// not sure if this is the best command but seems to be the most reliable based on the comments in
|
||||
// https://stackoverflow.com/questions/3739327/launching-a-website-via-the-windows-commandline#answer-49115945
|
||||
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
default: // linux, freebsd, etc.
|
||||
return exec.Command("xdg-open", url).Start()
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,6 @@ const (
|
||||
DefaultRequireGuestOnlyMiddlewareId = "pbRequireGuestOnly"
|
||||
DefaultRequireAuthMiddlewareId = "pbRequireAuth"
|
||||
DefaultRequireSuperuserAuthMiddlewareId = "pbRequireSuperuserAuth"
|
||||
DefaultRequireSuperuserAuthOnlyIfAnyMiddlewareId = "pbRequireSuperuserAuthOnlyIfAny"
|
||||
DefaultRequireSuperuserOrOwnerAuthMiddlewareId = "pbRequireSuperuserOrOwnerAuth"
|
||||
DefaultRequireSameCollectionContextAuthMiddlewareId = "pbRequireSameCollectionContextAuth"
|
||||
)
|
||||
@@ -110,31 +109,6 @@ func RequireSuperuserAuth() *hook.Handler[*core.RequestEvent] {
|
||||
}
|
||||
}
|
||||
|
||||
// RequireSuperuserAuthOnlyIfAny middleware requires a request to have
|
||||
// a valid superuser Authorization header ONLY if the application has
|
||||
// at least 1 existing superuser.
|
||||
func RequireSuperuserAuthOnlyIfAny() *hook.Handler[*core.RequestEvent] {
|
||||
return &hook.Handler[*core.RequestEvent]{
|
||||
Id: DefaultRequireSuperuserAuthOnlyIfAnyMiddlewareId,
|
||||
Func: func(e *core.RequestEvent) error {
|
||||
if e.HasSuperuserAuth() {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
totalSuperusers, err := e.App.CountRecords(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return e.InternalServerError("Failed to fetch superusers info.", err)
|
||||
}
|
||||
|
||||
if totalSuperusers == 0 {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
return requireAuth(core.CollectionNameSuperusers)(e)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RequireSuperuserOrOwnerAuth middleware requires a request to have
|
||||
// a valid superuser or regular record owner Authorization header set.
|
||||
//
|
||||
|
||||
@@ -302,95 +302,6 @@ func TestRequireSuperuserAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireSuperuserAuthOnlyIfAny(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []tests.ApiScenario{
|
||||
{
|
||||
Name: "guest (while having at least 1 existing superuser)",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "guest (while having 0 existing superusers)",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"test123"},
|
||||
},
|
||||
{
|
||||
Name: "expired/invalid token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjE2NDA5OTE2NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.0pDcBPGDpL2Khh76ivlRi7ugiLBSYvasct3qpHV3rfs",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "valid regular user token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.ZT3F0Z3iM-xbGgSG3LEKiEzHrPHr8t8IuHLZGGNuxLo",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "valid superuser auth token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"test123"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario.Test(t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireSuperuserOrOwnerAuth(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -169,17 +169,7 @@ func recordCreate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
}
|
||||
|
||||
hasSuperuserAuth := requestInfo.HasSuperuserAuth()
|
||||
canSkipRuleCheck := hasSuperuserAuth
|
||||
|
||||
// special case for the first superuser creation
|
||||
// ---
|
||||
if !canSkipRuleCheck && collection.Name == core.CollectionNameSuperusers {
|
||||
total, totalErr := e.App.CountRecords(core.CollectionNameSuperusers)
|
||||
canSkipRuleCheck = totalErr == nil && total == 0
|
||||
}
|
||||
// ---
|
||||
|
||||
if !canSkipRuleCheck && collection.CreateRule == nil {
|
||||
if !hasSuperuserAuth && collection.CreateRule == nil {
|
||||
return e.ForbiddenError("Only superusers can perform this action.", nil)
|
||||
}
|
||||
|
||||
@@ -212,7 +202,7 @@ func recordCreate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
form.SetRecord(e.Record)
|
||||
|
||||
// temporary save the record and check it against the create and manage rules
|
||||
if !canSkipRuleCheck && e.Collection.CreateRule != nil {
|
||||
if !hasSuperuserAuth && e.Collection.CreateRule != nil {
|
||||
// temporary grant manager access level
|
||||
form.GrantManagerAccess()
|
||||
|
||||
|
||||
@@ -229,41 +229,6 @@ func TestRecordCrudSuperuserCreate(t *testing.T) {
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "guest creating first superuser",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records",
|
||||
Body: body(),
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
ExpectedContent: []string{
|
||||
`"collectionName":"_superusers"`,
|
||||
`"verified":true`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
// because the action has no auth the email field shouldn't be returned if emailVisibility is not set
|
||||
`"email"`,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordCreateRequest": 1,
|
||||
"OnRecordEnrich": 1,
|
||||
"OnModelCreate": 1,
|
||||
"OnModelCreateExecute": 1,
|
||||
"OnModelAfterCreateSuccess": 1,
|
||||
"OnModelValidate": 1,
|
||||
"OnRecordCreate": 1,
|
||||
"OnRecordCreateExecute": 1,
|
||||
"OnRecordAfterCreateSuccess": 1,
|
||||
"OnRecordValidate": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "superusers auth",
|
||||
Method: http.MethodPost,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -88,11 +89,14 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
||||
}))
|
||||
|
||||
pbRouter.BindFunc(installerRedirect(app, config.DashboardPath))
|
||||
|
||||
pbRouter.GET(config.DashboardPath, Static(ui.DistDirFS, false)).
|
||||
BindFunc(dashboardRemoveInstallerParam()).
|
||||
BindFunc(dashboardCacheControl()).
|
||||
BindFunc(func(e *core.RequestEvent) error {
|
||||
// ingore root path
|
||||
if e.Request.PathValue(StaticWildcardParam) != "" {
|
||||
e.Response.Header().Set("Cache-Control", "max-age=1209600, stale-while-revalidate=86400")
|
||||
}
|
||||
return e.Next()
|
||||
}).
|
||||
Bind(Gzip())
|
||||
|
||||
// start http server
|
||||
@@ -240,18 +244,17 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
return errors.New("The OnServe finalizer wasn't invoked. Did you forget to call the ServeEvent.Next() method?")
|
||||
}
|
||||
|
||||
if config.ShowStartBanner {
|
||||
schema := "http"
|
||||
addr := server.Addr
|
||||
|
||||
if config.HttpsAddr != "" {
|
||||
schema = "https"
|
||||
|
||||
if len(config.CertificateDomains) > 0 {
|
||||
addr = config.CertificateDomains[0]
|
||||
}
|
||||
schema := "http"
|
||||
addr := server.Addr
|
||||
if config.HttpsAddr != "" {
|
||||
schema = "https"
|
||||
if len(config.CertificateDomains) > 0 {
|
||||
addr = config.CertificateDomains[0]
|
||||
}
|
||||
}
|
||||
fullAddr := fmt.Sprintf("%s://%s", schema, addr)
|
||||
|
||||
if config.ShowStartBanner {
|
||||
date := new(strings.Builder)
|
||||
log.New(date, "", log.LstdFlags).Print()
|
||||
|
||||
@@ -259,14 +262,16 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
bold.Printf(
|
||||
"%s Server started at %s\n",
|
||||
strings.TrimSpace(date.String()),
|
||||
color.CyanString("%s://%s", schema, addr),
|
||||
color.CyanString("%s", fullAddr),
|
||||
)
|
||||
|
||||
regular := color.New()
|
||||
regular.Printf("├─ REST API: %s\n", color.CyanString("%s://%s/api/", schema, addr))
|
||||
regular.Printf("└─ Admin UI: %s\n", color.CyanString("%s://%s/_/", schema, addr))
|
||||
regular.Printf("├─ REST API: %s\n", color.CyanString("%s/api/", fullAddr))
|
||||
regular.Printf("└─ Dashboard: %s\n", color.CyanString("%s/_/", fullAddr))
|
||||
}
|
||||
|
||||
go loadInstaller(app, fullAddr)
|
||||
|
||||
var serveErr error
|
||||
if config.HttpsAddr != "" {
|
||||
if config.HttpAddr != "" {
|
||||
|
||||
Reference in New Issue
Block a user