Compare commits

...
24 Commits
Author SHA1 Message Date
Gani Georgiev 982f876a93 updated jsvm types 2024-01-03 11:08:30 +02:00
Gani Georgiev 1fcc2d8683 updated CHANGELOG and added t.Parallel to some of the tests 2024-01-03 10:58:25 +02:00
Gani Georgiev 4f2492290e [#4068] fixed the json field query comparisons to work correctly with plain JSON values 2024-01-03 10:43:46 +02:00
Gani Georgiev 8f625daa2f updated some of the tests to use t.Parallel 2024-01-03 04:30:20 +02:00
Gani Georgiev 0599955676 sort cascadeDelete refs for deterministic tests output 2024-01-03 04:29:30 +02:00
Gani Georgiev c4116e3a7d added jsvm sleep binding 2023-12-29 23:29:00 +02:00
Gani Georgiev 64cee264f0 bumped app version 2023-12-29 22:00:47 +02:00
Gani Georgiev 0ae9f24a81 updated fields query param examples for the auth actions 2023-12-29 21:47:04 +02:00
Gani Georgiev 6d942c7d30 docs fixes commits from develop 2023-12-29 21:25:32 +02:00
Gani Georgiev 9f67c5d563 regenerated jsvm types 2023-12-29 09:58:21 +02:00
mookrs 1ac7330e0b [#4043] fixed typos in godoc comments 2023-12-29 09:56:36 +02:00
Gani Georgiev 461886f64e fixed the monospace font loading in the Admin UI 2023-12-27 10:47:18 +02:00
Gani Georgiev 8d97eb0769 [#4022] fixed multi-line text paste in the Admin UI search bar 2023-12-24 11:13:17 +02:00
Gani Georgiev 1208edec92 regenerated jsvm types 2023-12-17 00:20:04 +02:00
Gani Georgiev 5555e63116 updated dev debug log text message color to be slightly more visible 2023-12-17 00:17:29 +02:00
Gani Georgiev d6569b445c added timestamp to the generated JSVM types file to prevent creating it every time on app startup 2023-12-16 23:20:38 +02:00
Gani Georgiev 0b4f3b2adf combine the logs listing label span 2023-12-16 23:16:07 +02:00
Gani Georgiev 8bd968ed06 split changelog in chunks 2023-12-16 18:22:18 +02:00
Gani Georgiev 5c961f8537 [#3918] added --dev flag, dev log printer and some minor log UI enhacements 2023-12-16 18:15:36 +02:00
Gani Georgiev bf5eba0384 added bool to the view query sql syntax highlighter and autocompletion 2023-12-13 09:07:18 +02:00
Gani Georgiev c213d9313e fixed changelog typo 2023-12-12 19:48:28 +02:00
Gani Georgiev b31cf984a5 [#3930] replaced the default 100ms api tests timeout in favor of new ApiScenario.Timeout field 2023-12-12 19:46:58 +02:00
Gani Georgiev 8671debc35 removed the blank current time entry from the logs chart 2023-12-11 09:49:06 +02:00
Gani Georgiev b0f027d27a updated changelog formatting and temp moved the admin only rule checks to the record_helpers 2023-12-10 21:06:02 +02:00
150 changed files with 8006 additions and 6848 deletions

No files matched your search

+75 -1407
View File
File diff suppressed because it is too large. Load diff
+1384
View File
File diff suppressed because it is too large. Load diff
+18
View File
@@ -17,6 +17,8 @@ import (
)
func TestAdminAuthWithPassword(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "empty data",
@@ -119,6 +121,8 @@ func TestAdminAuthWithPassword(t *testing.T) {
}
func TestAdminRequestPasswordReset(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "empty data",
@@ -188,6 +192,8 @@ func TestAdminRequestPasswordReset(t *testing.T) {
}
func TestAdminConfirmPasswordReset(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "empty data",
@@ -277,6 +283,8 @@ func TestAdminConfirmPasswordReset(t *testing.T) {
}
func TestAdminRefresh(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -350,6 +358,8 @@ func TestAdminRefresh(t *testing.T) {
}
func TestAdminsList(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -453,6 +463,8 @@ func TestAdminsList(t *testing.T) {
}
func TestAdminView(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -508,6 +520,8 @@ func TestAdminView(t *testing.T) {
}
func TestAdminDelete(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -603,6 +617,8 @@ func TestAdminDelete(t *testing.T) {
}
func TestAdminCreate(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized (while having at least 1 existing admin)",
@@ -757,6 +773,8 @@ func TestAdminCreate(t *testing.T) {
}
func TestAdminUpdate(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
+12
View File
@@ -10,6 +10,8 @@ import (
)
func TestNewApiErrorWithRawData(t *testing.T) {
t.Parallel()
e := apis.NewApiError(
300,
"message_test",
@@ -33,6 +35,8 @@ func TestNewApiErrorWithRawData(t *testing.T) {
}
func TestNewApiErrorWithValidationData(t *testing.T) {
t.Parallel()
e := apis.NewApiError(
300,
"message_test",
@@ -66,6 +70,8 @@ func TestNewApiErrorWithValidationData(t *testing.T) {
}
func TestNewNotFoundError(t *testing.T) {
t.Parallel()
scenarios := []struct {
message string
data any
@@ -87,6 +93,8 @@ func TestNewNotFoundError(t *testing.T) {
}
func TestNewBadRequestError(t *testing.T) {
t.Parallel()
scenarios := []struct {
message string
data any
@@ -108,6 +116,8 @@ func TestNewBadRequestError(t *testing.T) {
}
func TestNewForbiddenError(t *testing.T) {
t.Parallel()
scenarios := []struct {
message string
data any
@@ -129,6 +139,8 @@ func TestNewForbiddenError(t *testing.T) {
}
func TestNewUnauthorizedError(t *testing.T) {
t.Parallel()
scenarios := []struct {
message string
data any
+12
View File
@@ -17,6 +17,8 @@ import (
)
func TestBackupsList(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -84,6 +86,8 @@ func TestBackupsList(t *testing.T) {
}
func TestBackupsCreate(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -198,6 +202,8 @@ func TestBackupsCreate(t *testing.T) {
}
func TestBackupsUpload(t *testing.T) {
t.Parallel()
// create dummy form data bodies
type body struct {
buffer io.Reader
@@ -330,6 +336,8 @@ func TestBackupsUpload(t *testing.T) {
}
func TestBackupsDownload(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -485,6 +493,8 @@ func TestBackupsDownload(t *testing.T) {
}
func TestBackupsDelete(t *testing.T) {
t.Parallel()
noTestBackupFilesChanges := func(t *testing.T, app *tests.TestApp) {
files, err := getBackupFiles(app)
if err != nil {
@@ -645,6 +655,8 @@ func TestBackupsDelete(t *testing.T) {
}
func TestBackupsRestore(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
+10
View File
@@ -15,6 +15,8 @@ import (
)
func Test404(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Method: http.MethodGet,
@@ -53,6 +55,8 @@ func Test404(t *testing.T) {
}
func TestCustomRoutesAndErrorsHandling(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "custom route",
@@ -142,6 +146,8 @@ func TestCustomRoutesAndErrorsHandling(t *testing.T) {
}
func TestRemoveTrailingSlashMiddleware(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "non /api/* route (exact match)",
@@ -215,6 +221,8 @@ func TestRemoveTrailingSlashMiddleware(t *testing.T) {
}
func TestEagerRequestInfoCache(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "custom non-api group route",
@@ -316,6 +324,8 @@ func TestEagerRequestInfoCache(t *testing.T) {
}
func TestErrorHandler(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "apis.ApiError",
+12
View File
@@ -17,6 +17,8 @@ import (
)
func TestCollectionsList(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -125,6 +127,8 @@ func TestCollectionsList(t *testing.T) {
}
func TestCollectionView(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -193,6 +197,8 @@ func TestCollectionView(t *testing.T) {
}
func TestCollectionDelete(t *testing.T) {
t.Parallel()
ensureDeletedFiles := func(app *tests.TestApp, collectionId string) {
storageDir := filepath.Join(app.DataDir(), "storage", collectionId)
@@ -338,6 +344,8 @@ func TestCollectionDelete(t *testing.T) {
}
func TestCollectionCreate(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -715,6 +723,8 @@ func TestCollectionCreate(t *testing.T) {
}
func TestCollectionUpdate(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -1082,6 +1092,8 @@ func TestCollectionUpdate(t *testing.T) {
}
func TestCollectionsImport(t *testing.T) {
t.Parallel()
totalCollections := 11
scenarios := []tests.ApiScenario{
+6
View File
@@ -20,6 +20,8 @@ import (
)
func TestFileToken(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -92,6 +94,8 @@ func TestFileToken(t *testing.T) {
}
func TestFileDownload(t *testing.T) {
t.Parallel()
_, currentFile, _, _ := runtime.Caller(0)
dataDirRelPath := "../tests/data/"
@@ -391,6 +395,8 @@ func TestFileDownload(t *testing.T) {
}
func TestConcurrentThumbsGeneration(t *testing.T) {
t.Parallel()
app, err := tests.NewTestApp()
if err != nil {
t.Fatal(err)
+2
View File
@@ -8,6 +8,8 @@ import (
)
func TestHealthAPI(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "health status returns 200",
+6
View File
@@ -9,6 +9,8 @@ import (
)
func TestLogsList(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -78,6 +80,8 @@ func TestLogsList(t *testing.T) {
}
func TestLogView(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -136,6 +140,8 @@ func TestLogView(t *testing.T) {
}
func TestLogsStats(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
+11 -3
View File
@@ -5,6 +5,7 @@ import (
"log/slog"
"net"
"net/http"
"net/url"
"strings"
"time"
@@ -315,7 +316,7 @@ func logRequest(app core.App, c echo.Context, err *ApiError) {
httpResponse := c.Response()
method := strings.ToUpper(httpRequest.Method)
status := httpResponse.Status
url := httpRequest.URL.RequestURI()
requestUri := httpRequest.URL.RequestURI()
// parse the request error
if err != nil {
@@ -336,7 +337,7 @@ func logRequest(app core.App, c echo.Context, err *ApiError) {
attrs = append(
attrs,
slog.String("url", url),
slog.String("url", requestUri),
slog.String("method", method),
slog.Int("status", status),
slog.String("auth", requestAuth),
@@ -355,7 +356,14 @@ func logRequest(app core.App, c echo.Context, err *ApiError) {
// don't block on logs write
routine.FireAndForget(func() {
message := method + " " + url
message := method + " "
if escaped, err := url.PathUnescape(requestUri); err == nil {
message += escaped
} else {
message += requestUri
}
if err != nil {
app.Logger().Error(message, attrs...)
} else {
+16
View File
@@ -10,6 +10,8 @@ import (
)
func TestRequireGuestOnly(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "valid record token",
@@ -104,6 +106,8 @@ func TestRequireGuestOnly(t *testing.T) {
}
func TestRequireRecordAuth(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "guest",
@@ -242,6 +246,8 @@ func TestRequireRecordAuth(t *testing.T) {
}
func TestRequireSameContextRecordAuth(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "guest",
@@ -358,6 +364,8 @@ func TestRequireSameContextRecordAuth(t *testing.T) {
}
func TestRequireAdminAuth(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "guest",
@@ -452,6 +460,8 @@ func TestRequireAdminAuth(t *testing.T) {
}
func TestRequireAdminAuthOnlyIfAny(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "guest (while having at least 1 existing admin)",
@@ -571,6 +581,8 @@ func TestRequireAdminAuthOnlyIfAny(t *testing.T) {
}
func TestRequireAdminOrRecordAuth(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "guest",
@@ -731,6 +743,8 @@ func TestRequireAdminOrRecordAuth(t *testing.T) {
}
func TestRequireAdminOrOwnerAuth(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "guest",
@@ -869,6 +883,8 @@ func TestRequireAdminOrOwnerAuth(t *testing.T) {
}
func TestLoadCollectionContext(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "missing collection",
+4
View File
@@ -568,6 +568,10 @@ func (api *realtimeApi) canAccessRecord(
return true // no further checks needed
}
if err := checkForAdminOnlyRuleFields(requestInfo); err != nil {
return false
}
ruleFunc := func(q *dbx.SelectQuery) error {
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), record.Collection(), requestInfo, false)
+4
View File
@@ -5,6 +5,7 @@ import (
"net/http"
"strings"
"testing"
"time"
"github.com/labstack/echo/v5"
"github.com/pocketbase/dbx"
@@ -22,6 +23,7 @@ func TestRealtimeConnect(t *testing.T) {
{
Method: http.MethodGet,
Url: "/api/realtime",
Timeout: 100 * time.Millisecond,
ExpectedStatus: 200,
ExpectedContent: []string{
`id:`,
@@ -44,6 +46,7 @@ func TestRealtimeConnect(t *testing.T) {
Name: "PB_CONNECT interrupt",
Method: http.MethodGet,
Url: "/api/realtime",
Timeout: 100 * time.Millisecond,
ExpectedStatus: 200,
ExpectedEvents: map[string]int{
"OnRealtimeConnectRequest": 1,
@@ -68,6 +71,7 @@ func TestRealtimeConnect(t *testing.T) {
Name: "Skipping/ignoring messages",
Method: http.MethodGet,
Url: "/api/realtime",
Timeout: 100 * time.Millisecond,
ExpectedStatus: 200,
ExpectedEvents: map[string]int{
"OnRealtimeConnectRequest": 1,
+24
View File
@@ -17,6 +17,8 @@ import (
)
func TestRecordAuthMethodsList(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "missing collection",
@@ -71,6 +73,8 @@ func TestRecordAuthMethodsList(t *testing.T) {
}
func TestRecordAuthWithPassword(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "invalid body format",
@@ -356,6 +360,8 @@ func TestRecordAuthWithPassword(t *testing.T) {
}
func TestRecordAuthRefresh(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -483,6 +489,8 @@ func TestRecordAuthRefresh(t *testing.T) {
}
func TestRecordAuthRequestPasswordReset(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "not an auth collection",
@@ -568,6 +576,8 @@ func TestRecordAuthRequestPasswordReset(t *testing.T) {
}
func TestRecordAuthConfirmPasswordReset(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "empty data",
@@ -681,6 +691,8 @@ func TestRecordAuthConfirmPasswordReset(t *testing.T) {
}
func TestRecordAuthRequestVerification(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "not an auth collection",
@@ -774,6 +786,8 @@ func TestRecordAuthRequestVerification(t *testing.T) {
}
func TestRecordAuthConfirmVerification(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "empty data",
@@ -902,6 +916,8 @@ func TestRecordAuthConfirmVerification(t *testing.T) {
}
func TestRecordAuthRequestEmailChange(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -1004,6 +1020,8 @@ func TestRecordAuthRequestEmailChange(t *testing.T) {
}
func TestRecordAuthConfirmEmailChange(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "not an auth collection",
@@ -1124,6 +1142,8 @@ func TestRecordAuthConfirmEmailChange(t *testing.T) {
}
func TestRecordAuthListExternalsAuths(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -1224,6 +1244,8 @@ func TestRecordAuthListExternalsAuths(t *testing.T) {
}
func TestRecordAuthUnlinkExternalsAuth(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -1353,6 +1375,8 @@ func TestRecordAuthUnlinkExternalsAuth(t *testing.T) {
}
func TestRecordAuthOAuth2Redirect(t *testing.T) {
t.Parallel()
c1 := subscriptions.NewDefaultClient()
c2 := subscriptions.NewDefaultClient()
+3 -22
View File
@@ -4,7 +4,6 @@ import (
"fmt"
"log/slog"
"net/http"
"strings"
"github.com/labstack/echo/v5"
"github.com/pocketbase/dbx"
@@ -43,13 +42,13 @@ func (api *recordApi) list(c echo.Context) error {
return NewNotFoundError("", "Missing collection context.")
}
requestInfo := RequestInfo(c)
// forbid users and guests to query special filter/sort fields
if err := api.checkForForbiddenQueryFields(c); err != nil {
if err := checkForAdminOnlyRuleFields(requestInfo); err != nil {
return err
}
requestInfo := RequestInfo(c)
if requestInfo.Admin == nil && collection.ListRule == nil {
// only admins can access if the rule is nil
return NewForbiddenError("Only admins can perform this action.", nil)
@@ -409,21 +408,3 @@ func (api *recordApi) delete(c echo.Context) error {
})
})
}
func (api *recordApi) checkForForbiddenQueryFields(c echo.Context) error {
admin, _ := c.Get(ContextAdminKey).(*models.Admin)
if admin != nil {
return nil // admins are allowed to query everything
}
decodedQuery := c.QueryParam(search.FilterQueryParam) + c.QueryParam(search.SortQueryParam)
forbiddenFields := []string{"@collection.", "@request."}
for _, field := range forbiddenFields {
if strings.Contains(decodedQuery, field) {
return NewForbiddenError("Only admins can filter by @collection and @request query params", nil)
}
}
return nil
}
+19 -2
View File
@@ -17,6 +17,8 @@ import (
)
func TestRecordCrudList(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "missing collection",
@@ -43,9 +45,16 @@ func TestRecordCrudList(t *testing.T) {
ExpectedContent: []string{`"data":{}`},
},
{
Name: "public collection but with admin only filter/sort (aka. @collection)",
Name: "public collection but with admin only filter param (aka. @collection, @request, etc.)",
Method: http.MethodGet,
Url: "/api/collections/demo2/records?filter=@collection.demo2.title='test1'",
Url: "/api/collections/demo2/records?filter=%40collection.demo2.title='test1'",
ExpectedStatus: 403,
ExpectedContent: []string{`"data":{}`},
},
{
Name: "public collection but with admin only sort param (aka. @collection, @request, etc.)",
Method: http.MethodGet,
Url: "/api/collections/demo2/records?sort=@request.auth.title",
ExpectedStatus: 403,
ExpectedContent: []string{`"data":{}`},
},
@@ -486,6 +495,8 @@ func TestRecordCrudList(t *testing.T) {
}
func TestRecordCrudView(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "missing collection",
@@ -765,6 +776,8 @@ func TestRecordCrudView(t *testing.T) {
}
func TestRecordCrudDelete(t *testing.T) {
t.Parallel()
ensureDeletedFiles := func(app *tests.TestApp, collectionId string, recordId string) {
storageDir := filepath.Join(app.DataDir(), "storage", collectionId, recordId)
@@ -1008,6 +1021,8 @@ func TestRecordCrudDelete(t *testing.T) {
}
func TestRecordCrudCreate(t *testing.T) {
t.Parallel()
formData, mp, err := tests.MockMultipartData(map[string]string{
"title": "title_test",
}, "files")
@@ -1584,6 +1599,8 @@ func TestRecordCrudCreate(t *testing.T) {
}
func TestRecordCrudUpdate(t *testing.T) {
t.Parallel()
formData, mp, err := tests.MockMultipartData(map[string]string{
"title": "title_test",
}, "files")
+28
View File
@@ -314,3 +314,31 @@ func hasAuthManageAccess(
return findErr == nil
}
var ruleQueryParams = []string{search.FilterQueryParam, search.SortQueryParam}
var adminOnlyRuleFields = []string{"@collection.", "@request."}
// @todo consider moving the rules check to the RecordFieldResolver.
//
// checkForAdminOnlyRuleFields loosely checks and returns an error if
// the provided RequestInfo contains rule fields that only the admin can use.
func checkForAdminOnlyRuleFields(requestInfo *models.RequestInfo) error {
if requestInfo.Admin != nil || len(requestInfo.Query) == 0 {
return nil // admin or nothing to check
}
for _, param := range ruleQueryParams {
v, _ := requestInfo.Query[param].(string)
if v == "" {
continue
}
for _, field := range adminOnlyRuleFields {
if strings.Contains(v, field) {
return NewForbiddenError("Only admins can filter by "+field, nil)
}
}
}
return nil
}
+6
View File
@@ -14,6 +14,8 @@ import (
)
func TestRequestInfo(t *testing.T) {
t.Parallel()
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/?test=123", strings.NewReader(`{"test":456}`))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
@@ -67,6 +69,8 @@ func TestRequestInfo(t *testing.T) {
}
func TestRecordAuthResponse(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -189,6 +193,8 @@ func TestRecordAuthResponse(t *testing.T) {
}
func TestEnrichRecords(t *testing.T) {
t.Parallel()
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/?expand=rel_many", nil)
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
+10
View File
@@ -18,6 +18,8 @@ import (
)
func TestSettingsList(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -97,6 +99,8 @@ func TestSettingsList(t *testing.T) {
}
func TestSettingsSet(t *testing.T) {
t.Parallel()
validData := `{"meta":{"appName":"update_test"}}`
scenarios := []tests.ApiScenario{
@@ -278,6 +282,8 @@ func TestSettingsSet(t *testing.T) {
}
func TestSettingsTestS3(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -344,6 +350,8 @@ func TestSettingsTestS3(t *testing.T) {
}
func TestSettingsTestEmail(t *testing.T) {
t.Parallel()
scenarios := []tests.ApiScenario{
{
Name: "unauthorized",
@@ -508,6 +516,8 @@ func TestSettingsTestEmail(t *testing.T) {
}
func TestGenerateAppleClientSecret(t *testing.T) {
t.Parallel()
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
+2
View File
@@ -8,6 +8,8 @@ import (
)
func TestAdminCreateCommand(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+5 -2
View File
@@ -59,6 +59,9 @@ type App interface {
// (used for settings encryption).
EncryptionEnv() string
// IsDev returns whether the app is in dev mode.
IsDev() bool
// Settings returns the loaded app settings.
Settings() *settings.Settings
@@ -288,14 +291,14 @@ type App interface {
// SSE client connection.
OnRealtimeDisconnectRequest() *hook.Hook[*RealtimeDisconnectEvent]
// OnRealtimeBeforeMessage hook is triggered right before sending
// OnRealtimeBeforeMessageSend hook is triggered right before sending
// an SSE message to a client.
//
// Returning [hook.StopPropagation] will prevent sending the message.
// Returning any other non-nil error will close the realtime connection.
OnRealtimeBeforeMessageSend() *hook.Hook[*RealtimeMessageEvent]
// OnRealtimeBeforeMessage hook is triggered right after sending
// OnRealtimeAfterMessageSend hook is triggered right after sending
// an SSE message to a client.
OnRealtimeAfterMessageSend() *hook.Hook[*RealtimeMessageEvent]
+45 -16
View File
@@ -47,6 +47,7 @@ type BaseApp struct {
// @todo consider introducing a mutex to allow safe concurrent config changes during runtime
// configurable parameters
isDev bool
dataDir string
encryptionEnv string
dataMaxOpenConns int
@@ -174,6 +175,7 @@ type BaseApp struct {
// BaseAppConfig defines a BaseApp configuration option
type BaseAppConfig struct {
IsDev bool
DataDir string
EncryptionEnv string
DataMaxOpenConns int // default to 500
@@ -188,6 +190,7 @@ type BaseAppConfig struct {
// To initialize the app, you need to call `app.Bootstrap()`.
func NewBaseApp(config BaseAppConfig) *BaseApp {
app := &BaseApp{
isDev: config.IsDev,
dataDir: config.DataDir,
encryptionEnv: config.EncryptionEnv,
dataMaxOpenConns: config.DataMaxOpenConns,
@@ -458,6 +461,13 @@ func (app *BaseApp) EncryptionEnv() string {
return app.encryptionEnv
}
// IsDev returns whether the app is in dev mode.
//
// When enabled logs, executed sql statements, etc. are printed to the stderr.
func (app *BaseApp) IsDev() bool {
return app.isDev
}
// Settings returns the loaded app settings.
func (app *BaseApp) Settings() *settings.Settings {
return app.settings
@@ -589,9 +599,11 @@ func (app *BaseApp) RefreshSettings() error {
return err
}
// reload handler level (if initialized)
if h, ok := app.Logger().Handler().(*logger.BatchHandler); ok {
h.SetLevel(slog.Level(app.settings.Logs.MinLevel))
// reload handler level (if initialized and not in dev mode)
if !app.IsDev() && app.Logger() != nil {
if h, ok := app.Logger().Handler().(*logger.BatchHandler); ok {
h.SetLevel(slog.Level(app.settings.Logs.MinLevel))
}
}
return nil
@@ -1054,15 +1066,16 @@ func (app *BaseApp) initDataDB() error {
nonconcurrentDB.DB().SetMaxIdleConns(1)
nonconcurrentDB.DB().SetConnMaxIdleTime(3 * time.Minute)
// @todo benchmark whether it will have an impact if always enabled as TRACE log
// nonconcurrentDB.QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {
// color.HiBlack("[%.2fms] %v\n", float64(t.Milliseconds()), sql)
// }
// concurrentDB.QueryLogFunc = nonconcurrentDB.QueryLogFunc
// nonconcurrentDB.ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {
// color.HiBlack("[%.2fms] %v\n", float64(t.Milliseconds()), sql)
// }
// concurrentDB.ExecLogFunc = nonconcurrentDB.ExecLogFunc
if app.IsDev() {
nonconcurrentDB.QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {
color.HiBlack("[%.2fms] %v\n", float64(t.Milliseconds()), sql)
}
nonconcurrentDB.ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {
color.HiBlack("[%.2fms] %v\n", float64(t.Milliseconds()), sql)
}
concurrentDB.QueryLogFunc = nonconcurrentDB.QueryLogFunc
concurrentDB.ExecLogFunc = nonconcurrentDB.ExecLogFunc
}
app.dao = app.createDaoWithHooks(concurrentDB, nonconcurrentDB)
@@ -1176,15 +1189,31 @@ func (app *BaseApp) initLogger() error {
ticker := time.NewTicker(duration)
done := make(chan bool)
level := slog.LevelInfo
if app.Settings() != nil {
level = slog.Level(app.Settings().Logs.MinLevel)
// Apply the min level only if it is not in develop
// to allow printing the logs to the console.
//
// DB logs are still filtered but the checks for the min level are done
// in the BatchOptions.BeforeAddFunc instead of the slog.Handler.Enabled() method.
var minLevel slog.Level
if app.IsDev() {
minLevel = -9999
} else if app.Settings() != nil {
minLevel = slog.Level(app.Settings().Logs.MinLevel)
}
handler := logger.NewBatchHandler(logger.BatchOptions{
Level: level,
Level: minLevel,
BatchSize: 200,
BeforeAddFunc: func(ctx context.Context, log *logger.Log) bool {
if app.IsDev() {
printLog(log)
// manually check the log level and skip if necessary
if log.Level < slog.Level(app.Settings().Logs.MinLevel) {
return false
}
}
ticker.Reset(duration)
return app.Settings().Logs.MaxDays > 0
+253 -46
View File
@@ -1,11 +1,17 @@
package core
import (
"fmt"
"log/slog"
"os"
"testing"
"time"
"github.com/pocketbase/pocketbase/daos"
"github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/migrations/logs"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/tools/list"
"github.com/pocketbase/pocketbase/tools/logger"
"github.com/pocketbase/pocketbase/tools/mailer"
"github.com/pocketbase/pocketbase/tools/migrate"
@@ -18,6 +24,7 @@ func TestNewBaseApp(t *testing.T) {
app := NewBaseApp(BaseAppConfig{
DataDir: testDataDir,
EncryptionEnv: "test_env",
IsDev: true,
})
if app.dataDir != testDataDir {
@@ -28,6 +35,10 @@ func TestNewBaseApp(t *testing.T) {
t.Fatalf("expected encryptionEnv test_env, got %q", app.dataDir)
}
if !app.isDev {
t.Fatalf("expected isDev true, got %v", app.isDev)
}
if app.store == nil {
t.Fatal("expected store to be set, got nil")
}
@@ -132,6 +143,7 @@ func TestBaseAppGetters(t *testing.T) {
app := NewBaseApp(BaseAppConfig{
DataDir: testDataDir,
EncryptionEnv: "pb_test_env",
IsDev: true,
})
defer app.ResetBootstrapState()
@@ -163,6 +175,10 @@ func TestBaseAppGetters(t *testing.T) {
t.Fatalf("Expected app.EncryptionEnv %v, got %v", app.EncryptionEnv(), app.encryptionEnv)
}
if app.isDev != app.IsDev() {
t.Fatalf("Expected app.IsDev %v, got %v", app.IsDev(), app.isDev)
}
if app.settings != app.Settings() {
t.Fatalf("Expected app.Settings %v, got %v", app.Settings(), app.settings)
}
@@ -185,13 +201,11 @@ func TestBaseAppGetters(t *testing.T) {
}
func TestBaseAppNewMailClient(t *testing.T) {
const testDataDir = "./pb_base_app_test_data_dir/"
defer os.RemoveAll(testDataDir)
app := NewBaseApp(BaseAppConfig{
DataDir: testDataDir,
EncryptionEnv: "pb_test_env",
})
app, cleanup, err := initTestBaseApp()
if err != nil {
t.Fatal(err)
}
defer cleanup()
client1 := app.NewMailClient()
if val, ok := client1.(*mailer.Sendmail); !ok {
@@ -207,13 +221,11 @@ func TestBaseAppNewMailClient(t *testing.T) {
}
func TestBaseAppNewFilesystem(t *testing.T) {
const testDataDir = "./pb_base_app_test_data_dir/"
defer os.RemoveAll(testDataDir)
app := NewBaseApp(BaseAppConfig{
DataDir: testDataDir,
EncryptionEnv: "pb_test_env",
})
app, cleanup, err := initTestBaseApp()
if err != nil {
t.Fatal(err)
}
defer cleanup()
// local
local, localErr := app.NewFilesystem()
@@ -236,13 +248,11 @@ func TestBaseAppNewFilesystem(t *testing.T) {
}
func TestBaseAppNewBackupsFilesystem(t *testing.T) {
const testDataDir = "./pb_base_app_test_data_dir/"
defer os.RemoveAll(testDataDir)
app := NewBaseApp(BaseAppConfig{
DataDir: testDataDir,
EncryptionEnv: "pb_test_env",
})
app, cleanup, err := initTestBaseApp()
if err != nil {
t.Fatal(err)
}
defer cleanup()
// local
local, localErr := app.NewBackupsFilesystem()
@@ -265,31 +275,25 @@ func TestBaseAppNewBackupsFilesystem(t *testing.T) {
}
func TestBaseAppLoggerWrites(t *testing.T) {
testDataDir, err := os.MkdirTemp("", "logger_writes")
app, cleanup, err := initTestBaseApp()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(testDataDir)
app := NewBaseApp(BaseAppConfig{
DataDir: testDataDir,
})
if err := app.Bootstrap(); err != nil {
t.Fatal(err)
}
// init logs migrations
runner, err := migrate.NewRunner(app.LogsDB(), logs.LogsMigrations)
if err != nil {
t.Fatalf("Logs runner error: %v", err)
}
if _, err := runner.Up(); err != nil {
t.Fatalf("Logs migration execution error: %v", err)
}
defer cleanup()
threshold := 200
totalLogs := func(app App, t *testing.T) int {
var total int
err := app.LogsDao().LogQuery().Select("count(*)").Row(&total)
if err != nil {
t.Fatalf("Failed to fetch total logs: %v", err)
}
return total
}
// disabled logs retention
{
app.Settings().Logs.MaxDays = 0
@@ -333,13 +337,216 @@ func TestBaseAppLoggerWrites(t *testing.T) {
}
}
func totalLogs(app App, t *testing.T) int {
var total int
err := app.LogsDao().LogQuery().Select("count(*)").Row(&total)
func TestBaseAppRefreshSettingsLoggerMinLevelEnabled(t *testing.T) {
app, cleanup, err := initTestBaseApp()
if err != nil {
t.Fatalf("Failed to fetch total logs: %v", err)
t.Fatal(err)
}
defer cleanup()
handler, ok := app.Logger().Handler().(*logger.BatchHandler)
if !ok {
t.Fatalf("Expected BatchHandler, got %v", app.Logger().Handler())
}
return total
scenarios := []struct {
name string
isDev bool
level int
// level->enabled map
expectations map[int]bool
}{
{
"dev mode",
true,
4,
map[int]bool{
3: true,
4: true,
5: true,
},
},
{
"nondev mode",
false,
4,
map[int]bool{
3: false,
4: true,
5: true,
},
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
app.isDev = s.isDev
app.Settings().Logs.MinLevel = s.level
if err := app.Dao().SaveSettings(app.Settings()); err != nil {
t.Fatalf("Failed to save settings: %v", err)
}
if err := app.RefreshSettings(); err != nil {
t.Fatalf("Failed to refresh app settings: %v", err)
}
for level, enabled := range s.expectations {
if v := handler.Enabled(nil, slog.Level(level)); v != enabled {
t.Fatalf("Expected level %d Enabled() to be %v, got %v", level, enabled, v)
}
}
})
}
}
func TestBaseAppLoggerLevelDevPrint(t *testing.T) {
app, cleanup, err := initTestBaseApp()
if err != nil {
t.Fatal(err)
}
defer cleanup()
testLogLevel := 4
app.Settings().Logs.MinLevel = testLogLevel
if err := app.Dao().SaveSettings(app.Settings()); err != nil {
t.Fatal(err)
}
scenarios := []struct {
name string
isDev bool
levels []int
printedLevels []int
persistedLevels []int
}{
{
"dev mode",
true,
[]int{testLogLevel - 1, testLogLevel, testLogLevel + 1},
[]int{testLogLevel - 1, testLogLevel, testLogLevel + 1},
[]int{testLogLevel, testLogLevel + 1},
},
{
"nondev mode",
false,
[]int{testLogLevel - 1, testLogLevel, testLogLevel + 1},
[]int{},
[]int{testLogLevel, testLogLevel + 1},
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
var printedLevels []int
var persistedLevels []int
app.isDev = s.isDev
// trigger slog handler min level refresh
if err := app.RefreshSettings(); err != nil {
t.Fatal(err)
}
// track printed logs
originalPrintLog := printLog
defer func() {
printLog = originalPrintLog
}()
printLog = func(log *logger.Log) {
printedLevels = append(printedLevels, int(log.Level))
}
// track persisted logs
app.LogsDao().AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
l, ok := m.(*models.Log)
if ok {
persistedLevels = append(persistedLevels, l.Level)
}
return nil
}
// write and persist logs
for _, l := range s.levels {
app.Logger().Log(nil, slog.Level(l), "test")
}
handler, ok := app.Logger().Handler().(*logger.BatchHandler)
if !ok {
t.Fatalf("Expected BatchHandler, got %v", app.Logger().Handler())
}
if err := handler.WriteAll(nil); err != nil {
t.Fatalf("Failed to write all logs: %v", err)
}
// check persisted log levels
if len(s.persistedLevels) != len(persistedLevels) {
t.Fatalf("Expected persisted levels \n%v\ngot\n%v", s.persistedLevels, persistedLevels)
}
for _, l := range persistedLevels {
if !list.ExistInSlice(l, s.persistedLevels) {
t.Fatalf("Missing expected persisted level %v in %v", l, persistedLevels)
}
}
// check printed log levels
if len(s.printedLevels) != len(printedLevels) {
t.Fatalf("Expected printed levels \n%v\ngot\n%v", s.printedLevels, printedLevels)
}
for _, l := range printedLevels {
if !list.ExistInSlice(l, s.printedLevels) {
t.Fatalf("Missing expected printed level %v in %v", l, printedLevels)
}
}
})
}
}
// -------------------------------------------------------------------
// note: make sure to call `defer cleanup()` when the app is no longer needed.
func initTestBaseApp() (app *BaseApp, cleanup func(), err error) {
testDataDir, err := os.MkdirTemp("", "test_base_app")
if err != nil {
return nil, nil, err
}
cleanup = func() {
os.RemoveAll(testDataDir)
}
app = NewBaseApp(BaseAppConfig{
DataDir: testDataDir,
})
initErr := func() error {
if err := app.Bootstrap(); err != nil {
return fmt.Errorf("bootstrap error: %w", err)
}
logsRunner, err := migrate.NewRunner(app.LogsDB(), logs.LogsMigrations)
if err != nil {
return fmt.Errorf("logsRunner error: %w", err)
}
if _, err := logsRunner.Up(); err != nil {
return fmt.Errorf("logsRunner migrations execution error: %w", err)
}
dataRunner, err := migrate.NewRunner(app.DB(), migrations.AppMigrations)
if err != nil {
return fmt.Errorf("logsRunner error: %w", err)
}
if _, err := dataRunner.Up(); err != nil {
return fmt.Errorf("dataRunner migrations execution error: %w", err)
}
return nil
}()
if initErr != nil {
cleanup()
return nil, nil, initErr
}
return app, cleanup, nil
}
+67
View File
@@ -0,0 +1,67 @@
package core
import (
"fmt"
"log/slog"
"strings"
"github.com/fatih/color"
"github.com/pocketbase/pocketbase/tools/logger"
"github.com/pocketbase/pocketbase/tools/store"
"github.com/spf13/cast"
)
var cachedColors = store.New[*color.Color](nil)
// getColor returns [color.Color] object and cache it (if not already).
func getColor(attrs ...color.Attribute) (c *color.Color) {
cacheKey := fmt.Sprint(attrs)
if c = cachedColors.Get(cacheKey); c == nil {
c = color.New(attrs...)
cachedColors.Set(cacheKey, c)
}
return
}
// printLog prints the provided log to the stderr.
// (note: defined as variable to overwriting in the tests)
var printLog = func(log *logger.Log) {
var str strings.Builder
switch log.Level {
case slog.LevelDebug:
str.WriteString(getColor(color.Bold, color.FgHiBlack).Sprint("DEBUG "))
str.WriteString(getColor(color.FgWhite).Sprint(log.Message))
case slog.LevelInfo:
str.WriteString(getColor(color.Bold, color.FgWhite).Sprint("INFO "))
str.WriteString(getColor(color.FgWhite).Sprint(log.Message))
case slog.LevelWarn:
str.WriteString(getColor(color.Bold, color.FgYellow).Sprint("WARN "))
str.WriteString(getColor(color.FgYellow).Sprint(log.Message))
case slog.LevelError:
str.WriteString(getColor(color.Bold, color.FgRed).Sprint("ERROR "))
str.WriteString(getColor(color.FgRed).Sprint(log.Message))
default:
str.WriteString(getColor(color.Bold, color.FgCyan).Sprintf("[%d] ", log.Level))
str.WriteString(getColor(color.FgCyan).Sprint(log.Message))
}
str.WriteString("\n")
if v, ok := log.Data["type"]; ok && cast.ToString(v) == "request" {
padding := 0
keys := []string{"error", "details"}
for _, k := range keys {
if v := log.Data[k]; v != nil {
str.WriteString(getColor(color.FgHiRed).Sprintf("%s└─ %v", strings.Repeat(" ", padding), v))
str.WriteString("\n")
padding += 3
}
}
} else if len(log.Data) > 0 {
str.WriteString(getColor(color.FgHiBlack).Sprintf("└─ %v", log.Data))
str.WriteString("\n")
}
fmt.Print(str.String())
}
+2 -2
View File
@@ -46,9 +46,9 @@ func (dao *Dao) FindAdminByEmail(email string) (*models.Admin, error) {
return model, nil
}
// FindAdminByToken finds the admin associated with the provided JWT token.
// FindAdminByToken finds the admin associated with the provided JWT.
//
// Returns an error if the JWT token is invalid or expired.
// Returns an error if the JWT is invalid or expired.
func (dao *Dao) FindAdminByToken(token string, baseTokenKey string) (*models.Admin, error) {
// @todo consider caching the unverified claims
unverifiedClaims, err := security.ParseUnverifiedJWT(token)
+16
View File
@@ -8,6 +8,8 @@ import (
)
func TestAdminQuery(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -20,6 +22,8 @@ func TestAdminQuery(t *testing.T) {
}
func TestFindAdminById(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -47,6 +51,8 @@ func TestFindAdminById(t *testing.T) {
}
func TestFindAdminByEmail(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -76,6 +82,8 @@ func TestFindAdminByEmail(t *testing.T) {
}
func TestFindAdminByToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -131,6 +139,8 @@ func TestFindAdminByToken(t *testing.T) {
}
func TestTotalAdmins(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -155,6 +165,8 @@ func TestTotalAdmins(t *testing.T) {
}
func TestIsAdminEmailUnique(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -180,6 +192,8 @@ func TestIsAdminEmailUnique(t *testing.T) {
}
func TestDeleteAdmin(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -225,6 +239,8 @@ func TestDeleteAdmin(t *testing.T) {
}
func TestSaveAdmin(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+4
View File
@@ -6,6 +6,8 @@ import (
)
func TestGetDefaultRetryInterval(t *testing.T) {
t.Parallel()
if i := getDefaultRetryInterval(-1); i.Milliseconds() != 1000 {
t.Fatalf("Expected 1000ms, got %v", i)
}
@@ -20,6 +22,8 @@ func TestGetDefaultRetryInterval(t *testing.T) {
}
func TestBaseLockRetry(t *testing.T) {
t.Parallel()
scenarios := []struct {
err error
failUntilAttempt int
+22
View File
@@ -16,6 +16,8 @@ import (
)
func TestCollectionQuery(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -28,6 +30,8 @@ func TestCollectionQuery(t *testing.T) {
}
func TestFindCollectionsByType(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -63,6 +67,8 @@ func TestFindCollectionsByType(t *testing.T) {
}
func TestFindCollectionByNameOrId(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -92,6 +98,8 @@ func TestFindCollectionByNameOrId(t *testing.T) {
}
func TestIsCollectionNameUnique(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -116,6 +124,8 @@ func TestIsCollectionNameUnique(t *testing.T) {
}
func TestFindCollectionReferences(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -164,6 +174,8 @@ func TestFindCollectionReferences(t *testing.T) {
}
func TestDeleteCollection(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -251,6 +263,8 @@ func TestDeleteCollection(t *testing.T) {
}
func TestSaveCollectionCreate(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -297,6 +311,8 @@ func TestSaveCollectionCreate(t *testing.T) {
}
func TestSaveCollectionUpdate(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -336,6 +352,8 @@ func TestSaveCollectionUpdate(t *testing.T) {
// indirect update of a field used in view should cause view(s) update
func TestSaveCollectionIndirectViewsUpdate(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -395,6 +413,8 @@ func TestSaveCollectionIndirectViewsUpdate(t *testing.T) {
}
func TestSaveCollectionViewWrapping(t *testing.T) {
t.Parallel()
viewName := "test_wrapping"
scenarios := []struct {
@@ -504,6 +524,8 @@ func TestSaveCollectionViewWrapping(t *testing.T) {
}
func TestImportCollections(t *testing.T) {
t.Parallel()
totalCollections := 11
scenarios := []struct {
+12
View File
@@ -9,6 +9,8 @@ import (
)
func TestExternalAuthQuery(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -21,6 +23,8 @@ func TestExternalAuthQuery(t *testing.T) {
}
func TestFindAllExternalAuthsByRecord(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -58,6 +62,8 @@ func TestFindAllExternalAuthsByRecord(t *testing.T) {
}
func TestFindFirstExternalAuthByExpr(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -89,6 +95,8 @@ func TestFindFirstExternalAuthByExpr(t *testing.T) {
}
func TestFindExternalAuthByRecordAndProvider(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -125,6 +133,8 @@ func TestFindExternalAuthByRecordAndProvider(t *testing.T) {
}
func TestSaveExternalAuth(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -161,6 +171,8 @@ func TestSaveExternalAuth(t *testing.T) {
}
func TestDeleteExternalAuth(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+10
View File
@@ -12,6 +12,8 @@ import (
)
func TestLogQuery(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -24,6 +26,8 @@ func TestLogQuery(t *testing.T) {
}
func TestFindLogById(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -54,6 +58,8 @@ func TestFindLogById(t *testing.T) {
}
func TestLogsStats(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -75,6 +81,8 @@ func TestLogsStats(t *testing.T) {
}
func TestDeleteOldLogs(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -115,6 +123,8 @@ func TestDeleteOldLogs(t *testing.T) {
}
func TestSaveLog(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+10
View File
@@ -11,6 +11,8 @@ import (
)
func TestParamQuery(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -23,6 +25,8 @@ func TestParamQuery(t *testing.T) {
}
func TestFindParamByKey(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -50,6 +54,8 @@ func TestFindParamByKey(t *testing.T) {
}
func TestSaveParam(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -92,6 +98,8 @@ func TestSaveParam(t *testing.T) {
}
func TestSaveParamEncrypted(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -126,6 +134,8 @@ func TestSaveParamEncrypted(t *testing.T) {
}
func TestDeleteParam(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+20 -5
View File
@@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
"sort"
"strings"
"github.com/pocketbase/dbx"
@@ -409,9 +410,9 @@ func (dao *Dao) IsRecordValueUnique(
return query.Row(&exists) == nil && !exists
}
// FindAuthRecordByToken finds the auth record associated with the provided JWT token.
// FindAuthRecordByToken finds the auth record associated with the provided JWT.
//
// Returns an error if the JWT token is invalid, expired or not associated to an auth collection record.
// Returns an error if the JWT is invalid, expired or not associated to an auth collection record.
func (dao *Dao) FindAuthRecordByToken(token string, baseTokenKey string) (*models.Record, error) {
unverifiedClaims, err := security.ParseUnverifiedJWT(token)
if err != nil {
@@ -660,9 +661,23 @@ func (dao *Dao) DeleteRecord(record *models.Record) error {
func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.Collection][]*schema.SchemaField) error {
uniqueJsonEachAlias := "__je__" + security.PseudorandomString(4)
for refCollection, fields := range refs {
if refCollection.IsView() {
continue // skip view collections
// @todo consider changing refs to a slice
//
// Sort the refs keys to ensure that the cascade events firing order is always the same.
// This is not necessary for the operation to function correctly but it helps having deterministic output during testing.
sortedRefKeys := make([]*models.Collection, 0, len(refs))
for k := range refs {
sortedRefKeys = append(sortedRefKeys, k)
}
sort.Slice(sortedRefKeys, func(i, j int) bool {
return sortedRefKeys[i].Name < sortedRefKeys[j].Name
})
for _, refCollection := range sortedRefKeys {
fields, ok := refs[refCollection]
if refCollection.IsView() || !ok {
continue // skip missing or view collections
}
for _, field := range fields {
+6
View File
@@ -14,6 +14,8 @@ import (
)
func TestExpandRecords(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -222,6 +224,8 @@ func TestExpandRecords(t *testing.T) {
}
func TestExpandRecord(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -372,6 +376,8 @@ func TestExpandRecord(t *testing.T) {
}
func TestIndirectExpandSingeVsArrayResult(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+4
View File
@@ -14,6 +14,8 @@ import (
)
func TestSyncRecordTableSchema(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -132,6 +134,8 @@ func TestSyncRecordTableSchema(t *testing.T) {
}
func TestSingleVsMultipleValuesNormalization(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+38
View File
@@ -19,6 +19,8 @@ import (
)
func TestRecordQueryWithDifferentCollectionValues(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -58,6 +60,8 @@ func TestRecordQueryWithDifferentCollectionValues(t *testing.T) {
}
func TestRecordQueryOneWithRecord(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -82,6 +86,8 @@ func TestRecordQueryOneWithRecord(t *testing.T) {
}
func TestRecordQueryAllWithRecordsSlices(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -143,6 +149,8 @@ func TestRecordQueryAllWithRecordsSlices(t *testing.T) {
}
func TestFindRecordById(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -203,6 +211,8 @@ func TestFindRecordById(t *testing.T) {
}
func TestFindRecordsByIds(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -294,6 +304,8 @@ func TestFindRecordsByIds(t *testing.T) {
}
func TestFindRecordsByExpr(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -364,6 +376,8 @@ func TestFindRecordsByExpr(t *testing.T) {
}
func TestFindFirstRecordByData(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -427,6 +441,8 @@ func TestFindFirstRecordByData(t *testing.T) {
}
func TestFindRecordsByFilter(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -566,6 +582,8 @@ func TestFindRecordsByFilter(t *testing.T) {
}
func TestFindFirstRecordByFilter(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -647,6 +665,8 @@ func TestFindFirstRecordByFilter(t *testing.T) {
}
func TestCanAccessRecord(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -814,6 +834,8 @@ func TestCanAccessRecord(t *testing.T) {
}
func TestIsRecordValueUnique(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -863,6 +885,8 @@ func TestIsRecordValueUnique(t *testing.T) {
}
func TestFindAuthRecordByToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -925,6 +949,8 @@ func TestFindAuthRecordByToken(t *testing.T) {
}
func TestFindAuthRecordByEmail(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -956,6 +982,8 @@ func TestFindAuthRecordByEmail(t *testing.T) {
}
func TestFindAuthRecordByUsername(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -988,6 +1016,8 @@ func TestFindAuthRecordByUsername(t *testing.T) {
}
func TestSuggestUniqueAuthRecordUsername(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -1025,6 +1055,8 @@ func TestSuggestUniqueAuthRecordUsername(t *testing.T) {
}
func TestSaveRecord(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -1058,6 +1090,8 @@ func TestSaveRecord(t *testing.T) {
}
func TestSaveRecordWithIdFromOtherCollection(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -1090,6 +1124,8 @@ func TestSaveRecordWithIdFromOtherCollection(t *testing.T) {
}
func TestDeleteRecord(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -1167,6 +1203,8 @@ func TestDeleteRecord(t *testing.T) {
}
func TestDeleteRecordBatchProcessing(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+2
View File
@@ -8,6 +8,8 @@ import (
)
func TestSaveAndFindSettings(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+12
View File
@@ -12,6 +12,8 @@ import (
)
func TestHasTable(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -36,6 +38,8 @@ func TestHasTable(t *testing.T) {
}
func TestTableColumns(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -64,6 +68,8 @@ func TestTableColumns(t *testing.T) {
}
func TestTableInfo(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -92,6 +98,8 @@ func TestTableInfo(t *testing.T) {
}
func TestDeleteTable(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -116,6 +124,8 @@ func TestDeleteTable(t *testing.T) {
}
func TestVacuum(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -141,6 +151,8 @@ func TestVacuum(t *testing.T) {
}
func TestTableIndexes(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+10
View File
@@ -33,6 +33,8 @@ func ensureNoTempViews(app core.App, t *testing.T) {
}
func TestDeleteView(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -60,6 +62,8 @@ func TestDeleteView(t *testing.T) {
}
func TestSaveView(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -180,6 +184,8 @@ func TestSaveView(t *testing.T) {
}
func TestCreateViewSchemaWithDiscardedNestedTransaction(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -196,6 +202,8 @@ func TestCreateViewSchemaWithDiscardedNestedTransaction(t *testing.T) {
}
func TestCreateViewSchema(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -486,6 +494,8 @@ func TestCreateViewSchema(t *testing.T) {
}
func TestFindRecordByViewFile(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+4
View File
@@ -10,6 +10,8 @@ import (
)
func TestAdminLoginValidateAndSubmit(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -51,6 +53,8 @@ func TestAdminLoginValidateAndSubmit(t *testing.T) {
}
func TestAdminLoginInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -11,6 +11,8 @@ import (
)
func TestAdminPasswordResetConfirmValidateAndSubmit(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -99,6 +101,8 @@ func TestAdminPasswordResetConfirmValidateAndSubmit(t *testing.T) {
}
func TestAdminPasswordResetConfirmInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -10,6 +10,8 @@ import (
)
func TestAdminPasswordResetRequestValidateAndSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -74,6 +76,8 @@ func TestAdminPasswordResetRequestValidateAndSubmit(t *testing.T) {
}
func TestAdminPasswordResetRequestInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
+8
View File
@@ -12,6 +12,8 @@ import (
)
func TestNewAdminUpsert(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -31,6 +33,8 @@ func TestNewAdminUpsert(t *testing.T) {
}
func TestAdminUpsertValidateAndSubmit(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -183,6 +187,8 @@ func TestAdminUpsertValidateAndSubmit(t *testing.T) {
}
func TestAdminUpsertSubmitInterceptors(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -231,6 +237,8 @@ func TestAdminUpsertSubmitInterceptors(t *testing.T) {
}
func TestAdminUpsertWithCustomId(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+2 -2
View File
@@ -12,7 +12,7 @@ import (
var privateKeyRegex = regexp.MustCompile(`(?m)-----BEGIN PRIVATE KEY----[\s\S]+-----END PRIVATE KEY-----`)
// AppleClientSecretCreate is a [models.Admin] upsert (create/update) form.
// AppleClientSecretCreate is a form struct to generate a new Apple Client Secret.
//
// Reference: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
type AppleClientSecretCreate struct {
@@ -33,7 +33,7 @@ type AppleClientSecretCreate struct {
// Usually wrapped within -----BEGIN PRIVATE KEY----- X -----END PRIVATE KEY-----.
PrivateKey string `form:"privateKey" json:"privateKey"`
// Duration specifies how long the generated JWT token should be considered valid.
// Duration specifies how long the generated JWT should be considered valid.
// The specified value must be in seconds and max 15777000 (~6months).
Duration int `form:"duration" json:"duration"`
}
+2
View File
@@ -15,6 +15,8 @@ import (
)
func TestAppleClientSecretCreateValidateAndSubmit(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+2
View File
@@ -10,6 +10,8 @@ import (
)
func TestBackupCreateValidateAndSubmit(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
backupName string
+2
View File
@@ -12,6 +12,8 @@ import (
)
func TestBackupUploadValidateAndSubmit(t *testing.T) {
t.Parallel()
var zb bytes.Buffer
zw := zip.NewWriter(&zb)
if err := zw.Close(); err != nil {
+8
View File
@@ -16,6 +16,8 @@ import (
)
func TestNewCollectionUpsert(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -88,6 +90,8 @@ func TestNewCollectionUpsert(t *testing.T) {
}
func TestCollectionUpsertValidateAndSubmit(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -654,6 +658,8 @@ func TestCollectionUpsertValidateAndSubmit(t *testing.T) {
}
func TestCollectionUpsertSubmitInterceptors(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -704,6 +710,8 @@ func TestCollectionUpsertSubmitInterceptors(t *testing.T) {
}
func TestCollectionUpsertWithCustomId(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+6
View File
@@ -11,6 +11,8 @@ import (
)
func TestCollectionsImportValidate(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -38,6 +40,8 @@ func TestCollectionsImportValidate(t *testing.T) {
}
func TestCollectionsImportSubmit(t *testing.T) {
t.Parallel()
totalCollections := 11
scenarios := []struct {
@@ -461,6 +465,8 @@ func TestCollectionsImportSubmit(t *testing.T) {
}
func TestCollectionsImportSubmitInterceptors(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+2
View File
@@ -8,6 +8,8 @@ import (
)
func TestRealtimeSubscribeValidate(t *testing.T) {
t.Parallel()
scenarios := []struct {
clientId string
expectError bool
@@ -13,6 +13,8 @@ import (
)
func TestRecordEmailChangeConfirmValidateAndSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -145,6 +147,8 @@ func TestRecordEmailChangeConfirmValidateAndSubmit(t *testing.T) {
}
func TestRecordEmailChangeConfirmInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -12,6 +12,8 @@ import (
)
func TestRecordEmailChangeRequestValidateAndSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -106,6 +108,8 @@ func TestRecordEmailChangeRequestValidateAndSubmit(t *testing.T) {
}
func TestRecordEmailChangeRequestInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
+2
View File
@@ -10,6 +10,8 @@ import (
)
func TestUserOauth2LoginValidate(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+4
View File
@@ -10,6 +10,8 @@ import (
)
func TestRecordPasswordLoginValidateAndSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -132,6 +134,8 @@ func TestRecordPasswordLoginValidateAndSubmit(t *testing.T) {
}
func TestRecordPasswordLoginInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -13,6 +13,8 @@ import (
)
func TestRecordPasswordResetConfirmValidateAndSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -136,6 +138,8 @@ func TestRecordPasswordResetConfirmValidateAndSubmit(t *testing.T) {
}
func TestRecordPasswordResetConfirmInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -13,6 +13,8 @@ import (
)
func TestRecordPasswordResetRequestSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -116,6 +118,8 @@ func TestRecordPasswordResetRequestSubmit(t *testing.T) {
}
func TestRecordPasswordResetRequestInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -12,6 +12,8 @@ import (
)
func TestRecordVerificationConfirmValidateAndSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -98,6 +100,8 @@ func TestRecordVerificationConfirmValidateAndSubmit(t *testing.T) {
}
func TestRecordVerificationConfirmInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -13,6 +13,8 @@ import (
)
func TestRecordVerificationRequestSubmit(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -134,6 +136,8 @@ func TestRecordVerificationRequestSubmit(t *testing.T) {
}
func TestRecordVerificationRequestInterceptors(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
+6
View File
@@ -14,6 +14,8 @@ import (
)
func TestNewSettingsUpsert(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -30,6 +32,8 @@ func TestNewSettingsUpsert(t *testing.T) {
}
func TestSettingsUpsertValidateAndSubmit(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -127,6 +131,8 @@ func TestSettingsUpsertValidateAndSubmit(t *testing.T) {
}
func TestSettingsUpsertSubmitInterceptors(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+2
View File
@@ -10,6 +10,8 @@ import (
)
func TestEmailSendValidateAndSubmit(t *testing.T) {
t.Parallel()
scenarios := []struct {
template string
email string
+4
View File
@@ -9,6 +9,8 @@ import (
)
func TestS3FilesystemValidate(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -66,6 +68,8 @@ func TestS3FilesystemValidate(t *testing.T) {
}
func TestS3FilesystemSubmitFailure(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+4
View File
@@ -12,6 +12,8 @@ import (
)
func TestUploadedFileSize(t *testing.T) {
t.Parallel()
data, mp, err := tests.MockMultipartData(nil, "test")
if err != nil {
t.Fatal(err)
@@ -52,6 +54,8 @@ func TestUploadedFileSize(t *testing.T) {
}
func TestUploadedFileMimeType(t *testing.T) {
t.Parallel()
data, mp, err := tests.MockMultipartData(nil, "test")
if err != nil {
t.Fatal(err)
+2
View File
@@ -8,6 +8,8 @@ import (
)
func TestUniqueId(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+2
View File
@@ -45,6 +45,8 @@ func TestRecordDataValidatorEmptyAndUnknown(t *testing.T) {
}
func TestRecordDataValidatorValidateText(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+2
View File
@@ -7,6 +7,8 @@ import (
)
func TestCompare(t *testing.T) {
t.Parallel()
scenarios := []struct {
valA string
valB string
+2
View File
@@ -9,6 +9,8 @@ import (
)
func TestSendAdminPasswordReset(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
+6
View File
@@ -9,6 +9,8 @@ import (
)
func TestSendRecordPasswordReset(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -37,6 +39,8 @@ func TestSendRecordPasswordReset(t *testing.T) {
}
func TestSendRecordVerification(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
@@ -62,6 +66,8 @@ func TestSendRecordVerification(t *testing.T) {
}
func TestSendRecordChangeEmail(t *testing.T) {
t.Parallel()
testApp, _ := tests.NewTestApp()
defer testApp.Cleanup()
+8
View File
@@ -8,6 +8,8 @@ import (
)
func TestAdminTableName(t *testing.T) {
t.Parallel()
m := models.Admin{}
if m.TableName() != "_admins" {
t.Fatalf("Unexpected table name, got %q", m.TableName())
@@ -15,6 +17,8 @@ func TestAdminTableName(t *testing.T) {
}
func TestAdminValidatePassword(t *testing.T) {
t.Parallel()
scenarios := []struct {
admin models.Admin
password string
@@ -61,6 +65,8 @@ func TestAdminValidatePassword(t *testing.T) {
}
func TestAdminSetPassword(t *testing.T) {
t.Parallel()
m := models.Admin{
// 123456
PasswordHash: "$2a$10$SKk/Y/Yc925PBtsSYBvq3Ous9Jy18m4KTn6b/PQQ.Y9QVjy3o/Fv.",
@@ -93,6 +99,8 @@ func TestAdminSetPassword(t *testing.T) {
}
func TestAdminRefreshTokenKey(t *testing.T) {
t.Parallel()
m := models.Admin{TokenKey: "test"}
m.RefreshTokenKey()
+10
View File
@@ -7,6 +7,8 @@ import (
)
func TestBaseModelHasId(t *testing.T) {
t.Parallel()
scenarios := []struct {
model models.BaseModel
expected bool
@@ -34,6 +36,8 @@ func TestBaseModelHasId(t *testing.T) {
}
func TestBaseModelId(t *testing.T) {
t.Parallel()
m := models.BaseModel{}
if m.GetId() != "" {
@@ -54,6 +58,8 @@ func TestBaseModelId(t *testing.T) {
}
func TestBaseModelIsNew(t *testing.T) {
t.Parallel()
m0 := models.BaseModel{}
m1 := models.BaseModel{Id: ""}
m2 := models.BaseModel{Id: "test"}
@@ -96,6 +102,8 @@ func TestBaseModelIsNew(t *testing.T) {
}
func TestBaseModelCreated(t *testing.T) {
t.Parallel()
m := models.BaseModel{}
if !m.GetCreated().IsZero() {
@@ -110,6 +118,8 @@ func TestBaseModelCreated(t *testing.T) {
}
func TestBaseModelUpdated(t *testing.T) {
t.Parallel()
m := models.BaseModel{}
if !m.GetUpdated().IsZero() {
+28
View File
@@ -11,6 +11,8 @@ import (
)
func TestCollectionTableName(t *testing.T) {
t.Parallel()
m := models.Collection{}
if m.TableName() != "_collections" {
t.Fatalf("Unexpected table name, got %q", m.TableName())
@@ -18,6 +20,8 @@ func TestCollectionTableName(t *testing.T) {
}
func TestCollectionBaseFilesPath(t *testing.T) {
t.Parallel()
m := models.Collection{}
m.RefreshId()
@@ -29,6 +33,8 @@ func TestCollectionBaseFilesPath(t *testing.T) {
}
func TestCollectionIsBase(t *testing.T) {
t.Parallel()
scenarios := []struct {
collection models.Collection
expected bool
@@ -48,6 +54,8 @@ func TestCollectionIsBase(t *testing.T) {
}
func TestCollectionIsAuth(t *testing.T) {
t.Parallel()
scenarios := []struct {
collection models.Collection
expected bool
@@ -67,6 +75,8 @@ func TestCollectionIsAuth(t *testing.T) {
}
func TestCollectionMarshalJSON(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
collection models.Collection
@@ -109,6 +119,8 @@ func TestCollectionMarshalJSON(t *testing.T) {
}
func TestCollectionBaseOptions(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
collection models.Collection
@@ -153,6 +165,8 @@ func TestCollectionBaseOptions(t *testing.T) {
}
func TestCollectionAuthOptions(t *testing.T) {
t.Parallel()
options := types.JsonMap{"test": 123, "minPasswordLength": 4}
expectedSerialization := `{"manageRule":null,"allowOAuth2Auth":false,"allowUsernameAuth":false,"allowEmailAuth":false,"requireEmail":false,"exceptEmailDomains":null,"onlyVerified":false,"onlyEmailDomains":null,"minPasswordLength":4}`
@@ -200,6 +214,8 @@ func TestCollectionAuthOptions(t *testing.T) {
}
func TestCollectionViewOptions(t *testing.T) {
t.Parallel()
options := types.JsonMap{"query": "select id from demo1", "minPasswordLength": 4}
expectedSerialization := `{"query":"select id from demo1"}`
@@ -247,6 +263,8 @@ func TestCollectionViewOptions(t *testing.T) {
}
func TestNormalizeOptions(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
collection models.Collection
@@ -288,6 +306,8 @@ func TestNormalizeOptions(t *testing.T) {
}
func TestDecodeOptions(t *testing.T) {
t.Parallel()
m := models.Collection{
Options: types.JsonMap{"test": 123},
}
@@ -306,6 +326,8 @@ func TestDecodeOptions(t *testing.T) {
}
func TestSetOptions(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
collection models.Collection
@@ -357,6 +379,8 @@ func TestSetOptions(t *testing.T) {
}
func TestCollectionBaseOptionsValidate(t *testing.T) {
t.Parallel()
opt := models.CollectionBaseOptions{}
if err := opt.Validate(); err != nil {
t.Fatal(err)
@@ -364,6 +388,8 @@ func TestCollectionBaseOptionsValidate(t *testing.T) {
}
func TestCollectionAuthOptionsValidate(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
options models.CollectionAuthOptions
@@ -451,6 +477,8 @@ func TestCollectionAuthOptionsValidate(t *testing.T) {
}
func TestCollectionViewOptionsValidate(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
options models.CollectionViewOptions
+2
View File
@@ -7,6 +7,8 @@ import (
)
func TestExternalAuthTableName(t *testing.T) {
t.Parallel()
m := models.ExternalAuth{}
if m.TableName() != "_externalAuths" {
t.Fatalf("Unexpected table name, got %q", m.TableName())
+2
View File
@@ -7,6 +7,8 @@ import (
)
func TestParamTableName(t *testing.T) {
t.Parallel()
m := models.Param{}
if m.TableName() != "_params" {
t.Fatalf("Unexpected table name, got %q", m.TableName())
+82
View File
@@ -15,6 +15,8 @@ import (
)
func TestNewRecord(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "test_collection",
Schema: schema.NewSchema(
@@ -37,6 +39,8 @@ func TestNewRecord(t *testing.T) {
}
func TestNewRecordFromNullStringMap(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "test",
Schema: schema.NewSchema(
@@ -200,6 +204,8 @@ func TestNewRecordFromNullStringMap(t *testing.T) {
}
func TestNewRecordsFromNullStringMaps(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "test",
Schema: schema.NewSchema(
@@ -310,6 +316,8 @@ func TestNewRecordsFromNullStringMaps(t *testing.T) {
}
func TestRecordTableName(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
collection.Name = "test"
collection.RefreshId()
@@ -322,6 +330,8 @@ func TestRecordTableName(t *testing.T) {
}
func TestRecordCollection(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
collection.RefreshId()
@@ -333,6 +343,8 @@ func TestRecordCollection(t *testing.T) {
}
func TestRecordOriginalCopy(t *testing.T) {
t.Parallel()
m := models.NewRecord(&models.Collection{})
m.Load(map[string]any{"f": "123"})
@@ -360,6 +372,8 @@ func TestRecordOriginalCopy(t *testing.T) {
}
func TestRecordCleanCopy(t *testing.T) {
t.Parallel()
m := models.NewRecord(&models.Collection{
Name: "cname",
Type: models.CollectionTypeAuth,
@@ -392,6 +406,8 @@ func TestRecordCleanCopy(t *testing.T) {
}
func TestRecordSetAndGetExpand(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
m := models.NewRecord(collection)
@@ -409,6 +425,8 @@ func TestRecordSetAndGetExpand(t *testing.T) {
}
func TestRecordMergeExpand(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
m := models.NewRecord(collection)
m.Id = "m"
@@ -501,6 +519,8 @@ func TestRecordMergeExpand(t *testing.T) {
}
func TestRecordMergeExpandNilCheck(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
scenarios := []struct {
@@ -542,6 +562,8 @@ func TestRecordMergeExpandNilCheck(t *testing.T) {
}
func TestRecordExpandedRel(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
main := models.NewRecord(collection)
@@ -574,6 +596,8 @@ func TestRecordExpandedRel(t *testing.T) {
}
func TestRecordExpandedAll(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
main := models.NewRecord(collection)
@@ -606,6 +630,8 @@ func TestRecordExpandedAll(t *testing.T) {
}
func TestRecordSchemaData(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Type: models.CollectionTypeAuth,
Schema: schema.NewSchema(
@@ -639,6 +665,8 @@ func TestRecordSchemaData(t *testing.T) {
}
func TestRecordUnknownData(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
@@ -719,6 +747,8 @@ func TestRecordUnknownData(t *testing.T) {
}
func TestRecordSetAndGet(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
@@ -799,6 +829,8 @@ func TestRecordSetAndGet(t *testing.T) {
}
func TestRecordGetBool(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected bool
@@ -830,6 +862,8 @@ func TestRecordGetBool(t *testing.T) {
}
func TestRecordGetString(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected string
@@ -860,6 +894,8 @@ func TestRecordGetString(t *testing.T) {
}
func TestRecordGetInt(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected int
@@ -892,6 +928,8 @@ func TestRecordGetInt(t *testing.T) {
}
func TestRecordGetFloat(t *testing.T) {
t.Parallel()
scenarios := []struct {
value any
expected float64
@@ -924,6 +962,8 @@ func TestRecordGetFloat(t *testing.T) {
}
func TestRecordGetTime(t *testing.T) {
t.Parallel()
nowTime := time.Now()
testTime, _ := time.Parse(types.DefaultDateLayout, "2022-01-01 08:00:40.000Z")
@@ -957,6 +997,8 @@ func TestRecordGetTime(t *testing.T) {
}
func TestRecordGetDateTime(t *testing.T) {
t.Parallel()
nowTime := time.Now()
testTime, _ := time.Parse(types.DefaultDateLayout, "2022-01-01 08:00:40.000Z")
@@ -990,6 +1032,8 @@ func TestRecordGetDateTime(t *testing.T) {
}
func TestRecordGetStringSlice(t *testing.T) {
t.Parallel()
nowTime := time.Now()
scenarios := []struct {
@@ -1031,6 +1075,8 @@ func TestRecordGetStringSlice(t *testing.T) {
}
func TestRecordUnmarshalJSONField(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(&schema.SchemaField{
Name: "field",
@@ -1086,6 +1132,8 @@ func TestRecordUnmarshalJSONField(t *testing.T) {
}
func TestRecordBaseFilesPath(t *testing.T) {
t.Parallel()
collection := &models.Collection{}
collection.RefreshId()
collection.Name = "test"
@@ -1102,6 +1150,8 @@ func TestRecordBaseFilesPath(t *testing.T) {
}
func TestRecordFindFileFieldByFile(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
@@ -1159,6 +1209,8 @@ func TestRecordFindFileFieldByFile(t *testing.T) {
}
func TestRecordLoadAndData(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
@@ -1232,6 +1284,8 @@ func TestRecordLoadAndData(t *testing.T) {
}
func TestRecordColumnValueMap(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
@@ -1319,6 +1373,8 @@ func TestRecordColumnValueMap(t *testing.T) {
}
func TestRecordPublicExportAndMarshalJSON(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Name: "c_name",
Schema: schema.NewSchema(
@@ -1463,6 +1519,8 @@ func TestRecordPublicExportAndMarshalJSON(t *testing.T) {
}
func TestRecordUnmarshalJSON(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
@@ -1551,6 +1609,8 @@ func TestRecordUnmarshalJSON(t *testing.T) {
}
func TestRecordReplaceModifers(t *testing.T) {
t.Parallel()
collection := &models.Collection{
Schema: schema.NewSchema(
&schema.SchemaField{
@@ -1658,6 +1718,8 @@ func TestRecordReplaceModifers(t *testing.T) {
// -------------------------------------------------------------------
func TestRecordUsername(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
@@ -1699,6 +1761,8 @@ func TestRecordUsername(t *testing.T) {
}
func TestRecordEmail(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
@@ -1740,6 +1804,8 @@ func TestRecordEmail(t *testing.T) {
}
func TestRecordEmailVisibility(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
value bool
@@ -1782,6 +1848,8 @@ func TestRecordEmailVisibility(t *testing.T) {
}
func TestRecordEmailVerified(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
value bool
@@ -1824,6 +1892,8 @@ func TestRecordEmailVerified(t *testing.T) {
}
func TestRecordTokenKey(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
@@ -1865,6 +1935,8 @@ func TestRecordTokenKey(t *testing.T) {
}
func TestRecordRefreshTokenKey(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
@@ -1904,6 +1976,8 @@ func TestRecordRefreshTokenKey(t *testing.T) {
}
func TestRecordLastResetSentAt(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
@@ -1948,6 +2022,8 @@ func TestRecordLastResetSentAt(t *testing.T) {
}
func TestRecordLastVerificationSentAt(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
expectError bool
@@ -1992,6 +2068,8 @@ func TestRecordLastVerificationSentAt(t *testing.T) {
}
func TestRecordPasswordHash(t *testing.T) {
t.Parallel()
m := models.NewRecord(&models.Collection{})
if v := m.PasswordHash(); v != "" {
@@ -2006,6 +2084,8 @@ func TestRecordPasswordHash(t *testing.T) {
}
func TestRecordValidatePassword(t *testing.T) {
t.Parallel()
// 123456
hash := "$2a$10$YKU8mPP8sTE3xZrpuM.xQuq27KJ7aIJB2oUeKPsDDqZshbl5g5cDK"
@@ -2034,6 +2114,8 @@ func TestRecordValidatePassword(t *testing.T) {
}
func TestRecordSetPassword(t *testing.T) {
t.Parallel()
scenarios := []struct {
collectionType string
password string
+1
View File
@@ -11,6 +11,7 @@ const (
RequestAuthRecord = "authRecord"
)
// Deprecated: Replaced by the Log model and will be removed in a future version.
type Request struct {
BaseModel
+2
View File
@@ -7,6 +7,8 @@ import (
)
func TestRequestInfoHasModifierDataKeys(t *testing.T) {
t.Parallel()
scenarios := []struct {
name string
requestInfo *models.RequestInfo
+4
View File
@@ -299,6 +299,10 @@ func baseBinds(vm *goja.Runtime) {
return string(bodyBytes), nil
})
vm.Set("sleep", func(milliseconds int64) {
time.Sleep(time.Duration(milliseconds) * time.Millisecond)
})
vm.Set("arrayOf", func(model any) any {
mt := reflect.TypeOf(model)
st := reflect.SliceOf(mt)
+23 -1
View File
@@ -46,7 +46,29 @@ func TestBaseBindsCount(t *testing.T) {
vm := goja.New()
baseBinds(vm)
testBindsCount(vm, "this", 16, t)
testBindsCount(vm, "this", 17, t)
}
func TestBaseBindsSleep(t *testing.T) {
app, _ := tests.NewTestApp()
defer app.Cleanup()
vm := goja.New()
baseBinds(vm)
vm.Set("reader", strings.NewReader("test"))
start := time.Now()
_, err := vm.RunString(`
sleep(100);
`)
if err != nil {
t.Fatal(err)
}
lasted := time.Since(start).Milliseconds()
if lasted < 100 || lasted > 150 {
t.Fatalf("Expected to sleep for ~100ms, got %d", lasted)
}
}
func TestBaseBindsReaderToString(t *testing.T) {
File diff suppressed because it is too large. Load diff
+20
View File
@@ -1,12 +1,14 @@
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"time"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/plugins/jsvm"
@@ -209,6 +211,20 @@ declare var $template: template.Registry
*/
declare function readerToString(reader: any, maxBytes?: number): string;
/**
* sleep pauses the current goroutine for at least the specified user duration (in ms).
* A zero or negative duration returns immediately.
*
* Example:
*
* ` + "```" + `js
* sleep(250) // sleeps for 250ms
* ` + "```" + `
*
* @group PocketBase
*/
declare function sleep(milliseconds: number): void;
/**
* arrayOf creates a placeholder array of the specified models.
* Usually used to populate DB result into an array of models.
@@ -1095,6 +1111,10 @@ func main() {
result = strings.ReplaceAll(result, "ORIGINAL_CORE_APP", "core.App")
result = strings.ReplaceAll(result, "ORIGINAL_POCKETBASE", "pocketbase.PocketBase")
// prepend a timestamp with the generation time
// so that it can be compared without reading the entire file
result = fmt.Sprintf("// %d\n%s", time.Now().Unix(), result)
parentDir := filepath.Dir(filename)
typesFile := filepath.Join(parentDir, "generated", "types.d.ts")
+22 -9
View File
@@ -9,8 +9,10 @@
package jsvm
import (
"bytes"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
@@ -77,6 +79,9 @@ type Config struct {
// TypeScript declarations file.
//
// If not set it fallbacks to "pb_data".
//
// Note: Avoid using the same directory as the HooksDir when HooksWatch is enabled
// to prevent unnecessary app restarts when the types file is initially created.
TypesDir string
}
@@ -117,10 +122,9 @@ func Register(app core.App, config Config) error {
}
p.app.OnAfterBootstrap().Add(func(e *core.BootstrapEvent) error {
// always update the app types on start to ensure that
// the user has the latest generated declarations
if err := p.saveTypesFile(); err != nil {
color.Yellow("Unable to save app types file: %v", err)
// ensure that the user has the latest types declaration
if err := p.refreshTypesFile(); err != nil {
color.Yellow("Unable to refresh app types file: %v", err)
}
return nil
@@ -423,8 +427,8 @@ func (p *plugin) relativeTypesPath(basepath string) string {
return rel
}
// saveTypesFile saves the embedded TS declarations as a file on the disk.
func (p *plugin) saveTypesFile() error {
// refreshTypesFile saves the embedded TS declarations as a file on the disk.
func (p *plugin) refreshTypesFile() error {
fullPath := p.fullTypesPath()
// ensure that the types directory exists
@@ -439,11 +443,20 @@ func (p *plugin) saveTypesFile() error {
return err
}
if err := os.WriteFile(fullPath, data, 0644); err != nil {
return err
// read the first timestamp line of the old file (if exists) and compare it to the embedded one
// (note: ignore errors to allow always overwriting the file if it is invalid)
existingFile, err := os.Open(fullPath)
if err == nil {
timestamp := make([]byte, 13)
io.ReadFull(existingFile, timestamp)
existingFile.Close()
if len(data) >= len(timestamp) && bytes.Equal(data[:13], timestamp) {
return nil // nothing new to save
}
}
return nil
return os.WriteFile(fullPath, data, 0644)
}
// prependToEmptyFile prepends the specified text to an empty file.
+16 -1
View File
@@ -32,6 +32,7 @@ type appWrapper struct {
type PocketBase struct {
*appWrapper
devFlag bool
dataDirFlag string
encryptionEnvFlag string
hideStartBanner bool
@@ -43,6 +44,7 @@ type PocketBase struct {
// Config is the PocketBase initialization config struct.
type Config struct {
// optional default values for the console flags
DefaultDev bool
DefaultDataDir string // if not set, it will fallback to "./pb_data"
DefaultEncryptionEnv string
@@ -65,7 +67,11 @@ type Config struct {
// If you want to initialize the application before calling [Start()],
// then you'll have to manually call [Bootstrap()].
func New() *PocketBase {
return NewWithConfig(Config{})
_, isUsingGoRun := inspectRuntime()
return NewWithConfig(Config{
DefaultDev: isUsingGoRun,
})
}
// NewWithConfig creates a new PocketBase instance with the provided config.
@@ -95,6 +101,7 @@ func NewWithConfig(config Config) *PocketBase {
DisableDefaultCmd: true,
},
},
devFlag: config.DefaultDev,
dataDirFlag: config.DefaultDataDir,
encryptionEnvFlag: config.DefaultEncryptionEnv,
hideStartBanner: config.HideStartBanner,
@@ -109,6 +116,7 @@ func NewWithConfig(config Config) *PocketBase {
// initialize the app instance
pb.appWrapper = &appWrapper{core.NewBaseApp(core.BaseAppConfig{
IsDev: pb.devFlag,
DataDir: pb.dataDirFlag,
EncryptionEnv: pb.encryptionEnvFlag,
DataMaxOpenConns: config.DataMaxOpenConns,
@@ -191,6 +199,13 @@ func (pb *PocketBase) eagerParseFlags(config *Config) error {
"the env variable whose value of 32 characters will be used \nas encryption key for the app settings (default none)",
)
pb.RootCmd.PersistentFlags().BoolVar(
&pb.devFlag,
"dev",
config.DefaultDev,
"enable dev mode, aka. printing logs and sql statements to the console",
)
return pb.RootCmd.ParseFlags(os.Args[1:])
}
+12
View File
@@ -453,6 +453,17 @@ func (r *runner) processActiveProps() (*search.ResolverResult, error) {
result.MultiMatchSubQuery = r.multiMatch
}
// wrap in json_extract to ensure that top-level primitives
// stored as json work correctly when compared to their SQL equivalent
// (https://github.com/pocketbase/pocketbase/issues/4068)
if field.Type == schema.FieldTypeJson {
result.NoCoalesce = true
result.Identifier = "JSON_EXTRACT(" + result.Identifier + ", '$')"
if r.withMultiMatch {
r.multiMatch.valueIdentifier = "JSON_EXTRACT(" + r.multiMatch.valueIdentifier + ", '$')"
}
}
return result, nil
}
@@ -480,6 +491,7 @@ func (r *runner) processActiveProps() (*search.ResolverResult, error) {
}
result := &search.ResolverResult{
NoCoalesce: true,
Identifier: fmt.Sprintf(
"JSON_EXTRACT([[%s.%s]], '%s')",
r.activeTableAlias,
+15 -1
View File
@@ -301,7 +301,21 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
"demo4",
"json_object.a.b = '' && self_rel_many:length != 2 && json_object.a.b > 3 && self_rel_many:length <= 4",
false,
"SELECT `demo4`.* FROM `demo4` WHERE ((JSON_EXTRACT([[demo4.json_object]], '$.a.b') = '' OR JSON_EXTRACT([[demo4.json_object]], '$.a.b') IS NULL) AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE (CASE WHEN [[demo4.self_rel_many]] = '' OR [[demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4.self_rel_many]]) END) END) IS NOT {:TEST} AND JSON_EXTRACT([[demo4.json_object]], '$.a.b') > {:TEST} AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE (CASE WHEN [[demo4.self_rel_many]] = '' OR [[demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4.self_rel_many]]) END) END) <= {:TEST})",
"SELECT `demo4`.* FROM `demo4` WHERE (JSON_EXTRACT([[demo4.json_object]], '$.a.b') IS {:TEST} AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE (CASE WHEN [[demo4.self_rel_many]] = '' OR [[demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4.self_rel_many]]) END) END) IS NOT {:TEST} AND JSON_EXTRACT([[demo4.json_object]], '$.a.b') > {:TEST} AND json_array_length(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE (CASE WHEN [[demo4.self_rel_many]] = '' OR [[demo4.self_rel_many]] IS NULL THEN json_array() ELSE json_array([[demo4.self_rel_many]]) END) END) <= {:TEST})",
},
{
"json field equal normalization checks",
"demo4",
"json_object = '' || json_object != '' || '' = json_object || '' != json_object ||" +
"json_object = null || json_object != null || null = json_object || null != json_object ||" +
"json_object = true || json_object != true || true = json_object || true != json_object ||" +
"json_object = json_object || json_object != json_object ||" +
"json_object = title || title != json_object ||" +
// multimatch expressions
"self_rel_many.json_object = '' || null = self_rel_many.json_object ||" +
"self_rel_many.json_object = self_rel_many.json_object",
false,
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN json_each(CASE WHEN json_valid([[demo4.self_rel_many]]) THEN [[demo4.self_rel_many]] ELSE json_array([[demo4.self_rel_many]]) END) `demo4_self_rel_many_je` LEFT JOIN `demo4` `demo4_self_rel_many` ON [[demo4_self_rel_many.id]] = [[demo4_self_rel_many_je.value]] WHERE (JSON_EXTRACT([[demo4.json_object]], '$') IS {:TEST} OR JSON_EXTRACT([[demo4.json_object]], '$') IS NOT {:TEST} OR {:TEST} IS JSON_EXTRACT([[demo4.json_object]], '$') OR {:TEST} IS NOT JSON_EXTRACT([[demo4.json_object]], '$') OR JSON_EXTRACT([[demo4.json_object]], '$') IS NULL OR JSON_EXTRACT([[demo4.json_object]], '$') IS NOT NULL OR NULL IS JSON_EXTRACT([[demo4.json_object]], '$') OR NULL IS NOT JSON_EXTRACT([[demo4.json_object]], '$') OR JSON_EXTRACT([[demo4.json_object]], '$') IS 1 OR JSON_EXTRACT([[demo4.json_object]], '$') IS NOT 1 OR 1 IS JSON_EXTRACT([[demo4.json_object]], '$') OR 1 IS NOT JSON_EXTRACT([[demo4.json_object]], '$') OR JSON_EXTRACT([[demo4.json_object]], '$') IS JSON_EXTRACT([[demo4.json_object]], '$') OR JSON_EXTRACT([[demo4.json_object]], '$') IS NOT JSON_EXTRACT([[demo4.json_object]], '$') OR JSON_EXTRACT([[demo4.json_object]], '$') IS [[demo4.title]] OR [[demo4.title]] IS NOT JSON_EXTRACT([[demo4.json_object]], '$') OR ((JSON_EXTRACT([[demo4_self_rel_many.json_object]], '$') IS {:TEST}) AND (NOT EXISTS (SELECT 1 FROM (SELECT JSON_EXTRACT([[__mm_demo4_self_rel_many.json_object]], '$') as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN json_each(CASE WHEN json_valid([[__mm_demo4.self_rel_many]]) THEN [[__mm_demo4.self_rel_many]] ELSE json_array([[__mm_demo4.self_rel_many]]) END) `__mm_demo4_self_rel_many_je` LEFT JOIN `demo4` `__mm_demo4_self_rel_many` ON [[__mm_demo4_self_rel_many.id]] = [[__mm_demo4_self_rel_many_je.value]] WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__smTEST}} WHERE NOT ([[__smTEST.multiMatchValue]] IS {:TEST})))) OR ((NULL IS JSON_EXTRACT([[demo4_self_rel_many.json_object]], '$')) AND (NOT EXISTS (SELECT 1 FROM (SELECT JSON_EXTRACT([[__mm_demo4_self_rel_many.json_object]], '$') as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN json_each(CASE WHEN json_valid([[__mm_demo4.self_rel_many]]) THEN [[__mm_demo4.self_rel_many]] ELSE json_array([[__mm_demo4.self_rel_many]]) END) `__mm_demo4_self_rel_many_je` LEFT JOIN `demo4` `__mm_demo4_self_rel_many` ON [[__mm_demo4_self_rel_many.id]] = [[__mm_demo4_self_rel_many_je.value]] WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__smTEST}} WHERE NOT (NULL IS [[__smTEST.multiMatchValue]])))) OR ((JSON_EXTRACT([[demo4_self_rel_many.json_object]], '$') IS JSON_EXTRACT([[demo4_self_rel_many.json_object]], '$')) AND (NOT EXISTS (SELECT 1 FROM (SELECT JSON_EXTRACT([[__mm_demo4_self_rel_many.json_object]], '$') as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN json_each(CASE WHEN json_valid([[__mm_demo4.self_rel_many]]) THEN [[__mm_demo4.self_rel_many]] ELSE json_array([[__mm_demo4.self_rel_many]]) END) `__mm_demo4_self_rel_many_je` LEFT JOIN `demo4` `__mm_demo4_self_rel_many` ON [[__mm_demo4_self_rel_many.id]] = [[__mm_demo4_self_rel_many_je.value]] WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__mlTEST}} LEFT JOIN (SELECT JSON_EXTRACT([[__mm_demo4_self_rel_many.json_object]], '$') as [[multiMatchValue]] FROM `demo4` `__mm_demo4` LEFT JOIN json_each(CASE WHEN json_valid([[__mm_demo4.self_rel_many]]) THEN [[__mm_demo4.self_rel_many]] ELSE json_array([[__mm_demo4.self_rel_many]]) END) `__mm_demo4_self_rel_many_je` LEFT JOIN `demo4` `__mm_demo4_self_rel_many` ON [[__mm_demo4_self_rel_many.id]] = [[__mm_demo4_self_rel_many_je.value]] WHERE `__mm_demo4`.`id` = `demo4`.`id`) {{__mrTEST}} WHERE NOT ([[__mlTEST.multiMatchValue]] IS [[__mrTEST.multiMatchValue]])))))",
},
}
+16 -3
View File
@@ -29,6 +29,11 @@ type ApiScenario struct {
// to ensure that all fired non-awaited go routines have finished
Delay time.Duration
// Timeout specifies how long to wait before cancelling the request context.
//
// A zero or negative value means that there will be no timeout.
Timeout time.Duration
// expectations
// ---
ExpectedStatus int
@@ -90,9 +95,17 @@ func (scenario *ApiScenario) test(t *testing.T) {
// add middleware to timeout long-running requests (eg. keep-alive routes)
e.Pre(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctx, cancelFunc := context.WithTimeout(c.Request().Context(), 100*time.Millisecond)
defer cancelFunc()
c.SetRequest(c.Request().Clone(ctx))
slowTimer := time.AfterFunc(3*time.Second, func() {
t.Logf("[WARN] Long running test %q", scenario.Name)
})
defer slowTimer.Stop()
if scenario.Timeout > 0 {
ctx, cancelFunc := context.WithTimeout(c.Request().Context(), scenario.Timeout)
defer cancelFunc()
c.SetRequest(c.Request().Clone(ctx))
}
return next(c)
}
})
Binary file not shown.
+6
View File
@@ -8,6 +8,8 @@ import (
)
func TestNewAdminAuthToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -31,6 +33,8 @@ func TestNewAdminAuthToken(t *testing.T) {
}
func TestNewAdminResetPasswordToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -54,6 +58,8 @@ func TestNewAdminResetPasswordToken(t *testing.T) {
}
func TestNewAdminFileToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+10
View File
@@ -8,6 +8,8 @@ import (
)
func TestNewRecordAuthToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -31,6 +33,8 @@ func TestNewRecordAuthToken(t *testing.T) {
}
func TestNewRecordVerifyToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -54,6 +58,8 @@ func TestNewRecordVerifyToken(t *testing.T) {
}
func TestNewRecordResetPasswordToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -77,6 +83,8 @@ func TestNewRecordResetPasswordToken(t *testing.T) {
}
func TestNewRecordChangeEmailToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
@@ -100,6 +108,8 @@ func TestNewRecordChangeEmailToken(t *testing.T) {
}
func TestNewRecordFileToken(t *testing.T) {
t.Parallel()
app, _ := tests.NewTestApp()
defer app.Cleanup()
+16
View File
@@ -7,6 +7,8 @@ import (
)
func TestCronNew(t *testing.T) {
t.Parallel()
c := New()
expectedInterval := 1 * time.Minute
@@ -29,6 +31,8 @@ func TestCronNew(t *testing.T) {
}
func TestCronSetInterval(t *testing.T) {
t.Parallel()
c := New()
interval := 2 * time.Minute
@@ -41,6 +45,8 @@ func TestCronSetInterval(t *testing.T) {
}
func TestCronSetTimezone(t *testing.T) {
t.Parallel()
c := New()
timezone, _ := time.LoadLocation("Asia/Tokyo")
@@ -53,6 +59,8 @@ func TestCronSetTimezone(t *testing.T) {
}
func TestCronAddAndRemove(t *testing.T) {
t.Parallel()
c := New()
if err := c.Add("test0", "* * * * *", nil); err == nil {
@@ -126,6 +134,8 @@ func TestCronAddAndRemove(t *testing.T) {
}
func TestCronMustAdd(t *testing.T) {
t.Parallel()
c := New()
defer func() {
@@ -144,6 +154,8 @@ func TestCronMustAdd(t *testing.T) {
}
func TestCronRemoveAll(t *testing.T) {
t.Parallel()
c := New()
if err := c.Add("test1", "* * * * *", func() {}); err != nil {
@@ -170,6 +182,8 @@ func TestCronRemoveAll(t *testing.T) {
}
func TestCronTotal(t *testing.T) {
t.Parallel()
c := New()
if v := c.Total(); v != 0 {
@@ -195,6 +209,8 @@ func TestCronTotal(t *testing.T) {
}
func TestCronStartStop(t *testing.T) {
t.Parallel()
c := New()
c.SetInterval(1 * time.Second)
+6
View File
@@ -9,6 +9,8 @@ import (
)
func TestNewMoment(t *testing.T) {
t.Parallel()
date, err := time.Parse("2006-01-02 15:04", "2023-05-09 15:20")
if err != nil {
t.Fatal(err)
@@ -38,6 +40,8 @@ func TestNewMoment(t *testing.T) {
}
func TestNewSchedule(t *testing.T) {
t.Parallel()
scenarios := []struct {
cronExpr string
expectError bool
@@ -272,6 +276,8 @@ func TestNewSchedule(t *testing.T) {
}
func TestScheduleIsDue(t *testing.T) {
t.Parallel()
scenarios := []struct {
cronExpr string
moment *cron.Moment
+30 -12
View File
@@ -184,14 +184,15 @@ func buildResolversExpr(
if !isAnyMatchOp(op) {
if left.MultiMatchSubQuery != nil && right.MultiMatchSubQuery != nil {
mm := &manyVsManyExpr{
leftSubQuery: left.MultiMatchSubQuery,
rightSubQuery: right.MultiMatchSubQuery,
op: op,
left: left,
right: right,
op: op,
}
expr = dbx.Enclose(dbx.And(expr, mm))
} else if left.MultiMatchSubQuery != nil {
mm := &manyVsOneExpr{
noCoalesce: left.NoCoalesce,
subQuery: left.MultiMatchSubQuery,
op: op,
otherOperand: right,
@@ -200,6 +201,7 @@ func buildResolversExpr(
expr = dbx.Enclose(dbx.And(expr, mm))
} else if right.MultiMatchSubQuery != nil {
mm := &manyVsOneExpr{
noCoalesce: right.NoCoalesce,
subQuery: right.MultiMatchSubQuery,
op: op,
otherOperand: left,
@@ -290,17 +292,29 @@ func resolveEqualExpr(equal bool, left, right *ResolverResult) dbx.Expression {
isRightEmpty := isEmptyIdentifier(right) || (len(right.Params) == 1 && hasEmptyParamValue(right))
equalOp := "="
nullEqualOp := "IS"
concatOp := "OR"
nullExpr := "IS NULL"
if !equal {
// use `IS NOT` instead of `!=` because direct non-equal comparisons
// always use `IS NOT` instead of `!=` because direct non-equal comparisons
// to nullable column values that are actually NULL yields to NULL instead of TRUE, eg.:
// `'example' != nullableColumn` -> NULL even if nullableColumn row value is NULL
equalOp = "IS NOT"
nullEqualOp = equalOp
concatOp = "AND"
nullExpr = "IS NOT NULL"
}
// no coalesce (eg. compare to a json field)
// a IS b
// a IS NOT b
if left.NoCoalesce || right.NoCoalesce {
return dbx.NewExp(
fmt.Sprintf("%s %s %s", left.Identifier, nullEqualOp, right.Identifier),
mergeParams(left.Params, right.Params),
)
}
// both operands are empty
if isLeftEmpty && isRightEmpty {
return dbx.NewExp(fmt.Sprintf("'' %s ''", equalOp), mergeParams(left.Params, right.Params))
@@ -459,8 +473,8 @@ var _ dbx.Expression = (*concatExpr)(nil)
// concatExpr defines an expression that concatenates multiple
// other expressions with a specified separator.
type concatExpr struct {
parts []dbx.Expression
separator string
parts []dbx.Expression
}
// Build converts the expression into a SQL fragment.
@@ -503,16 +517,16 @@ var _ dbx.Expression = (*manyVsManyExpr)(nil)
// Expects leftSubQuery and rightSubQuery to return a subquery with a
// single "multiMatchValue" column.
type manyVsManyExpr struct {
leftSubQuery dbx.Expression
rightSubQuery dbx.Expression
op fexpr.SignOp
left *ResolverResult
right *ResolverResult
op fexpr.SignOp
}
// Build converts the expression into a SQL fragment.
//
// Implements [dbx.Expression] interface.
func (e *manyVsManyExpr) Build(db *dbx.DB, params dbx.Params) string {
if e.leftSubQuery == nil || e.rightSubQuery == nil {
if e.left.MultiMatchSubQuery == nil || e.right.MultiMatchSubQuery == nil {
return "0=1"
}
@@ -521,10 +535,12 @@ func (e *manyVsManyExpr) Build(db *dbx.DB, params dbx.Params) string {
whereExpr, buildErr := buildResolversExpr(
&ResolverResult{
NoCoalesce: e.left.NoCoalesce,
Identifier: "[[" + lAlias + ".multiMatchValue]]",
},
e.op,
&ResolverResult{
NoCoalesce: e.right.NoCoalesce,
Identifier: "[[" + rAlias + ".multiMatchValue]]",
// note: the AfterBuild needs to be handled only once and it
// doesn't matter whether it is applied on the left or right subquery operand
@@ -538,9 +554,9 @@ func (e *manyVsManyExpr) Build(db *dbx.DB, params dbx.Params) string {
return fmt.Sprintf(
"NOT EXISTS (SELECT 1 FROM (%s) {{%s}} LEFT JOIN (%s) {{%s}} WHERE %s)",
e.leftSubQuery.Build(db, params),
e.left.MultiMatchSubQuery.Build(db, params),
lAlias,
e.rightSubQuery.Build(db, params),
e.right.MultiMatchSubQuery.Build(db, params),
rAlias,
whereExpr.Build(db, params),
)
@@ -556,10 +572,11 @@ var _ dbx.Expression = (*manyVsOneExpr)(nil)
//
// You can set inverse=false to reverse the condition sides (aka. one<->many).
type manyVsOneExpr struct {
otherOperand *ResolverResult
subQuery dbx.Expression
op fexpr.SignOp
otherOperand *ResolverResult
inverse bool
noCoalesce bool
}
// Build converts the expression into a SQL fragment.
@@ -573,6 +590,7 @@ func (e *manyVsOneExpr) Build(db *dbx.DB, params dbx.Params) string {
alias := "__sm" + security.PseudorandomString(5)
r1 := &ResolverResult{
NoCoalesce: e.noCoalesce,
Identifier: "[[" + alias + ".multiMatchValue]]",
AfterBuild: multiMatchAfterBuildFunc(e.op, alias),
}
+7 -1
View File
@@ -13,7 +13,7 @@ import (
)
func TestFilterDataBuildExpr(t *testing.T) {
resolver := search.NewSimpleFieldResolver("test1", "test2", "test3", `^test4_\w+$`)
resolver := search.NewSimpleFieldResolver("test1", "test2", "test3", `^test4_\w+$`, `^test5\.[\w\.\:]*\w+$`)
scenarios := []struct {
name string
@@ -93,6 +93,12 @@ func TestFilterDataBuildExpr(t *testing.T) {
false,
"[[test1]] NOT LIKE {:TEST} ESCAPE '\\'",
},
{
"nested json no coalesce",
"test5.a = test5.b || test5.c != test5.d",
false,
"(JSON_EXTRACT([[test5]], '$.a') IS JSON_EXTRACT([[test5]], '$.b') OR JSON_EXTRACT([[test5]], '$.c') IS NOT JSON_EXTRACT([[test5]], '$.d'))",
},
{
"macros",
`
+5
View File
@@ -16,6 +16,10 @@ type ResolverResult struct {
// in the final db expression as left or right operand.
Identifier string
// NoCoalesce instructs to not use COALESCE or NULL fallbacks
// when building the identifier expression.
NoCoalesce bool
// Params is a map with db placeholder->value pairs that will be added
// to the query when building both resolved operands/sides in a single expression.
Params dbx.Params
@@ -99,6 +103,7 @@ func (r *SimpleFieldResolver) Resolve(field string) (*ResolverResult, error) {
}
return &ResolverResult{
NoCoalesce: true,
Identifier: fmt.Sprintf(
"JSON_EXTRACT([[%s]], '%s')",
inflector.Columnify(parts[0]),
+4 -4
View File
@@ -7,7 +7,7 @@ import (
"github.com/golang-jwt/jwt/v4"
)
// ParseUnverifiedJWT parses JWT token and returns its claims
// ParseUnverifiedJWT parses JWT and returns its claims
// but DOES NOT verify the signature.
//
// It verifies only the exp, iat and nbf claims.
@@ -24,7 +24,7 @@ func ParseUnverifiedJWT(token string) (jwt.MapClaims, error) {
return claims, err
}
// ParseJWT verifies and parses JWT token and returns its claims.
// ParseJWT verifies and parses JWT and returns its claims.
func ParseJWT(token string, verificationKey string) (jwt.MapClaims, error) {
parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"}))
@@ -42,7 +42,7 @@ func ParseJWT(token string, verificationKey string) (jwt.MapClaims, error) {
return nil, errors.New("Unable to parse token.")
}
// NewJWT generates and returns new HS256 signed JWT token.
// NewJWT generates and returns new HS256 signed JWT.
func NewJWT(payload jwt.MapClaims, signingKey string, secondsDuration int64) (string, error) {
seconds := time.Duration(secondsDuration) * time.Second
@@ -60,7 +60,7 @@ func NewJWT(payload jwt.MapClaims, signingKey string, secondsDuration int64) (st
// Deprecated:
// Consider replacing with NewJWT().
//
// NewToken is a legacy alias for NewJWT that generates a HS256 signed JWT token.
// NewToken is a legacy alias for NewJWT that generates a HS256 signed JWT.
func NewToken(payload jwt.MapClaims, signingKey string, secondsDuration int64) (string, error) {
return NewJWT(payload, signingKey, secondsDuration)
}
+10 -10
View File
@@ -8,7 +8,7 @@ import (
)
func TestParseUnverifiedJWT(t *testing.T) {
// invalid formatted JWT token
// invalid formatted JWT
result1, err1 := security.ParseUnverifiedJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCJ9")
if err1 == nil {
t.Error("Expected error got nil")
@@ -17,7 +17,7 @@ func TestParseUnverifiedJWT(t *testing.T) {
t.Error("Expected no parsed claims, got", result1)
}
// properly formatted JWT token with INVALID claims
// properly formatted JWT with INVALID claims
// {"name": "test", "exp": 1516239022}
result2, err2 := security.ParseUnverifiedJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCIsImV4cCI6MTUxNjIzOTAyMn0.xYHirwESfSEW3Cq2BL47CEASvD_p_ps3QCA54XtNktU")
if err2 == nil {
@@ -27,7 +27,7 @@ func TestParseUnverifiedJWT(t *testing.T) {
t.Errorf("Expected to have 2 claims, got %v", result2)
}
// properly formatted JWT token with VALID claims
// properly formatted JWT with VALID claims
// {"name": "test"}
result3, err3 := security.ParseUnverifiedJWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCJ9.ml0QsTms3K9wMygTu41ZhKlTyjmW9zHQtoS8FUsCCjU")
if err3 != nil {
@@ -45,14 +45,14 @@ func TestParseJWT(t *testing.T) {
expectError bool
expectClaims jwt.MapClaims
}{
// invalid formatted JWT token
// invalid formatted JWT
{
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCJ9",
"test",
true,
nil,
},
// properly formatted JWT token with INVALID claims and INVALID secret
// properly formatted JWT with INVALID claims and INVALID secret
// {"name": "test", "exp": 1516239022}
{
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCIsImV4cCI6MTUxNjIzOTAyMn0.xYHirwESfSEW3Cq2BL47CEASvD_p_ps3QCA54XtNktU",
@@ -60,7 +60,7 @@ func TestParseJWT(t *testing.T) {
true,
nil,
},
// properly formatted JWT token with INVALID claims and VALID secret
// properly formatted JWT with INVALID claims and VALID secret
// {"name": "test", "exp": 1516239022}
{
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCIsImV4cCI6MTUxNjIzOTAyMn0.xYHirwESfSEW3Cq2BL47CEASvD_p_ps3QCA54XtNktU",
@@ -68,7 +68,7 @@ func TestParseJWT(t *testing.T) {
true,
nil,
},
// properly formatted JWT token with VALID claims and INVALID secret
// properly formatted JWT with VALID claims and INVALID secret
// {"name": "test", "exp": 1898636137}
{
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCIsImV4cCI6MTg5ODYzNjEzN30.gqRkHjpK5s1PxxBn9qPaWEWxTbpc1PPSD-an83TsXRY",
@@ -76,7 +76,7 @@ func TestParseJWT(t *testing.T) {
true,
nil,
},
// properly formatted EXPIRED JWT token with VALID secret
// properly formatted EXPIRED JWT with VALID secret
// {"name": "test", "exp": 1652097610}
{
"eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidGVzdCIsImV4cCI6OTU3ODczMzc0fQ.0oUUKUnsQHs4nZO1pnxQHahKtcHspHu4_AplN2sGC4A",
@@ -84,7 +84,7 @@ func TestParseJWT(t *testing.T) {
true,
nil,
},
// properly formatted JWT token with VALID claims and VALID secret
// properly formatted JWT with VALID claims and VALID secret
// {"name": "test", "exp": 1898636137}
{
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCIsImV4cCI6MTg5ODYzNjEzN30.gqRkHjpK5s1PxxBn9qPaWEWxTbpc1PPSD-an83TsXRY",
@@ -92,7 +92,7 @@ func TestParseJWT(t *testing.T) {
false,
jwt.MapClaims{"name": "test", "exp": 1898636137.0},
},
// properly formatted JWT token with VALID claims (without exp) and VALID secret
// properly formatted JWT with VALID claims (without exp) and VALID secret
// {"name": "test"}
{
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoidGVzdCJ9.ml0QsTms3K9wMygTu41ZhKlTyjmW9zHQtoS8FUsCCjU",
+1 -1
View File
@@ -9,4 +9,4 @@ PB_DOCS_URL = "https://pocketbase.io/docs/"
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
PB_VERSION = "v0.20.0"
PB_VERSION = "v0.20.3"
@@ -1,4 +1,4 @@
import{S as Se,i as ye,s as Te,O as G,e as c,w,b as k,c as se,f as p,g as d,h as a,m as ae,x as U,P as ve,Q as je,k as Ae,R as Be,n as Oe,t as W,a as V,o as u,d as ne,C as Fe,p as Qe,r as L,u as Ne,N as He}from"./index-97d7969a.js";import{S as Ke}from"./SdkTabs-15828c7f.js";import{F as qe}from"./FieldsQueryParam-932d6c55.js";function Ce(n,l,o){const s=n.slice();return s[5]=l[o],s}function Pe(n,l,o){const s=n.slice();return s[5]=l[o],s}function $e(n,l){let o,s=l[5].code+"",_,f,i,h;function m(){return l[4](l[5])}return{key:n,first:null,c(){o=c("button"),_=w(s),f=k(),p(o,"class","tab-item"),L(o,"active",l[1]===l[5].code),this.first=o},m(v,C){d(v,o,C),a(o,_),a(o,f),i||(h=Ne(o,"click",m),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&U(_,s),C&6&&L(o,"active",l[1]===l[5].code)},d(v){v&&u(o),i=!1,h()}}}function Me(n,l){let o,s,_,f;return s=new He({props:{content:l[5].body}}),{key:n,first:null,c(){o=c("div"),se(s.$$.fragment),_=k(),p(o,"class","tab-item"),L(o,"active",l[1]===l[5].code),this.first=o},m(i,h){d(i,o,h),ae(s,o,null),a(o,_),f=!0},p(i,h){l=i;const m={};h&4&&(m.content=l[5].body),s.$set(m),(!f||h&6)&&L(o,"active",l[1]===l[5].code)},i(i){f||(W(s.$$.fragment,i),f=!0)},o(i){V(s.$$.fragment,i),f=!1},d(i){i&&u(o),ne(s)}}}function ze(n){var be,ke;let l,o,s=n[0].name+"",_,f,i,h,m,v,C,H=n[0].name+"",E,ie,I,P,J,j,Y,$,K,ce,q,A,re,R,z=n[0].name+"",X,de,Z,B,x,M,ee,ue,te,T,le,O,oe,S,F,g=[],he=new Map,me,Q,b=[],fe=new Map,y;P=new Ke({props:{js:`
import{S as Se,i as ye,s as Te,O as G,e as c,w,b as k,c as se,f as p,g as d,h as a,m as ae,x as U,P as ve,Q as je,k as Ae,R as Be,n as Oe,t as W,a as V,o as u,d as ne,C as Fe,p as Qe,r as L,u as Ne,N as He}from"./index-26bf667a.js";import{S as Ke}from"./SdkTabs-27205987.js";import{F as qe}from"./FieldsQueryParam-c4b1abdd.js";function Ce(n,l,o){const s=n.slice();return s[5]=l[o],s}function Pe(n,l,o){const s=n.slice();return s[5]=l[o],s}function $e(n,l){let o,s=l[5].code+"",_,f,i,h;function m(){return l[4](l[5])}return{key:n,first:null,c(){o=c("button"),_=w(s),f=k(),p(o,"class","tab-item"),L(o,"active",l[1]===l[5].code),this.first=o},m(v,C){d(v,o,C),a(o,_),a(o,f),i||(h=Ne(o,"click",m),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&U(_,s),C&6&&L(o,"active",l[1]===l[5].code)},d(v){v&&u(o),i=!1,h()}}}function Me(n,l){let o,s,_,f;return s=new He({props:{content:l[5].body}}),{key:n,first:null,c(){o=c("div"),se(s.$$.fragment),_=k(),p(o,"class","tab-item"),L(o,"active",l[1]===l[5].code),this.first=o},m(i,h){d(i,o,h),ae(s,o,null),a(o,_),f=!0},p(i,h){l=i;const m={};h&4&&(m.content=l[5].body),s.$set(m),(!f||h&6)&&L(o,"active",l[1]===l[5].code)},i(i){f||(W(s.$$.fragment,i),f=!0)},o(i){V(s.$$.fragment,i),f=!1},d(i){i&&u(o),ne(s)}}}function ze(n){var be,ke;let l,o,s=n[0].name+"",_,f,i,h,m,v,C,H=n[0].name+"",E,ie,I,P,J,j,Y,$,K,ce,q,A,re,R,z=n[0].name+"",X,de,Z,B,x,M,ee,ue,te,T,le,O,oe,S,F,g=[],he=new Map,me,Q,b=[],fe=new Map,y;P=new Ke({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${n[3]}');
@@ -1,4 +1,4 @@
import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,w as k,b as p,c as K,f as b,g as d,h as o,m as I,x as de,P as Ee,Q as Ke,k as Ie,R as We,n as Ge,t as N,a as V,o as u,d as W,C as Le,p as Xe,r as G,u as Ye}from"./index-97d7969a.js";import{S as Ze}from"./SdkTabs-15828c7f.js";import{F as et}from"./FieldsQueryParam-932d6c55.js";function Ne(r,l,a){const n=r.slice();return n[5]=l[a],n}function Ve(r,l,a){const n=r.slice();return n[5]=l[a],n}function ze(r,l){let a,n=l[5].code+"",m,_,i,h;function g(){return l[4](l[5])}return{key:r,first:null,c(){a=s("button"),m=k(n),_=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(v,w){d(v,a,w),o(a,m),o(a,_),i||(h=Ye(a,"click",g),i=!0)},p(v,w){l=v,w&4&&n!==(n=l[5].code+"")&&de(m,n),w&6&&G(a,"active",l[1]===l[5].code)},d(v){v&&u(a),i=!1,h()}}}function Qe(r,l){let a,n,m,_;return n=new Ue({props:{content:l[5].body}}),{key:r,first:null,c(){a=s("div"),K(n.$$.fragment),m=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(i,h){d(i,a,h),I(n,a,null),o(a,m),_=!0},p(i,h){l=i;const g={};h&4&&(g.content=l[5].body),n.$set(g),(!_||h&6)&&G(a,"active",l[1]===l[5].code)},i(i){_||(N(n.$$.fragment,i),_=!0)},o(i){V(n.$$.fragment,i),_=!1},d(i){i&&u(a),W(n)}}}function tt(r){var De,Fe;let l,a,n=r[0].name+"",m,_,i,h,g,v,w,M,X,S,z,ue,Q,q,pe,Y,U=r[0].name+"",Z,he,fe,j,ee,D,te,T,oe,be,F,C,le,me,ae,_e,f,ke,R,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Pe,A,ie,O,ce,P,H,y=[],Re=new Map,Ae,E,$=[],Be=new Map,B;v=new Ze({props:{js:`
import{S as Ue,i as je,s as Je,N as Qe,O as J,e as s,w as k,b as p,c as K,f as b,g as d,h as o,m as I,x as de,P as Ee,Q as Ke,k as Ie,R as We,n as Ge,t as N,a as V,o as u,d as W,C as Le,p as Xe,r as G,u as Ye}from"./index-26bf667a.js";import{S as Ze}from"./SdkTabs-27205987.js";import{F as et}from"./FieldsQueryParam-c4b1abdd.js";function Ne(r,l,a){const n=r.slice();return n[5]=l[a],n}function Ve(r,l,a){const n=r.slice();return n[5]=l[a],n}function xe(r,l){let a,n=l[5].code+"",m,_,i,h;function g(){return l[4](l[5])}return{key:r,first:null,c(){a=s("button"),m=k(n),_=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(v,w){d(v,a,w),o(a,m),o(a,_),i||(h=Ye(a,"click",g),i=!0)},p(v,w){l=v,w&4&&n!==(n=l[5].code+"")&&de(m,n),w&6&&G(a,"active",l[1]===l[5].code)},d(v){v&&u(a),i=!1,h()}}}function ze(r,l){let a,n,m,_;return n=new Qe({props:{content:l[5].body}}),{key:r,first:null,c(){a=s("div"),K(n.$$.fragment),m=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(i,h){d(i,a,h),I(n,a,null),o(a,m),_=!0},p(i,h){l=i;const g={};h&4&&(g.content=l[5].body),n.$set(g),(!_||h&6)&&G(a,"active",l[1]===l[5].code)},i(i){_||(N(n.$$.fragment,i),_=!0)},o(i){V(n.$$.fragment,i),_=!1},d(i){i&&u(a),W(n)}}}function tt(r){var De,Fe;let l,a,n=r[0].name+"",m,_,i,h,g,v,w,M,X,S,x,ue,z,q,pe,Y,Q=r[0].name+"",Z,he,fe,U,ee,D,te,T,oe,be,F,C,le,me,ae,_e,f,ke,R,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Pe,A,ie,O,ce,P,H,y=[],Re=new Map,Ae,E,$=[],Be=new Map,B;v=new Ze({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${r[3]}');
@@ -24,15 +24,15 @@ import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,w as k,b as p,c as K,f as b
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
`}}),R=new Ue({props:{content:"?expand=relField1,relField2.subRelField"}}),A=new et({});let x=J(r[2]);const Me=e=>e[5].code;for(let e=0;e<x.length;e+=1){let t=Ve(r,x,e),c=Me(t);Re.set(c,y[e]=ze(c,t))}let L=J(r[2]);const qe=e=>e[5].code;for(let e=0;e<L.length;e+=1){let t=Ne(r,L,e),c=qe(t);Be.set(c,$[e]=Qe(c,t))}return{c(){l=s("h3"),a=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),h=s("div"),h.innerHTML=`<p>Returns a new auth response (token and record data) for an
`}}),R=new Qe({props:{content:"?expand=relField1,relField2.subRelField"}}),A=new et({props:{prefix:"record."}});let j=J(r[2]);const Me=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=Ve(r,j,e),c=Me(t);Re.set(c,y[e]=xe(c,t))}let L=J(r[2]);const qe=e=>e[5].code;for(let e=0;e<L.length;e+=1){let t=Ne(r,L,e),c=qe(t);Be.set(c,$[e]=ze(c,t))}return{c(){l=s("h3"),a=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),h=s("div"),h.innerHTML=`<p>Returns a new auth response (token and record data) for an
<strong>already authenticated record</strong>.</p> <p><em>This method is usually called by users on page/screen reload to ensure that the previously stored
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,g=p(),K(v.$$.fragment),w=p(),M=s("h6"),M.textContent="API details",X=p(),S=s("div"),z=s("strong"),z.textContent="POST",ue=p(),Q=s("div"),q=s("p"),pe=k("/api/collections/"),Y=s("strong"),Z=k(U),he=k("/auth-refresh"),fe=p(),j=s("p"),j.innerHTML="Requires record <code>Authorization:TOKEN</code> header",ee=p(),D=s("div"),D.textContent="Query parameters",te=p(),T=s("table"),oe=s("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',be=p(),F=s("tbody"),C=s("tr"),le=s("td"),le.textContent="expand",me=p(),ae=s("td"),ae.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ke=k(`Auto expand record relations. Ex.:
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,g=p(),K(v.$$.fragment),w=p(),M=s("h6"),M.textContent="API details",X=p(),S=s("div"),x=s("strong"),x.textContent="POST",ue=p(),z=s("div"),q=s("p"),pe=k("/api/collections/"),Y=s("strong"),Z=k(Q),he=k("/auth-refresh"),fe=p(),U=s("p"),U.innerHTML="Requires record <code>Authorization:TOKEN</code> header",ee=p(),D=s("div"),D.textContent="Query parameters",te=p(),T=s("table"),oe=s("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',be=p(),F=s("tbody"),C=s("tr"),le=s("td"),le.textContent="expand",me=p(),ae=s("td"),ae.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ke=k(`Auto expand record relations. Ex.:
`),K(R.$$.fragment),ge=k(`
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),$e=k(`
The expanded relations will be appended to the record under the
`),se=s("code"),se.textContent="expand",ye=k(" property (eg. "),ne=s("code"),ne.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).
`),we=s("br"),Te=k(`
Only the relations to which the request user has permissions to `),re=s("strong"),re.textContent="view",Ce=k(" will be expanded."),Pe=p(),K(A.$$.fragment),ie=p(),O=s("div"),O.textContent="Responses",ce=p(),P=s("div"),H=s("div");for(let e=0;e<y.length;e+=1)y[e].c();Ae=p(),E=s("div");for(let e=0;e<$.length;e+=1)$[e].c();b(l,"class","m-b-sm"),b(h,"class","content txt-lg m-b-sm"),b(M,"class","m-b-xs"),b(z,"class","label label-primary"),b(Q,"class","content"),b(j,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(D,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(O,"class","section-title"),b(H,"class","tabs-header compact combined left"),b(E,"class","tabs-content"),b(P,"class","tabs")},m(e,t){d(e,l,t),o(l,a),o(l,m),o(l,_),d(e,i,t),d(e,h,t),d(e,g,t),I(v,e,t),d(e,w,t),d(e,M,t),d(e,X,t),d(e,S,t),o(S,z),o(S,ue),o(S,Q),o(Q,q),o(q,pe),o(q,Y),o(Y,Z),o(q,he),o(S,fe),o(S,j),d(e,ee,t),d(e,D,t),d(e,te,t),d(e,T,t),o(T,oe),o(T,be),o(T,F),o(F,C),o(C,le),o(C,me),o(C,ae),o(C,_e),o(C,f),o(f,ke),I(R,f,null),o(f,ge),o(f,ve),o(f,$e),o(f,se),o(f,ye),o(f,ne),o(f,Se),o(f,we),o(f,Te),o(f,re),o(f,Ce),o(F,Pe),I(A,F,null),d(e,ie,t),d(e,O,t),d(e,ce,t),d(e,P,t),o(P,H);for(let c=0;c<y.length;c+=1)y[c]&&y[c].m(H,null);o(P,Ae),o(P,E);for(let c=0;c<$.length;c+=1)$[c]&&$[c].m(E,null);B=!0},p(e,[t]){var Oe,He;(!B||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const c={};t&9&&(c.js=`
Only the relations to which the request user has permissions to `),re=s("strong"),re.textContent="view",Ce=k(" will be expanded."),Pe=p(),K(A.$$.fragment),ie=p(),O=s("div"),O.textContent="Responses",ce=p(),P=s("div"),H=s("div");for(let e=0;e<y.length;e+=1)y[e].c();Ae=p(),E=s("div");for(let e=0;e<$.length;e+=1)$[e].c();b(l,"class","m-b-sm"),b(h,"class","content txt-lg m-b-sm"),b(M,"class","m-b-xs"),b(x,"class","label label-primary"),b(z,"class","content"),b(U,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(D,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(O,"class","section-title"),b(H,"class","tabs-header compact combined left"),b(E,"class","tabs-content"),b(P,"class","tabs")},m(e,t){d(e,l,t),o(l,a),o(l,m),o(l,_),d(e,i,t),d(e,h,t),d(e,g,t),I(v,e,t),d(e,w,t),d(e,M,t),d(e,X,t),d(e,S,t),o(S,x),o(S,ue),o(S,z),o(z,q),o(q,pe),o(q,Y),o(Y,Z),o(q,he),o(S,fe),o(S,U),d(e,ee,t),d(e,D,t),d(e,te,t),d(e,T,t),o(T,oe),o(T,be),o(T,F),o(F,C),o(C,le),o(C,me),o(C,ae),o(C,_e),o(C,f),o(f,ke),I(R,f,null),o(f,ge),o(f,ve),o(f,$e),o(f,se),o(f,ye),o(f,ne),o(f,Se),o(f,we),o(f,Te),o(f,re),o(f,Ce),o(F,Pe),I(A,F,null),d(e,ie,t),d(e,O,t),d(e,ce,t),d(e,P,t),o(P,H);for(let c=0;c<y.length;c+=1)y[c]&&y[c].m(H,null);o(P,Ae),o(P,E);for(let c=0;c<$.length;c+=1)$[c]&&$[c].m(E,null);B=!0},p(e,[t]){var Oe,He;(!B||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const c={};t&9&&(c.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -58,7 +58,7 @@ import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,w as k,b as p,c as K,f as b
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
`),v.$set(c),(!B||t&1)&&U!==(U=e[0].name+"")&&de(Z,U),t&6&&(x=J(e[2]),y=Ee(y,t,Me,1,e,x,Re,H,Ke,ze,null,Ve)),t&6&&(L=J(e[2]),Ie(),$=Ee($,t,qe,1,e,L,Be,E,We,Qe,null,Ne),Ge())},i(e){if(!B){N(v.$$.fragment,e),N(R.$$.fragment,e),N(A.$$.fragment,e);for(let t=0;t<L.length;t+=1)N($[t]);B=!0}},o(e){V(v.$$.fragment,e),V(R.$$.fragment,e),V(A.$$.fragment,e);for(let t=0;t<$.length;t+=1)V($[t]);B=!1},d(e){e&&(u(l),u(i),u(h),u(g),u(w),u(M),u(X),u(S),u(ee),u(D),u(te),u(T),u(ie),u(O),u(ce),u(P)),W(v,e),W(R),W(A);for(let t=0;t<y.length;t+=1)y[t].d();for(let t=0;t<$.length;t+=1)$[t].d()}}}function ot(r,l,a){let n,{collection:m}=l,_=200,i=[];const h=g=>a(1,_=g.code);return r.$$set=g=>{"collection"in g&&a(0,m=g.collection)},r.$$.update=()=>{r.$$.dirty&1&&a(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Le.dummyCollectionRecord(m)},null,2)},{code:401,body:`
`),v.$set(c),(!B||t&1)&&Q!==(Q=e[0].name+"")&&de(Z,Q),t&6&&(j=J(e[2]),y=Ee(y,t,Me,1,e,j,Re,H,Ke,xe,null,Ve)),t&6&&(L=J(e[2]),Ie(),$=Ee($,t,qe,1,e,L,Be,E,We,ze,null,Ne),Ge())},i(e){if(!B){N(v.$$.fragment,e),N(R.$$.fragment,e),N(A.$$.fragment,e);for(let t=0;t<L.length;t+=1)N($[t]);B=!0}},o(e){V(v.$$.fragment,e),V(R.$$.fragment,e),V(A.$$.fragment,e);for(let t=0;t<$.length;t+=1)V($[t]);B=!1},d(e){e&&(u(l),u(i),u(h),u(g),u(w),u(M),u(X),u(S),u(ee),u(D),u(te),u(T),u(ie),u(O),u(ce),u(P)),W(v,e),W(R),W(A);for(let t=0;t<y.length;t+=1)y[t].d();for(let t=0;t<$.length;t+=1)$[t].d()}}}function ot(r,l,a){let n,{collection:m}=l,_=200,i=[];const h=g=>a(1,_=g.code);return r.$$set=g=>{"collection"in g&&a(0,m=g.collection)},r.$$.update=()=>{r.$$.dirty&1&&a(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Le.dummyCollectionRecord(m)},null,2)},{code:401,body:`
{
"code": 401,
"message": "The request requires valid record authorization token to be set.",
@@ -76,4 +76,4 @@ import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,w as k,b as p,c as K,f as b
"message": "Missing auth record context.",
"data": {}
}
`}])},a(3,n=Le.getApiExampleUrl(Xe.baseUrl)),[m,_,i,n,h]}class nt extends je{constructor(l){super(),xe(this,l,ot,tt,Je,{collection:0})}}export{nt as default};
`}])},a(3,n=Le.getApiExampleUrl(Xe.baseUrl)),[m,_,i,n,h]}class nt extends Ue{constructor(l){super(),je(this,l,ot,tt,Je,{collection:0})}}export{nt as default};
@@ -1,4 +1,4 @@
import{S as Ee,i as Je,s as Ne,N as Le,O as z,e as o,w as k,b as h,c as I,f as p,g as r,h as a,m as K,x as pe,P as Ue,Q as Qe,k as xe,R as ze,n as Ie,t as L,a as E,o as c,d as G,C as Be,p as Ke,r as X,u as Ge}from"./index-97d7969a.js";import{S as Xe}from"./SdkTabs-15828c7f.js";import{F as Ye}from"./FieldsQueryParam-932d6c55.js";function Fe(s,l,n){const i=s.slice();return i[5]=l[n],i}function He(s,l,n){const i=s.slice();return i[5]=l[n],i}function je(s,l){let n,i=l[5].code+"",f,g,d,b;function _(){return l[4](l[5])}return{key:s,first:null,c(){n=o("button"),f=k(i),g=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(v,O){r(v,n,O),a(n,f),a(n,g),d||(b=Ge(n,"click",_),d=!0)},p(v,O){l=v,O&4&&i!==(i=l[5].code+"")&&pe(f,i),O&6&&X(n,"active",l[1]===l[5].code)},d(v){v&&c(n),d=!1,b()}}}function Ve(s,l){let n,i,f,g;return i=new Le({props:{content:l[5].body}}),{key:s,first:null,c(){n=o("div"),I(i.$$.fragment),f=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(d,b){r(d,n,b),K(i,n,null),a(n,f),g=!0},p(d,b){l=d;const _={};b&4&&(_.content=l[5].body),i.$set(_),(!g||b&6)&&X(n,"active",l[1]===l[5].code)},i(d){g||(L(i.$$.fragment,d),g=!0)},o(d){E(i.$$.fragment,d),g=!1},d(d){d&&c(n),G(i)}}}function Ze(s){let l,n,i=s[0].name+"",f,g,d,b,_,v,O,P,Y,A,J,be,N,R,me,Z,Q=s[0].name+"",ee,fe,te,M,ae,W,le,U,ne,S,oe,ge,B,y,se,ke,ie,_e,m,ve,C,we,$e,Oe,re,Ae,ce,Se,ye,Te,de,Ce,qe,q,ue,F,he,T,H,$=[],De=new Map,Pe,j,w=[],Re=new Map,D;v=new Xe({props:{js:`
import{S as xe,i as Ee,s as Je,N as Le,O as z,e as o,w as k,b as h,c as I,f as p,g as r,h as a,m as K,x as pe,P as Ue,Q as Ne,k as Qe,R as ze,n as Ie,t as L,a as x,o as c,d as G,C as Be,p as Ke,r as X,u as Ge}from"./index-26bf667a.js";import{S as Xe}from"./SdkTabs-27205987.js";import{F as Ye}from"./FieldsQueryParam-c4b1abdd.js";function Fe(s,l,n){const i=s.slice();return i[5]=l[n],i}function He(s,l,n){const i=s.slice();return i[5]=l[n],i}function je(s,l){let n,i=l[5].code+"",f,g,d,b;function _(){return l[4](l[5])}return{key:s,first:null,c(){n=o("button"),f=k(i),g=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(v,O){r(v,n,O),a(n,f),a(n,g),d||(b=Ge(n,"click",_),d=!0)},p(v,O){l=v,O&4&&i!==(i=l[5].code+"")&&pe(f,i),O&6&&X(n,"active",l[1]===l[5].code)},d(v){v&&c(n),d=!1,b()}}}function Ve(s,l){let n,i,f,g;return i=new Le({props:{content:l[5].body}}),{key:s,first:null,c(){n=o("div"),I(i.$$.fragment),f=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(d,b){r(d,n,b),K(i,n,null),a(n,f),g=!0},p(d,b){l=d;const _={};b&4&&(_.content=l[5].body),i.$set(_),(!g||b&6)&&X(n,"active",l[1]===l[5].code)},i(d){g||(L(i.$$.fragment,d),g=!0)},o(d){x(i.$$.fragment,d),g=!1},d(d){d&&c(n),G(i)}}}function Ze(s){let l,n,i=s[0].name+"",f,g,d,b,_,v,O,P,Y,A,E,be,J,R,me,Z,N=s[0].name+"",ee,fe,te,M,ae,W,le,U,ne,S,oe,ge,B,y,se,ke,ie,_e,m,ve,C,we,$e,Oe,re,Ae,ce,Se,ye,Te,de,Ce,qe,q,ue,F,he,T,H,$=[],De=new Map,Pe,j,w=[],Re=new Map,D;v=new Xe({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${s[3]}');
@@ -45,9 +45,9 @@ import{S as Ee,i as Je,s as Ne,N as Le,O as z,e as o,w as k,b as h,c as I,f as p
// "logout" the last authenticated model
pb.authStore.clear();
`}}),C=new Le({props:{content:"?expand=relField1,relField2.subRelField"}}),q=new Ye({});let x=z(s[2]);const Me=e=>e[5].code;for(let e=0;e<x.length;e+=1){let t=He(s,x,e),u=Me(t);De.set(u,$[e]=je(u,t))}let V=z(s[2]);const We=e=>e[5].code;for(let e=0;e<V.length;e+=1){let t=Fe(s,V,e),u=We(t);Re.set(u,w[e]=Ve(u,t))}return{c(){l=o("h3"),n=k("Auth with OAuth2 ("),f=k(i),g=k(")"),d=h(),b=o("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p> <p>For more details please check the
`}}),C=new Le({props:{content:"?expand=relField1,relField2.subRelField"}}),q=new Ye({props:{prefix:"record."}});let Q=z(s[2]);const Me=e=>e[5].code;for(let e=0;e<Q.length;e+=1){let t=He(s,Q,e),u=Me(t);De.set(u,$[e]=je(u,t))}let V=z(s[2]);const We=e=>e[5].code;for(let e=0;e<V.length;e+=1){let t=Fe(s,V,e),u=We(t);Re.set(u,w[e]=Ve(u,t))}return{c(){l=o("h3"),n=k("Auth with OAuth2 ("),f=k(i),g=k(")"),d=h(),b=o("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p> <p>For more details please check the
<a href="https://pocketbase.io/docs/authentication/#oauth2-integration" target="_blank" rel="noopener noreferrer">OAuth2 integration documentation
</a>.</p>`,_=h(),I(v.$$.fragment),O=h(),P=o("h6"),P.textContent="API details",Y=h(),A=o("div"),J=o("strong"),J.textContent="POST",be=h(),N=o("div"),R=o("p"),me=k("/api/collections/"),Z=o("strong"),ee=k(Q),fe=k("/auth-with-oauth2"),te=h(),M=o("div"),M.textContent="Body Parameters",ae=h(),W=o("table"),W.innerHTML=`<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>provider</span></div></td> <td><span class="label">String</span></td> <td>The name of the OAuth2 client provider (eg. &quot;google&quot;).</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>code</span></div></td> <td><span class="label">String</span></td> <td>The authorization code returned from the initial request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>codeVerifier</span></div></td> <td><span class="label">String</span></td> <td>The code verifier sent with the initial request as part of the code_challenge.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>redirectUrl</span></div></td> <td><span class="label">String</span></td> <td>The redirect url sent with the initial request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>createData</span></div></td> <td><span class="label">Object</span></td> <td><p>Optional data that will be used when creating the auth record on OAuth2 sign-up.</p> <p>The created auth record must comply with the same requirements and validations in the
</a>.</p>`,_=h(),I(v.$$.fragment),O=h(),P=o("h6"),P.textContent="API details",Y=h(),A=o("div"),E=o("strong"),E.textContent="POST",be=h(),J=o("div"),R=o("p"),me=k("/api/collections/"),Z=o("strong"),ee=k(N),fe=k("/auth-with-oauth2"),te=h(),M=o("div"),M.textContent="Body Parameters",ae=h(),W=o("table"),W.innerHTML=`<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>provider</span></div></td> <td><span class="label">String</span></td> <td>The name of the OAuth2 client provider (eg. &quot;google&quot;).</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>code</span></div></td> <td><span class="label">String</span></td> <td>The authorization code returned from the initial request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>codeVerifier</span></div></td> <td><span class="label">String</span></td> <td>The code verifier sent with the initial request as part of the code_challenge.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>redirectUrl</span></div></td> <td><span class="label">String</span></td> <td>The redirect url sent with the initial request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>createData</span></div></td> <td><span class="label">Object</span></td> <td><p>Optional data that will be used when creating the auth record on OAuth2 sign-up.</p> <p>The created auth record must comply with the same requirements and validations in the
regular <strong>create</strong> action.
<br/> <em>The data can only be in <code>json</code>, aka. <code>multipart/form-data</code> and files
upload currently are not supported during OAuth2 sign-ups.</em></p></td></tr></tbody>`,le=h(),U=o("div"),U.textContent="Query parameters",ne=h(),S=o("table"),oe=o("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',ge=h(),B=o("tbody"),y=o("tr"),se=o("td"),se.textContent="expand",ke=h(),ie=o("td"),ie.innerHTML='<span class="label">String</span>',_e=h(),m=o("td"),ve=k(`Auto expand record relations. Ex.:
@@ -56,7 +56,7 @@ import{S as Ee,i as Je,s as Ne,N as Le,O as z,e as o,w as k,b as h,c as I,f as p
The expanded relations will be appended to the record under the
`),re=o("code"),re.textContent="expand",Ae=k(" property (eg. "),ce=o("code"),ce.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).
`),ye=o("br"),Te=k(`
Only the relations to which the request user has permissions to `),de=o("strong"),de.textContent="view",Ce=k(" will be expanded."),qe=h(),I(q.$$.fragment),ue=h(),F=o("div"),F.textContent="Responses",he=h(),T=o("div"),H=o("div");for(let e=0;e<$.length;e+=1)$[e].c();Pe=h(),j=o("div");for(let e=0;e<w.length;e+=1)w[e].c();p(l,"class","m-b-sm"),p(b,"class","content txt-lg m-b-sm"),p(P,"class","m-b-xs"),p(J,"class","label label-primary"),p(N,"class","content"),p(A,"class","alert alert-success"),p(M,"class","section-title"),p(W,"class","table-compact table-border m-b-base"),p(U,"class","section-title"),p(S,"class","table-compact table-border m-b-base"),p(F,"class","section-title"),p(H,"class","tabs-header compact combined left"),p(j,"class","tabs-content"),p(T,"class","tabs")},m(e,t){r(e,l,t),a(l,n),a(l,f),a(l,g),r(e,d,t),r(e,b,t),r(e,_,t),K(v,e,t),r(e,O,t),r(e,P,t),r(e,Y,t),r(e,A,t),a(A,J),a(A,be),a(A,N),a(N,R),a(R,me),a(R,Z),a(Z,ee),a(R,fe),r(e,te,t),r(e,M,t),r(e,ae,t),r(e,W,t),r(e,le,t),r(e,U,t),r(e,ne,t),r(e,S,t),a(S,oe),a(S,ge),a(S,B),a(B,y),a(y,se),a(y,ke),a(y,ie),a(y,_e),a(y,m),a(m,ve),K(C,m,null),a(m,we),a(m,$e),a(m,Oe),a(m,re),a(m,Ae),a(m,ce),a(m,Se),a(m,ye),a(m,Te),a(m,de),a(m,Ce),a(B,qe),K(q,B,null),r(e,ue,t),r(e,F,t),r(e,he,t),r(e,T,t),a(T,H);for(let u=0;u<$.length;u+=1)$[u]&&$[u].m(H,null);a(T,Pe),a(T,j);for(let u=0;u<w.length;u+=1)w[u]&&w[u].m(j,null);D=!0},p(e,[t]){(!D||t&1)&&i!==(i=e[0].name+"")&&pe(f,i);const u={};t&9&&(u.js=`
Only the relations to which the request user has permissions to `),de=o("strong"),de.textContent="view",Ce=k(" will be expanded."),qe=h(),I(q.$$.fragment),ue=h(),F=o("div"),F.textContent="Responses",he=h(),T=o("div"),H=o("div");for(let e=0;e<$.length;e+=1)$[e].c();Pe=h(),j=o("div");for(let e=0;e<w.length;e+=1)w[e].c();p(l,"class","m-b-sm"),p(b,"class","content txt-lg m-b-sm"),p(P,"class","m-b-xs"),p(E,"class","label label-primary"),p(J,"class","content"),p(A,"class","alert alert-success"),p(M,"class","section-title"),p(W,"class","table-compact table-border m-b-base"),p(U,"class","section-title"),p(S,"class","table-compact table-border m-b-base"),p(F,"class","section-title"),p(H,"class","tabs-header compact combined left"),p(j,"class","tabs-content"),p(T,"class","tabs")},m(e,t){r(e,l,t),a(l,n),a(l,f),a(l,g),r(e,d,t),r(e,b,t),r(e,_,t),K(v,e,t),r(e,O,t),r(e,P,t),r(e,Y,t),r(e,A,t),a(A,E),a(A,be),a(A,J),a(J,R),a(R,me),a(R,Z),a(Z,ee),a(R,fe),r(e,te,t),r(e,M,t),r(e,ae,t),r(e,W,t),r(e,le,t),r(e,U,t),r(e,ne,t),r(e,S,t),a(S,oe),a(S,ge),a(S,B),a(B,y),a(y,se),a(y,ke),a(y,ie),a(y,_e),a(y,m),a(m,ve),K(C,m,null),a(m,we),a(m,$e),a(m,Oe),a(m,re),a(m,Ae),a(m,ce),a(m,Se),a(m,ye),a(m,Te),a(m,de),a(m,Ce),a(B,qe),K(q,B,null),r(e,ue,t),r(e,F,t),r(e,he,t),r(e,T,t),a(T,H);for(let u=0;u<$.length;u+=1)$[u]&&$[u].m(H,null);a(T,Pe),a(T,j);for(let u=0;u<w.length;u+=1)w[u]&&w[u].m(j,null);D=!0},p(e,[t]){(!D||t&1)&&i!==(i=e[0].name+"")&&pe(f,i);const u={};t&9&&(u.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[3]}');
@@ -103,7 +103,7 @@ import{S as Ee,i as Je,s as Ne,N as Le,O as z,e as o,w as k,b as h,c as I,f as p
// "logout" the last authenticated model
pb.authStore.clear();
`),v.$set(u),(!D||t&1)&&Q!==(Q=e[0].name+"")&&pe(ee,Q),t&6&&(x=z(e[2]),$=Ue($,t,Me,1,e,x,De,H,Qe,je,null,He)),t&6&&(V=z(e[2]),xe(),w=Ue(w,t,We,1,e,V,Re,j,ze,Ve,null,Fe),Ie())},i(e){if(!D){L(v.$$.fragment,e),L(C.$$.fragment,e),L(q.$$.fragment,e);for(let t=0;t<V.length;t+=1)L(w[t]);D=!0}},o(e){E(v.$$.fragment,e),E(C.$$.fragment,e),E(q.$$.fragment,e);for(let t=0;t<w.length;t+=1)E(w[t]);D=!1},d(e){e&&(c(l),c(d),c(b),c(_),c(O),c(P),c(Y),c(A),c(te),c(M),c(ae),c(W),c(le),c(U),c(ne),c(S),c(ue),c(F),c(he),c(T)),G(v,e),G(C),G(q);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function et(s,l,n){let i,{collection:f}=l,g=200,d=[];const b=_=>n(1,g=_.code);return s.$$set=_=>{"collection"in _&&n(0,f=_.collection)},s.$$.update=()=>{s.$$.dirty&1&&n(2,d=[{code:200,body:JSON.stringify({token:"JWT_AUTH_TOKEN",record:Be.dummyCollectionRecord(f),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png",accessToken:"...",refreshToken:"...",rawUser:{}}},null,2)},{code:400,body:`
`),v.$set(u),(!D||t&1)&&N!==(N=e[0].name+"")&&pe(ee,N),t&6&&(Q=z(e[2]),$=Ue($,t,Me,1,e,Q,De,H,Ne,je,null,He)),t&6&&(V=z(e[2]),Qe(),w=Ue(w,t,We,1,e,V,Re,j,ze,Ve,null,Fe),Ie())},i(e){if(!D){L(v.$$.fragment,e),L(C.$$.fragment,e),L(q.$$.fragment,e);for(let t=0;t<V.length;t+=1)L(w[t]);D=!0}},o(e){x(v.$$.fragment,e),x(C.$$.fragment,e),x(q.$$.fragment,e);for(let t=0;t<w.length;t+=1)x(w[t]);D=!1},d(e){e&&(c(l),c(d),c(b),c(_),c(O),c(P),c(Y),c(A),c(te),c(M),c(ae),c(W),c(le),c(U),c(ne),c(S),c(ue),c(F),c(he),c(T)),G(v,e),G(C),G(q);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function et(s,l,n){let i,{collection:f}=l,g=200,d=[];const b=_=>n(1,g=_.code);return s.$$set=_=>{"collection"in _&&n(0,f=_.collection)},s.$$.update=()=>{s.$$.dirty&1&&n(2,d=[{code:200,body:JSON.stringify({token:"JWT_AUTH_TOKEN",record:Be.dummyCollectionRecord(f),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png",accessToken:"...",refreshToken:"...",rawUser:{}}},null,2)},{code:400,body:`
{
"code": 400,
"message": "An error occurred while submitting the form.",
@@ -114,4 +114,4 @@ import{S as Ee,i as Je,s as Ne,N as Le,O as z,e as o,w as k,b as h,c as I,f as p
}
}
}
`}])},n(3,i=Be.getApiExampleUrl(Ke.baseUrl)),[f,g,d,i,b]}class nt extends Ee{constructor(l){super(),Je(this,l,et,Ze,Ne,{collection:0})}}export{nt as default};
`}])},n(3,i=Be.getApiExampleUrl(Ke.baseUrl)),[f,g,d,i,b]}class nt extends xe{constructor(l){super(),Ee(this,l,et,Ze,Je,{collection:0})}}export{nt as default};
-98
View File
@@ -1,98 +0,0 @@
import{S as we,i as ye,s as $e,N as ve,O as ot,e as n,w as p,b as d,c as nt,f as m,g as r,h as e,m as st,x as Dt,P as pe,Q as Pe,k as Re,R as Ce,n as Oe,t as Z,a as x,o as c,d as it,C as fe,p as Ae,r as rt,u as Te}from"./index-97d7969a.js";import{S as Ue}from"./SdkTabs-15828c7f.js";import{F as Me}from"./FieldsQueryParam-932d6c55.js";function he(s,l,a){const i=s.slice();return i[8]=l[a],i}function be(s,l,a){const i=s.slice();return i[8]=l[a],i}function De(s){let l;return{c(){l=p("email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function Ee(s){let l;return{c(){l=p("username")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function We(s){let l;return{c(){l=p("username/email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function me(s){let l;return{c(){l=n("strong"),l.textContent="username"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function _e(s){let l;return{c(){l=p("or")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function ke(s){let l;return{c(){l=n("strong"),l.textContent="email"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function ge(s,l){let a,i=l[8].code+"",g,b,f,u;function _(){return l[7](l[8])}return{key:s,first:null,c(){a=n("button"),g=p(i),b=d(),m(a,"class","tab-item"),rt(a,"active",l[3]===l[8].code),this.first=a},m(R,C){r(R,a,C),e(a,g),e(a,b),f||(u=Te(a,"click",_),f=!0)},p(R,C){l=R,C&16&&i!==(i=l[8].code+"")&&Dt(g,i),C&24&&rt(a,"active",l[3]===l[8].code)},d(R){R&&c(a),f=!1,u()}}}function Se(s,l){let a,i,g,b;return i=new ve({props:{content:l[8].body}}),{key:s,first:null,c(){a=n("div"),nt(i.$$.fragment),g=d(),m(a,"class","tab-item"),rt(a,"active",l[3]===l[8].code),this.first=a},m(f,u){r(f,a,u),st(i,a,null),e(a,g),b=!0},p(f,u){l=f;const _={};u&16&&(_.content=l[8].body),i.$set(_),(!b||u&24)&&rt(a,"active",l[3]===l[8].code)},i(f){b||(Z(i.$$.fragment,f),b=!0)},o(f){x(i.$$.fragment,f),b=!1},d(f){f&&c(a),it(i)}}}function Le(s){var re,ce;let l,a,i=s[0].name+"",g,b,f,u,_,R,C,O,B,Et,ct,T,dt,N,ut,U,tt,Wt,et,I,Lt,pt,lt=s[0].name+"",ft,Bt,ht,V,bt,M,mt,qt,Q,D,_t,Ft,kt,Ht,$,Yt,gt,St,vt,Nt,wt,yt,j,$t,E,Pt,It,J,W,Rt,Vt,Ct,Qt,k,jt,q,Jt,Kt,zt,Ot,Gt,At,Xt,Zt,xt,Tt,te,ee,F,Ut,K,Mt,L,z,A=[],le=new Map,ae,G,S=[],oe=new Map,H;function ne(t,o){if(t[1]&&t[2])return We;if(t[1])return Ee;if(t[2])return De}let Y=ne(s),P=Y&&Y(s);T=new Ue({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${s[6]}');
...
const authData = await pb.collection('${(re=s[0])==null?void 0:re.name}').authWithPassword(
'${s[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
console.log(pb.authStore.isValid);
console.log(pb.authStore.token);
console.log(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`,dart:`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${s[6]}');
...
final authData = await pb.collection('${(ce=s[0])==null?void 0:ce.name}').authWithPassword(
'${s[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`}});let v=s[1]&&me(),w=s[1]&&s[2]&&_e(),y=s[2]&&ke();q=new ve({props:{content:"?expand=relField1,relField2.subRelField"}}),F=new Me({});let at=ot(s[4]);const se=t=>t[8].code;for(let t=0;t<at.length;t+=1){let o=be(s,at,t),h=se(o);le.set(h,A[t]=ge(h,o))}let X=ot(s[4]);const ie=t=>t[8].code;for(let t=0;t<X.length;t+=1){let o=he(s,X,t),h=ie(o);oe.set(h,S[t]=Se(h,o))}return{c(){l=n("h3"),a=p("Auth with password ("),g=p(i),b=p(")"),f=d(),u=n("div"),_=n("p"),R=p(`Returns new auth token and account data by a combination of
`),C=n("strong"),P&&P.c(),O=p(`
and `),B=n("strong"),B.textContent="password",Et=p("."),ct=d(),nt(T.$$.fragment),dt=d(),N=n("h6"),N.textContent="API details",ut=d(),U=n("div"),tt=n("strong"),tt.textContent="POST",Wt=d(),et=n("div"),I=n("p"),Lt=p("/api/collections/"),pt=n("strong"),ft=p(lt),Bt=p("/auth-with-password"),ht=d(),V=n("div"),V.textContent="Body Parameters",bt=d(),M=n("table"),mt=n("thead"),mt.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',qt=d(),Q=n("tbody"),D=n("tr"),_t=n("td"),_t.innerHTML='<div class="inline-flex"><span class="label label-success">Required</span> <span>identity</span></div>',Ft=d(),kt=n("td"),kt.innerHTML='<span class="label">String</span>',Ht=d(),$=n("td"),Yt=p(`The
`),v&&v.c(),gt=d(),w&&w.c(),St=d(),y&&y.c(),vt=p(`
of the record to authenticate.`),Nt=d(),wt=n("tr"),wt.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>The auth record password.</td>',yt=d(),j=n("div"),j.textContent="Query parameters",$t=d(),E=n("table"),Pt=n("thead"),Pt.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',It=d(),J=n("tbody"),W=n("tr"),Rt=n("td"),Rt.textContent="expand",Vt=d(),Ct=n("td"),Ct.innerHTML='<span class="label">String</span>',Qt=d(),k=n("td"),jt=p(`Auto expand record relations. Ex.:
`),nt(q.$$.fragment),Jt=p(`
Supports up to 6-levels depth nested relations expansion. `),Kt=n("br"),zt=p(`
The expanded relations will be appended to the record under the
`),Ot=n("code"),Ot.textContent="expand",Gt=p(" property (eg. "),At=n("code"),At.textContent='"expand": {"relField1": {...}, ...}',Xt=p(`).
`),Zt=n("br"),xt=p(`
Only the relations to which the request user has permissions to `),Tt=n("strong"),Tt.textContent="view",te=p(" will be expanded."),ee=d(),nt(F.$$.fragment),Ut=d(),K=n("div"),K.textContent="Responses",Mt=d(),L=n("div"),z=n("div");for(let t=0;t<A.length;t+=1)A[t].c();ae=d(),G=n("div");for(let t=0;t<S.length;t+=1)S[t].c();m(l,"class","m-b-sm"),m(u,"class","content txt-lg m-b-sm"),m(N,"class","m-b-xs"),m(tt,"class","label label-primary"),m(et,"class","content"),m(U,"class","alert alert-success"),m(V,"class","section-title"),m(M,"class","table-compact table-border m-b-base"),m(j,"class","section-title"),m(E,"class","table-compact table-border m-b-base"),m(K,"class","section-title"),m(z,"class","tabs-header compact combined left"),m(G,"class","tabs-content"),m(L,"class","tabs")},m(t,o){r(t,l,o),e(l,a),e(l,g),e(l,b),r(t,f,o),r(t,u,o),e(u,_),e(_,R),e(_,C),P&&P.m(C,null),e(_,O),e(_,B),e(_,Et),r(t,ct,o),st(T,t,o),r(t,dt,o),r(t,N,o),r(t,ut,o),r(t,U,o),e(U,tt),e(U,Wt),e(U,et),e(et,I),e(I,Lt),e(I,pt),e(pt,ft),e(I,Bt),r(t,ht,o),r(t,V,o),r(t,bt,o),r(t,M,o),e(M,mt),e(M,qt),e(M,Q),e(Q,D),e(D,_t),e(D,Ft),e(D,kt),e(D,Ht),e(D,$),e($,Yt),v&&v.m($,null),e($,gt),w&&w.m($,null),e($,St),y&&y.m($,null),e($,vt),e(Q,Nt),e(Q,wt),r(t,yt,o),r(t,j,o),r(t,$t,o),r(t,E,o),e(E,Pt),e(E,It),e(E,J),e(J,W),e(W,Rt),e(W,Vt),e(W,Ct),e(W,Qt),e(W,k),e(k,jt),st(q,k,null),e(k,Jt),e(k,Kt),e(k,zt),e(k,Ot),e(k,Gt),e(k,At),e(k,Xt),e(k,Zt),e(k,xt),e(k,Tt),e(k,te),e(J,ee),st(F,J,null),r(t,Ut,o),r(t,K,o),r(t,Mt,o),r(t,L,o),e(L,z);for(let h=0;h<A.length;h+=1)A[h]&&A[h].m(z,null);e(L,ae),e(L,G);for(let h=0;h<S.length;h+=1)S[h]&&S[h].m(G,null);H=!0},p(t,[o]){var de,ue;(!H||o&1)&&i!==(i=t[0].name+"")&&Dt(g,i),Y!==(Y=ne(t))&&(P&&P.d(1),P=Y&&Y(t),P&&(P.c(),P.m(C,null)));const h={};o&97&&(h.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${t[6]}');
...
const authData = await pb.collection('${(de=t[0])==null?void 0:de.name}').authWithPassword(
'${t[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
console.log(pb.authStore.isValid);
console.log(pb.authStore.token);
console.log(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`),o&97&&(h.dart=`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${t[6]}');
...
final authData = await pb.collection('${(ue=t[0])==null?void 0:ue.name}').authWithPassword(
'${t[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`),T.$set(h),(!H||o&1)&&lt!==(lt=t[0].name+"")&&Dt(ft,lt),t[1]?v||(v=me(),v.c(),v.m($,gt)):v&&(v.d(1),v=null),t[1]&&t[2]?w||(w=_e(),w.c(),w.m($,St)):w&&(w.d(1),w=null),t[2]?y||(y=ke(),y.c(),y.m($,vt)):y&&(y.d(1),y=null),o&24&&(at=ot(t[4]),A=pe(A,o,se,1,t,at,le,z,Pe,ge,null,be)),o&24&&(X=ot(t[4]),Re(),S=pe(S,o,ie,1,t,X,oe,G,Ce,Se,null,he),Oe())},i(t){if(!H){Z(T.$$.fragment,t),Z(q.$$.fragment,t),Z(F.$$.fragment,t);for(let o=0;o<X.length;o+=1)Z(S[o]);H=!0}},o(t){x(T.$$.fragment,t),x(q.$$.fragment,t),x(F.$$.fragment,t);for(let o=0;o<S.length;o+=1)x(S[o]);H=!1},d(t){t&&(c(l),c(f),c(u),c(ct),c(dt),c(N),c(ut),c(U),c(ht),c(V),c(bt),c(M),c(yt),c(j),c($t),c(E),c(Ut),c(K),c(Mt),c(L)),P&&P.d(),it(T,t),v&&v.d(),w&&w.d(),y&&y.d(),it(q),it(F);for(let o=0;o<A.length;o+=1)A[o].d();for(let o=0;o<S.length;o+=1)S[o].d()}}}function Be(s,l,a){let i,g,b,f,{collection:u}=l,_=200,R=[];const C=O=>a(3,_=O.code);return s.$$set=O=>{"collection"in O&&a(0,u=O.collection)},s.$$.update=()=>{var O,B;s.$$.dirty&1&&a(2,g=(O=u==null?void 0:u.options)==null?void 0:O.allowEmailAuth),s.$$.dirty&1&&a(1,b=(B=u==null?void 0:u.options)==null?void 0:B.allowUsernameAuth),s.$$.dirty&6&&a(5,f=b&&g?"YOUR_USERNAME_OR_EMAIL":b?"YOUR_USERNAME":"YOUR_EMAIL"),s.$$.dirty&1&&a(4,R=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:fe.dummyCollectionRecord(u)},null,2)},{code:400,body:`
{
"code": 400,
"message": "Failed to authenticate.",
"data": {
"identity": {
"code": "validation_required",
"message": "Missing required value."
}
}
}
`}])},a(6,i=fe.getApiExampleUrl(Ae.baseUrl)),[u,b,g,_,R,f,i,C]}class Ye extends we{constructor(l){super(),ye(this,l,Be,Le,$e,{collection:0})}}export{Ye as default};
+98
View File
@@ -0,0 +1,98 @@
import{S as wt,i as yt,s as $t,N as vt,O as oe,e as n,w as p,b as d,c as ne,f as m,g as r,h as t,m as se,x as De,P as pt,Q as Pt,k as Rt,R as Ct,n as Ot,t as Z,a as x,o as c,d as ie,C as ft,p as At,r as re,u as Tt}from"./index-26bf667a.js";import{S as Ut}from"./SdkTabs-27205987.js";import{F as Mt}from"./FieldsQueryParam-c4b1abdd.js";function ht(s,l,a){const i=s.slice();return i[8]=l[a],i}function bt(s,l,a){const i=s.slice();return i[8]=l[a],i}function Dt(s){let l;return{c(){l=p("email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function Et(s){let l;return{c(){l=p("username")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function Wt(s){let l;return{c(){l=p("username/email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function mt(s){let l;return{c(){l=n("strong"),l.textContent="username"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function _t(s){let l;return{c(){l=p("or")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function kt(s){let l;return{c(){l=n("strong"),l.textContent="email"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function gt(s,l){let a,i=l[8].code+"",g,b,f,u;function _(){return l[7](l[8])}return{key:s,first:null,c(){a=n("button"),g=p(i),b=d(),m(a,"class","tab-item"),re(a,"active",l[3]===l[8].code),this.first=a},m(R,C){r(R,a,C),t(a,g),t(a,b),f||(u=Tt(a,"click",_),f=!0)},p(R,C){l=R,C&16&&i!==(i=l[8].code+"")&&De(g,i),C&24&&re(a,"active",l[3]===l[8].code)},d(R){R&&c(a),f=!1,u()}}}function St(s,l){let a,i,g,b;return i=new vt({props:{content:l[8].body}}),{key:s,first:null,c(){a=n("div"),ne(i.$$.fragment),g=d(),m(a,"class","tab-item"),re(a,"active",l[3]===l[8].code),this.first=a},m(f,u){r(f,a,u),se(i,a,null),t(a,g),b=!0},p(f,u){l=f;const _={};u&16&&(_.content=l[8].body),i.$set(_),(!b||u&24)&&re(a,"active",l[3]===l[8].code)},i(f){b||(Z(i.$$.fragment,f),b=!0)},o(f){x(i.$$.fragment,f),b=!1},d(f){f&&c(a),ie(i)}}}function Lt(s){var rt,ct;let l,a,i=s[0].name+"",g,b,f,u,_,R,C,O,B,Ee,ce,T,de,N,ue,U,ee,We,te,I,Le,pe,le=s[0].name+"",fe,Be,he,V,be,M,me,qe,Q,D,_e,Fe,ke,He,$,Ye,ge,Se,ve,Ne,we,ye,j,$e,E,Pe,Ie,J,W,Re,Ve,Ce,Qe,k,je,q,Je,Ke,ze,Oe,Ge,Ae,Xe,Ze,xe,Te,et,tt,F,Ue,K,Me,L,z,A=[],lt=new Map,at,G,S=[],ot=new Map,H;function nt(e,o){if(e[1]&&e[2])return Wt;if(e[1])return Et;if(e[2])return Dt}let Y=nt(s),P=Y&&Y(s);T=new Ut({props:{js:`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${s[6]}');
...
const authData = await pb.collection('${(rt=s[0])==null?void 0:rt.name}').authWithPassword(
'${s[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
console.log(pb.authStore.isValid);
console.log(pb.authStore.token);
console.log(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`,dart:`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${s[6]}');
...
final authData = await pb.collection('${(ct=s[0])==null?void 0:ct.name}').authWithPassword(
'${s[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`}});let v=s[1]&&mt(),w=s[1]&&s[2]&&_t(),y=s[2]&&kt();q=new vt({props:{content:"?expand=relField1,relField2.subRelField"}}),F=new Mt({props:{prefix:"record."}});let ae=oe(s[4]);const st=e=>e[8].code;for(let e=0;e<ae.length;e+=1){let o=bt(s,ae,e),h=st(o);lt.set(h,A[e]=gt(h,o))}let X=oe(s[4]);const it=e=>e[8].code;for(let e=0;e<X.length;e+=1){let o=ht(s,X,e),h=it(o);ot.set(h,S[e]=St(h,o))}return{c(){l=n("h3"),a=p("Auth with password ("),g=p(i),b=p(")"),f=d(),u=n("div"),_=n("p"),R=p(`Returns new auth token and account data by a combination of
`),C=n("strong"),P&&P.c(),O=p(`
and `),B=n("strong"),B.textContent="password",Ee=p("."),ce=d(),ne(T.$$.fragment),de=d(),N=n("h6"),N.textContent="API details",ue=d(),U=n("div"),ee=n("strong"),ee.textContent="POST",We=d(),te=n("div"),I=n("p"),Le=p("/api/collections/"),pe=n("strong"),fe=p(le),Be=p("/auth-with-password"),he=d(),V=n("div"),V.textContent="Body Parameters",be=d(),M=n("table"),me=n("thead"),me.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',qe=d(),Q=n("tbody"),D=n("tr"),_e=n("td"),_e.innerHTML='<div class="inline-flex"><span class="label label-success">Required</span> <span>identity</span></div>',Fe=d(),ke=n("td"),ke.innerHTML='<span class="label">String</span>',He=d(),$=n("td"),Ye=p(`The
`),v&&v.c(),ge=d(),w&&w.c(),Se=d(),y&&y.c(),ve=p(`
of the record to authenticate.`),Ne=d(),we=n("tr"),we.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>The auth record password.</td>',ye=d(),j=n("div"),j.textContent="Query parameters",$e=d(),E=n("table"),Pe=n("thead"),Pe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',Ie=d(),J=n("tbody"),W=n("tr"),Re=n("td"),Re.textContent="expand",Ve=d(),Ce=n("td"),Ce.innerHTML='<span class="label">String</span>',Qe=d(),k=n("td"),je=p(`Auto expand record relations. Ex.:
`),ne(q.$$.fragment),Je=p(`
Supports up to 6-levels depth nested relations expansion. `),Ke=n("br"),ze=p(`
The expanded relations will be appended to the record under the
`),Oe=n("code"),Oe.textContent="expand",Ge=p(" property (eg. "),Ae=n("code"),Ae.textContent='"expand": {"relField1": {...}, ...}',Xe=p(`).
`),Ze=n("br"),xe=p(`
Only the relations to which the request user has permissions to `),Te=n("strong"),Te.textContent="view",et=p(" will be expanded."),tt=d(),ne(F.$$.fragment),Ue=d(),K=n("div"),K.textContent="Responses",Me=d(),L=n("div"),z=n("div");for(let e=0;e<A.length;e+=1)A[e].c();at=d(),G=n("div");for(let e=0;e<S.length;e+=1)S[e].c();m(l,"class","m-b-sm"),m(u,"class","content txt-lg m-b-sm"),m(N,"class","m-b-xs"),m(ee,"class","label label-primary"),m(te,"class","content"),m(U,"class","alert alert-success"),m(V,"class","section-title"),m(M,"class","table-compact table-border m-b-base"),m(j,"class","section-title"),m(E,"class","table-compact table-border m-b-base"),m(K,"class","section-title"),m(z,"class","tabs-header compact combined left"),m(G,"class","tabs-content"),m(L,"class","tabs")},m(e,o){r(e,l,o),t(l,a),t(l,g),t(l,b),r(e,f,o),r(e,u,o),t(u,_),t(_,R),t(_,C),P&&P.m(C,null),t(_,O),t(_,B),t(_,Ee),r(e,ce,o),se(T,e,o),r(e,de,o),r(e,N,o),r(e,ue,o),r(e,U,o),t(U,ee),t(U,We),t(U,te),t(te,I),t(I,Le),t(I,pe),t(pe,fe),t(I,Be),r(e,he,o),r(e,V,o),r(e,be,o),r(e,M,o),t(M,me),t(M,qe),t(M,Q),t(Q,D),t(D,_e),t(D,Fe),t(D,ke),t(D,He),t(D,$),t($,Ye),v&&v.m($,null),t($,ge),w&&w.m($,null),t($,Se),y&&y.m($,null),t($,ve),t(Q,Ne),t(Q,we),r(e,ye,o),r(e,j,o),r(e,$e,o),r(e,E,o),t(E,Pe),t(E,Ie),t(E,J),t(J,W),t(W,Re),t(W,Ve),t(W,Ce),t(W,Qe),t(W,k),t(k,je),se(q,k,null),t(k,Je),t(k,Ke),t(k,ze),t(k,Oe),t(k,Ge),t(k,Ae),t(k,Xe),t(k,Ze),t(k,xe),t(k,Te),t(k,et),t(J,tt),se(F,J,null),r(e,Ue,o),r(e,K,o),r(e,Me,o),r(e,L,o),t(L,z);for(let h=0;h<A.length;h+=1)A[h]&&A[h].m(z,null);t(L,at),t(L,G);for(let h=0;h<S.length;h+=1)S[h]&&S[h].m(G,null);H=!0},p(e,[o]){var dt,ut;(!H||o&1)&&i!==(i=e[0].name+"")&&De(g,i),Y!==(Y=nt(e))&&(P&&P.d(1),P=Y&&Y(e),P&&(P.c(),P.m(C,null)));const h={};o&97&&(h.js=`
import PocketBase from 'pocketbase';
const pb = new PocketBase('${e[6]}');
...
const authData = await pb.collection('${(dt=e[0])==null?void 0:dt.name}').authWithPassword(
'${e[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
console.log(pb.authStore.isValid);
console.log(pb.authStore.token);
console.log(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`),o&97&&(h.dart=`
import 'package:pocketbase/pocketbase.dart';
final pb = PocketBase('${e[6]}');
...
final authData = await pb.collection('${(ut=e[0])==null?void 0:ut.name}').authWithPassword(
'${e[5]}',
'YOUR_PASSWORD',
);
// after the above you can also access the auth data from the authStore
print(pb.authStore.isValid);
print(pb.authStore.token);
print(pb.authStore.model.id);
// "logout" the last authenticated account
pb.authStore.clear();
`),T.$set(h),(!H||o&1)&&le!==(le=e[0].name+"")&&De(fe,le),e[1]?v||(v=mt(),v.c(),v.m($,ge)):v&&(v.d(1),v=null),e[1]&&e[2]?w||(w=_t(),w.c(),w.m($,Se)):w&&(w.d(1),w=null),e[2]?y||(y=kt(),y.c(),y.m($,ve)):y&&(y.d(1),y=null),o&24&&(ae=oe(e[4]),A=pt(A,o,st,1,e,ae,lt,z,Pt,gt,null,bt)),o&24&&(X=oe(e[4]),Rt(),S=pt(S,o,it,1,e,X,ot,G,Ct,St,null,ht),Ot())},i(e){if(!H){Z(T.$$.fragment,e),Z(q.$$.fragment,e),Z(F.$$.fragment,e);for(let o=0;o<X.length;o+=1)Z(S[o]);H=!0}},o(e){x(T.$$.fragment,e),x(q.$$.fragment,e),x(F.$$.fragment,e);for(let o=0;o<S.length;o+=1)x(S[o]);H=!1},d(e){e&&(c(l),c(f),c(u),c(ce),c(de),c(N),c(ue),c(U),c(he),c(V),c(be),c(M),c(ye),c(j),c($e),c(E),c(Ue),c(K),c(Me),c(L)),P&&P.d(),ie(T,e),v&&v.d(),w&&w.d(),y&&y.d(),ie(q),ie(F);for(let o=0;o<A.length;o+=1)A[o].d();for(let o=0;o<S.length;o+=1)S[o].d()}}}function Bt(s,l,a){let i,g,b,f,{collection:u}=l,_=200,R=[];const C=O=>a(3,_=O.code);return s.$$set=O=>{"collection"in O&&a(0,u=O.collection)},s.$$.update=()=>{var O,B;s.$$.dirty&1&&a(2,g=(O=u==null?void 0:u.options)==null?void 0:O.allowEmailAuth),s.$$.dirty&1&&a(1,b=(B=u==null?void 0:u.options)==null?void 0:B.allowUsernameAuth),s.$$.dirty&6&&a(5,f=b&&g?"YOUR_USERNAME_OR_EMAIL":b?"YOUR_USERNAME":"YOUR_EMAIL"),s.$$.dirty&1&&a(4,R=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:ft.dummyCollectionRecord(u)},null,2)},{code:400,body:`
{
"code": 400,
"message": "Failed to authenticate.",
"data": {
"identity": {
"code": "validation_required",
"message": "Missing required value."
}
}
}
`}])},a(6,i=ft.getApiExampleUrl(At.baseUrl)),[u,b,g,_,R,f,i,C]}class Yt extends wt{constructor(l){super(),yt(this,l,Bt,Lt,$t,{collection:0})}}export{Yt as default};
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff. Show more