mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-09-20 13:30:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1921aeef8 | ||
|
|
012546e838 | ||
|
|
f792a9e08d | ||
|
|
a8b2f0f6f1 | ||
|
|
f7d4722052 | ||
|
|
3dc1bf6fa7 | ||
|
|
d3cc87abee | ||
|
|
19ad827302 | ||
|
|
40830b6c43 | ||
|
|
f234132629 | ||
|
|
9b880f5ab4 | ||
|
|
d5775ff657 | ||
|
|
41ba7e12e1 | ||
|
|
5ef6b3a8be | ||
|
|
c673d9d314 | ||
|
|
a7aa3da67e | ||
|
|
0439af458f | ||
|
|
6f3241399c | ||
|
|
19ccc70fe5 | ||
|
|
9033cd109e | ||
|
|
e1773eead0 | ||
|
|
f6fff85d07 | ||
|
|
166d2eafc7 | ||
|
|
d710446c71 | ||
|
|
8f6cb5ad2d | ||
|
|
775417ac2b | ||
|
|
079616ee8e | ||
|
|
26b794aa08 | ||
|
|
d55610511d | ||
|
|
a00606d969 | ||
|
|
4abc8ae021 | ||
|
|
f91f009fce | ||
|
|
7fc1d979dd | ||
|
|
ede7804a80 | ||
|
|
cea287a2c1 | ||
|
|
9e03ac4dc4 | ||
|
|
e713de1e44 | ||
|
|
233ab62f8e | ||
|
|
3449084e54 |
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '>=1.19.4'
|
||||
go-version: '>=1.19.5'
|
||||
|
||||
# This step usually is not needed because the /ui/dist is pregenerated locally
|
||||
# but its here to ensure that each release embeds the latest admin ui artifacts.
|
||||
|
||||
+121
@@ -1,3 +1,124 @@
|
||||
## v0.11.2
|
||||
|
||||
- Fixed `fs.DeleteByPrefix()` hang on invalid S3 settings ([#1575](https://github.com/pocketbase/pocketbase/discussions/1575#discussioncomment-4661089)).
|
||||
|
||||
- Updated file(s) delete to run in the background on record/collection delete to avoid blocking the delete model transaction.
|
||||
_Currently the cascade files delete operation is treated as "non-critical" and in case of an error it is just logged during debug._
|
||||
_This will be improved in the near future with the planned async job queue implementation._
|
||||
|
||||
|
||||
## v0.11.1
|
||||
|
||||
- Unescaped path parameter values ([#1552](https://github.com/pocketbase/pocketbase/issues/1552)).
|
||||
|
||||
|
||||
## v0.11.0
|
||||
|
||||
- Added `+` and `-` body field modifiers for `number`, `files`, `select` and `relation` fields.
|
||||
```js
|
||||
{
|
||||
// oldValue + 2
|
||||
"someNumber+": 2,
|
||||
|
||||
// oldValue + ["id1", "id2"] - ["id3"]
|
||||
"someRelation+": ["id1", "id2"],
|
||||
"someRelation-": ["id3"],
|
||||
|
||||
// delete single file by its name (file fields supports only the "-" modifier!)
|
||||
"someFile-": "filename.png",
|
||||
}
|
||||
```
|
||||
_Note1: `@request.data.someField` will contain the final resolved value._
|
||||
|
||||
_Note2: The old index (`"field.0":null`) and filename (`"field.filename.png":null`) based suffixed syntax for deleting files is still supported._
|
||||
|
||||
- ! Added support for multi-match/match-all request data and collection multi-valued fields (`select`, `relation`) conditions.
|
||||
If you want a "at least one of" type of condition, you can prefix the operator with `?`.
|
||||
```js
|
||||
// for each someRelA.someRelB record require the "status" field to be "active"
|
||||
someRelA.someRelB.status = "active"
|
||||
|
||||
// OR for "at least one of" condition
|
||||
someRelA.someRelB.status ?= "active"
|
||||
```
|
||||
_**Note: Previously the behavior for multi-valued fields was as the "at least one of" type.
|
||||
The release comes with system db migration that will update your existing API rules (if needed) to preserve the compatibility.
|
||||
If you have multi-select or multi-relation filter checks in your client-side code and want to preserve the old behavior, you'll have to prefix with `?` your operators.**_
|
||||
|
||||
- Added support for querying `@request.data.someRelField.*` relation fields.
|
||||
```js
|
||||
// example submitted data: {"someRel": "REL_RECORD_ID"}
|
||||
@request.data.someRel.status = "active"
|
||||
```
|
||||
|
||||
- Added `:isset` modifier for the static request data fields.
|
||||
```js
|
||||
// prevent changing the "role" field
|
||||
@request.data.role:isset = false
|
||||
```
|
||||
|
||||
- Added `:length` modifier for the arrayable request data and collection fields (`select`, `file`, `relation`).
|
||||
```js
|
||||
// example submitted data: {"someSelectField": ["val1", "val2"]}
|
||||
@request.data.someSelectField:length = 2
|
||||
|
||||
// check existing record field length
|
||||
someSelectField:length = 2
|
||||
```
|
||||
|
||||
- Added `:each` modifier support for the multi-`select` request data and collection field.
|
||||
```js
|
||||
// check if all selected rows has "pb_" prefix
|
||||
roles:each ~ 'pb_%'
|
||||
```
|
||||
|
||||
- Improved the Admin UI filters autocomplete.
|
||||
|
||||
- Added `@random` sort key for `RANDOM()` sorted list results.
|
||||
|
||||
- Added Strava OAuth2 provider ([#1443](https://github.com/pocketbase/pocketbase/pull/1443); thanks @szsascha).
|
||||
|
||||
- Added Gitee OAuth2 provider ([#1448](https://github.com/pocketbase/pocketbase/pull/1448); thanks @yuxiang-gao).
|
||||
|
||||
- Added IME status check to the textarea keydown handler ([#1370](https://github.com/pocketbase/pocketbase/pull/1370); thanks @tenthree).
|
||||
|
||||
- Added `filesystem.NewFileFromBytes()` helper ([#1420](https://github.com/pocketbase/pocketbase/pull/1420); thanks @dschissler).
|
||||
|
||||
- Added support for reordering uploaded multiple files.
|
||||
|
||||
- Added `webp` to the default images mime type presets list ([#1469](https://github.com/pocketbase/pocketbase/pull/1469); thanks @khairulhaaziq).
|
||||
|
||||
- Added the OAuth2 refresh token to the auth meta response ([#1487](https://github.com/pocketbase/pocketbase/issues/1487)).
|
||||
|
||||
- Fixed the text wrapping in the Admin UI listing searchbar ([#1416](https://github.com/pocketbase/pocketbase/issues/1416)).
|
||||
|
||||
- Fixed number field value output in the records listing ([#1447](https://github.com/pocketbase/pocketbase/issues/1447)).
|
||||
|
||||
- Fixed duplicated settings view pages caused by uncompleted transitions ([#1498](https://github.com/pocketbase/pocketbase/issues/1498)).
|
||||
|
||||
- Allowed sending `Authorization` header with the `/auth-with-password` record and admin login requests ([#1494](https://github.com/pocketbase/pocketbase/discussions/1494)).
|
||||
|
||||
- `migrate down` now reverts migrations in the applied order.
|
||||
|
||||
- Added additional list-bucket check in the S3 config test API.
|
||||
|
||||
- Other minor improvements.
|
||||
|
||||
|
||||
## v0.10.4
|
||||
|
||||
- Fixed `Record.MergeExpand` panic when the main model expand map is not initialized ([#1365](https://github.com/pocketbase/pocketbase/issues/1365)).
|
||||
|
||||
|
||||
## v0.10.3
|
||||
|
||||
- ! Renamed the metadata key `original_filename` to `original-filename` due to an S3 file upload error caused by the underscore character ([#1343](https://github.com/pocketbase/pocketbase/pull/1343); thanks @yuxiang-gao).
|
||||
|
||||
- Fixed request verification docs api url ([#1332](https://github.com/pocketbase/pocketbase/pull/1332); thanks @JoyMajumdar2001)
|
||||
|
||||
- Excluded `collectionId` and `collectionName` from the displayable relation props list ([1322](https://github.com/pocketbase/pocketbase/issues/1322); thanks @dhall2).
|
||||
|
||||
|
||||
## v0.10.2
|
||||
|
||||
- Fixed nested multiple expands with shared path ([#586](https://github.com/pocketbase/pocketbase/issues/586#issuecomment-1357784227)).
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2022, Gani Georgiev
|
||||
Copyright (c) 2022 - present, Gani Georgiev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ func bindAdminApi(app core.App, rg *echo.Group) {
|
||||
api := adminApi{app: app}
|
||||
|
||||
subGroup := rg.Group("/admins", ActivityLogger(app))
|
||||
subGroup.POST("/auth-with-password", api.authWithPassword, RequireGuestOnly())
|
||||
subGroup.POST("/auth-with-password", api.authWithPassword)
|
||||
subGroup.POST("/request-password-reset", api.requestPasswordReset)
|
||||
subGroup.POST("/confirm-password-reset", api.confirmPasswordReset)
|
||||
subGroup.POST("/auth-refresh", api.authRefresh, RequireAdminAuth())
|
||||
|
||||
+14
-8
@@ -48,6 +48,20 @@ func TestAdminAuthWithEmail(t *testing.T) {
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
},
|
||||
{
|
||||
Name: "valid email/password (guest)",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/admins/auth-with-password",
|
||||
Body: strings.NewReader(`{"identity":"test@example.com","password":"1234567890"}`),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"admin":{"id":"sywbhecnh46rhm0"`,
|
||||
`"token":`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnAdminAuthRequest": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "valid email/password (already authorized)",
|
||||
Method: http.MethodPost,
|
||||
@@ -56,14 +70,6 @@ func TestAdminAuthWithEmail(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4MTYwMH0.han3_sG65zLddpcX2ic78qgy7FKecuPfOpFa8Dvi5Bg",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"message":"The request can be accessed only by guests.","data":{}`},
|
||||
},
|
||||
{
|
||||
Name: "valid email/password (guest)",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/admins/auth-with-password",
|
||||
Body: strings.NewReader(`{"identity":"test@example.com","password":"1234567890"}`),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"admin":{"id":"sywbhecnh46rhm0"`,
|
||||
|
||||
@@ -26,6 +26,13 @@ func InitApi(app core.App) (*echo.Echo, error) {
|
||||
e := echo.New()
|
||||
e.Debug = app.IsDebug()
|
||||
|
||||
// configure a custom router
|
||||
e.ResetRouterCreator(func(ec *echo.Echo) echo.Router {
|
||||
return echo.NewRouter(echo.RouterConfig{
|
||||
UnescapePathParamValues: true,
|
||||
})
|
||||
})
|
||||
|
||||
// default middlewares
|
||||
e.Pre(middleware.RemoveTrailingSlashWithConfig(middleware.RemoveTrailingSlashConfig{
|
||||
Skipper: func(c echo.Context) bool {
|
||||
|
||||
@@ -66,6 +66,22 @@ func TestCustomRoutesAndErrorsHandling(t *testing.T) {
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"test123"},
|
||||
},
|
||||
{
|
||||
Name: "custom route with url encoded parameter",
|
||||
Method: http.MethodGet,
|
||||
Url: "/a%2Bb%2Bc",
|
||||
BeforeTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
e.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/:param",
|
||||
Handler: func(c echo.Context) error {
|
||||
return c.String(200, c.PathParam("param"))
|
||||
},
|
||||
})
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"a+b+c"},
|
||||
},
|
||||
{
|
||||
Name: "route with HTTPError",
|
||||
Method: http.MethodGet,
|
||||
|
||||
+15
-10
@@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
@@ -42,7 +43,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
ExpectedContent: []string{
|
||||
`"page":1`,
|
||||
`"perPage":30`,
|
||||
`"totalItems":7`,
|
||||
`"totalItems":8`,
|
||||
`"items":[{`,
|
||||
`"id":"_pb_users_auth_"`,
|
||||
`"id":"v851q4r790rhknl"`,
|
||||
@@ -51,6 +52,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
`"id":"sz5l5z67tg7gku0"`,
|
||||
`"id":"wzlqyes4orhoygb"`,
|
||||
`"id":"4d1blo5cuycfaca"`,
|
||||
`"id":"9n89pl5vkct6330"`,
|
||||
`"type":"auth"`,
|
||||
`"type":"base"`,
|
||||
},
|
||||
@@ -69,10 +71,10 @@ func TestCollectionsList(t *testing.T) {
|
||||
ExpectedContent: []string{
|
||||
`"page":2`,
|
||||
`"perPage":2`,
|
||||
`"totalItems":7`,
|
||||
`"totalItems":8`,
|
||||
`"items":[{`,
|
||||
`"id":"v851q4r790rhknl"`,
|
||||
`"id":"4d1blo5cuycfaca"`,
|
||||
`"id":"wzlqyes4orhoygb"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnCollectionsListRequest": 1,
|
||||
@@ -99,12 +101,13 @@ func TestCollectionsList(t *testing.T) {
|
||||
ExpectedContent: []string{
|
||||
`"page":1`,
|
||||
`"perPage":30`,
|
||||
`"totalItems":4`,
|
||||
`"totalItems":5`,
|
||||
`"items":[{`,
|
||||
`"id":"wsmn24bux7wo113"`,
|
||||
`"id":"sz5l5z67tg7gku0"`,
|
||||
`"id":"wzlqyes4orhoygb"`,
|
||||
`"id":"4d1blo5cuycfaca"`,
|
||||
`"id":"9n89pl5vkct6330"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnCollectionsListRequest": 1,
|
||||
@@ -230,6 +233,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
Delay: 100 * time.Millisecond,
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelBeforeDelete": 1,
|
||||
@@ -248,6 +252,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
Delay: 100 * time.Millisecond,
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelBeforeDelete": 1,
|
||||
@@ -786,7 +791,7 @@ func TestCollectionImport(t *testing.T) {
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := 7
|
||||
expected := 8
|
||||
if len(collections) != expected {
|
||||
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
|
||||
}
|
||||
@@ -814,7 +819,7 @@ func TestCollectionImport(t *testing.T) {
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := 7
|
||||
expected := 8
|
||||
if len(collections) != expected {
|
||||
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
|
||||
}
|
||||
@@ -856,7 +861,7 @@ func TestCollectionImport(t *testing.T) {
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := 7
|
||||
expected := 8
|
||||
if len(collections) != expected {
|
||||
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
|
||||
}
|
||||
@@ -909,7 +914,7 @@ func TestCollectionImport(t *testing.T) {
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := 10
|
||||
expected := 11
|
||||
if len(collections) != expected {
|
||||
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
|
||||
}
|
||||
@@ -996,8 +1001,8 @@ func TestCollectionImport(t *testing.T) {
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnCollectionsAfterImportRequest": 1,
|
||||
"OnCollectionsBeforeImportRequest": 1,
|
||||
"OnModelBeforeDelete": 5,
|
||||
"OnModelAfterDelete": 5,
|
||||
"OnModelBeforeDelete": 6,
|
||||
"OnModelAfterDelete": 6,
|
||||
"OnModelBeforeUpdate": 2,
|
||||
"OnModelAfterUpdate": 2,
|
||||
"OnModelBeforeCreate": 1,
|
||||
|
||||
+2
-2
@@ -35,8 +35,8 @@ func bindRecordAuthApi(app core.App, rg *echo.Group) {
|
||||
|
||||
subGroup.GET("/auth-methods", api.authMethods)
|
||||
subGroup.POST("/auth-refresh", api.authRefresh, RequireSameContextRecordAuth())
|
||||
subGroup.POST("/auth-with-oauth2", api.authWithOAuth2) // allow anyone so that we can link the OAuth2 profile with the authenticated record
|
||||
subGroup.POST("/auth-with-password", api.authWithPassword, RequireGuestOnly())
|
||||
subGroup.POST("/auth-with-oauth2", api.authWithOAuth2)
|
||||
subGroup.POST("/auth-with-password", api.authWithPassword)
|
||||
subGroup.POST("/request-password-reset", api.requestPasswordReset)
|
||||
subGroup.POST("/confirm-password-reset", api.confirmPasswordReset)
|
||||
subGroup.POST("/request-verification", api.requestVerification)
|
||||
|
||||
+46
-20
@@ -66,26 +66,6 @@ func TestRecordAuthMethodsList(t *testing.T) {
|
||||
|
||||
func TestRecordAuthWithPassword(t *testing.T) {
|
||||
scenarios := []tests.ApiScenario{
|
||||
{
|
||||
Name: "authenticated record",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/collections/users/auth-with-password",
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
},
|
||||
{
|
||||
Name: "authenticated admin",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/collections/users/auth-with-password",
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
},
|
||||
{
|
||||
Name: "invalid body format",
|
||||
Method: http.MethodPost,
|
||||
@@ -226,6 +206,52 @@ func TestRecordAuthWithPassword(t *testing.T) {
|
||||
"OnRecordAuthRequest": 1,
|
||||
},
|
||||
},
|
||||
|
||||
// with already authenticated record or admin
|
||||
{
|
||||
Name: "authenticated record",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/collections/users/auth-with-password",
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
|
||||
},
|
||||
Body: strings.NewReader(`{
|
||||
"identity":"test@example.com",
|
||||
"password":"1234567890"
|
||||
}`),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"record":{`,
|
||||
`"token":"`,
|
||||
`"id":"4q1xlclmfloku33"`,
|
||||
`"email":"test@example.com"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnRecordAuthRequest": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authenticated admin",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/collections/users/auth-with-password",
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
Body: strings.NewReader(`{
|
||||
"identity":"test@example.com",
|
||||
"password":"1234567890"
|
||||
}`),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"record":{`,
|
||||
`"token":"`,
|
||||
`"id":"4q1xlclmfloku33"`,
|
||||
`"email":"test@example.com"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnRecordAuthRequest": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
|
||||
+24
-7
@@ -166,6 +166,20 @@ func (api *recordApi) create(c echo.Context) error {
|
||||
|
||||
// temporary save the record and check it against the create rule
|
||||
if requestData.Admin == nil && collection.CreateRule != nil {
|
||||
testRecord := models.NewRecord(collection)
|
||||
|
||||
// replace modifiers fields so that the resolved value is always
|
||||
// available when accessing requestData.Data using just the field name
|
||||
if requestData.HasModifierDataKeys() {
|
||||
requestData.Data = testRecord.ReplaceModifers(requestData.Data)
|
||||
}
|
||||
|
||||
testForm := forms.NewRecordUpsert(api.app, testRecord)
|
||||
testForm.SetFullManageAccess(true)
|
||||
if err := testForm.LoadRequest(c.Request(), ""); err != nil {
|
||||
return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err)
|
||||
}
|
||||
|
||||
createRuleFunc := func(q *dbx.SelectQuery) error {
|
||||
if *collection.CreateRule == "" {
|
||||
return nil // no create rule to resolve
|
||||
@@ -181,13 +195,6 @@ func (api *recordApi) create(c echo.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
testRecord := models.NewRecord(collection)
|
||||
testForm := forms.NewRecordUpsert(api.app, testRecord)
|
||||
testForm.SetFullManageAccess(true)
|
||||
if err := testForm.LoadRequest(c.Request(), ""); err != nil {
|
||||
return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err)
|
||||
}
|
||||
|
||||
testErr := testForm.DrySubmit(func(txDao *daos.Dao) error {
|
||||
foundRecord, err := txDao.FindRecordById(collection.Id, testRecord.Id, createRuleFunc)
|
||||
if err != nil {
|
||||
@@ -258,6 +265,16 @@ func (api *recordApi) update(c echo.Context) error {
|
||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||
}
|
||||
|
||||
// eager fetch the record so that the modifier field values are replaced
|
||||
// and available when accessing requestData.Data using just the field name
|
||||
if requestData.HasModifierDataKeys() {
|
||||
record, err := api.app.Dao().FindRecordById(collection.Id, recordId)
|
||||
if err != nil || record == nil {
|
||||
return NewNotFoundError("", err)
|
||||
}
|
||||
requestData.Data = record.ReplaceModifers(requestData.Data)
|
||||
}
|
||||
|
||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||
if requestData.Admin == nil && collection.UpdateRule != nil && *collection.UpdateRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
||||
|
||||
+162
-1
@@ -2,10 +2,12 @@ package apis_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
@@ -209,6 +211,50 @@ func TestRecordCrudList(t *testing.T) {
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: ":rule modifer",
|
||||
Method: http.MethodGet,
|
||||
Url: "/api/collections/demo5/records",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"page":1`,
|
||||
`"perPage":30`,
|
||||
`"totalPages":1`,
|
||||
`"totalItems":1`,
|
||||
`"items":[{`,
|
||||
`"id":"qjeql998mtp1azp"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: "multi-match - at least one of",
|
||||
Method: http.MethodGet,
|
||||
Url: "/api/collections/demo4/records?filter=" + url.QueryEscape("rel_many_no_cascade_required.files:length?=2"),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"page":1`,
|
||||
`"perPage":30`,
|
||||
`"totalPages":1`,
|
||||
`"totalItems":1`,
|
||||
`"items":[{`,
|
||||
`"id":"qzaqccwrmva4o1n"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: "multi-match - all",
|
||||
Method: http.MethodGet,
|
||||
Url: "/api/collections/demo4/records?filter=" + url.QueryEscape("rel_many_no_cascade_required.files:length=2"),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"page":1`,
|
||||
`"perPage":30`,
|
||||
`"totalPages":0`,
|
||||
`"totalItems":0`,
|
||||
`"items":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
|
||||
// auth collection checks
|
||||
// -----------------------------------------------------------
|
||||
@@ -691,6 +737,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
// users, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
|
||||
},
|
||||
Delay: 100 * time.Millisecond,
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterDelete": 3, // +2 because of the external auths
|
||||
@@ -716,6 +763,25 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "@request :isset (rule failure check)",
|
||||
Method: http.MethodDelete,
|
||||
Url: "/api/collections/demo5/records/la4y2w4o98acwuj",
|
||||
ExpectedStatus: 404,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
},
|
||||
{
|
||||
Name: "@request :isset (rule pass check)",
|
||||
Method: http.MethodDelete,
|
||||
Url: "/api/collections/demo5/records/la4y2w4o98acwuj?test=1",
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterDelete": 1,
|
||||
"OnModelBeforeDelete": 1,
|
||||
"OnRecordAfterDeleteRequest": 1,
|
||||
"OnRecordBeforeDeleteRequest": 1,
|
||||
},
|
||||
},
|
||||
|
||||
// cascade delete checks
|
||||
// -----------------------------------------------------------
|
||||
@@ -730,7 +796,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnRecordBeforeDeleteRequest": 1,
|
||||
"OnModelBeforeUpdate": 1, // self_rel_many update of test1 record
|
||||
"OnModelBeforeUpdate": 2, // self_rel_many update of test1 record + rel_one_cascade demo4 cascaded in demo5
|
||||
"OnModelBeforeDelete": 2, // the record itself + rel_one_cascade of test1 record
|
||||
},
|
||||
},
|
||||
@@ -758,6 +824,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
Delay: 100 * time.Millisecond,
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelBeforeDelete": 2,
|
||||
@@ -1092,6 +1159,63 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
|
||||
// fields modifier checks
|
||||
// -----------------------------------------------------------
|
||||
{
|
||||
Name: "trying to delete a record while being part of a non-cascade required relation",
|
||||
Method: http.MethodDelete,
|
||||
Url: "/api/collections/demo3/records/7nwo8tuiatetxdm",
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnRecordBeforeDeleteRequest": 1,
|
||||
"OnModelBeforeUpdate": 2, // self_rel_many update of test1 record + rel_one_cascade demo4 cascaded in demo5
|
||||
"OnModelBeforeDelete": 2, // the record itself + rel_one_cascade of test1 record
|
||||
},
|
||||
},
|
||||
|
||||
// check whether if @request.data modifer fields are properly resolved
|
||||
// -----------------------------------------------------------
|
||||
{
|
||||
Name: "@request.data.field with compute modifers (rule failure check)",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/collections/demo5/records",
|
||||
Body: strings.NewReader(`{
|
||||
"total":1,
|
||||
"total+":4,
|
||||
"total-":1
|
||||
}`),
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
`"data":{}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "@request.data.field with compute modifers (rule pass check)",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/collections/demo5/records",
|
||||
Body: strings.NewReader(`{
|
||||
"total":1,
|
||||
"total+":3,
|
||||
"total-":1
|
||||
}`),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"id":"`,
|
||||
`"collectionName":"demo5"`,
|
||||
`"total":3`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterCreate": 1,
|
||||
"OnModelBeforeCreate": 1,
|
||||
"OnRecordAfterCreateRequest": 1,
|
||||
"OnRecordBeforeCreateRequest": 1,
|
||||
},
|
||||
},
|
||||
|
||||
// auth records
|
||||
// -----------------------------------------------------------
|
||||
{
|
||||
@@ -1501,6 +1625,43 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
|
||||
// check whether if @request.data modifer fields are properly resolved
|
||||
// -----------------------------------------------------------
|
||||
{
|
||||
Name: "@request.data.field with compute modifers (rule failure check)",
|
||||
Method: http.MethodPatch,
|
||||
Url: "/api/collections/demo5/records/la4y2w4o98acwuj",
|
||||
Body: strings.NewReader(`{
|
||||
"total+":3,
|
||||
"total-":1
|
||||
}`),
|
||||
ExpectedStatus: 404,
|
||||
ExpectedContent: []string{
|
||||
`"data":{}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "@request.data.field with compute modifers (rule pass check)",
|
||||
Method: http.MethodPatch,
|
||||
Url: "/api/collections/demo5/records/la4y2w4o98acwuj",
|
||||
Body: strings.NewReader(`{
|
||||
"total+":2,
|
||||
"total-":1
|
||||
}`),
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"id":"la4y2w4o98acwuj"`,
|
||||
`"collectionName":"demo5"`,
|
||||
`"total":3`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelAfterUpdate": 1,
|
||||
"OnModelBeforeUpdate": 1,
|
||||
"OnRecordAfterUpdateRequest": 1,
|
||||
"OnRecordBeforeUpdateRequest": 1,
|
||||
},
|
||||
},
|
||||
|
||||
// auth records
|
||||
// -----------------------------------------------------------
|
||||
{
|
||||
|
||||
@@ -15,11 +15,6 @@ import (
|
||||
|
||||
const ContextRequestDataKey = "requestData"
|
||||
|
||||
// Deprecated: Will be removed after v0.9. Use apis.RequestData(c) instead.
|
||||
func GetRequestData(c echo.Context) *models.RequestData {
|
||||
return RequestData(c)
|
||||
}
|
||||
|
||||
// RequestData exports cached common request data fields
|
||||
// (query, body, logged auth state, etc.) from the provided context.
|
||||
func RequestData(c echo.Context) *models.RequestData {
|
||||
|
||||
+7
-3
@@ -1,6 +1,7 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
@@ -91,14 +92,17 @@ func (api *settingsApi) testS3(c echo.Context) error {
|
||||
}
|
||||
defer fs.Close()
|
||||
|
||||
testFileKey := "pb_test_" + security.PseudorandomString(5) + "/test.txt"
|
||||
testPrefix := "pb_settings_test_" + security.PseudorandomString(5)
|
||||
testFileKey := testPrefix + "/test.txt"
|
||||
|
||||
// try to upload a test file
|
||||
if err := fs.Upload([]byte("test"), testFileKey); err != nil {
|
||||
return NewBadRequestError("Failed to upload a test file. Raw error: \n"+err.Error(), nil)
|
||||
}
|
||||
|
||||
if err := fs.Delete(testFileKey); err != nil {
|
||||
return NewBadRequestError("Failed to delete a test file. Raw error: \n"+err.Error(), nil)
|
||||
// test prefix deletion (ensures that both bucket list and delete works)
|
||||
if errs := fs.DeletePrefix(testPrefix); len(errs) > 0 {
|
||||
return NewBadRequestError(fmt.Sprintf("Failed to delete a test file. Raw error: %v", errs), nil)
|
||||
}
|
||||
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
|
||||
@@ -58,6 +58,8 @@ func TestSettingsList(t *testing.T) {
|
||||
`"spotifyAuth":{`,
|
||||
`"kakaoAuth":{`,
|
||||
`"twitchAuth":{`,
|
||||
`"stravaAuth":{`,
|
||||
`"giteeAuth":{`,
|
||||
`"secret":"******"`,
|
||||
`"clientSecret":"******"`,
|
||||
},
|
||||
@@ -125,6 +127,8 @@ func TestSettingsSet(t *testing.T) {
|
||||
`"spotifyAuth":{`,
|
||||
`"kakaoAuth":{`,
|
||||
`"twitchAuth":{`,
|
||||
`"stravaAuth":{`,
|
||||
`"giteeAuth":{`,
|
||||
`"secret":"******"`,
|
||||
`"clientSecret":"******"`,
|
||||
`"appName":"acme_test"`,
|
||||
@@ -181,6 +185,8 @@ func TestSettingsSet(t *testing.T) {
|
||||
`"spotifyAuth":{`,
|
||||
`"kakaoAuth":{`,
|
||||
`"twitchAuth":{`,
|
||||
`"stravaAuth":{`,
|
||||
`"giteeAuth":{`,
|
||||
`"secret":"******"`,
|
||||
`"clientSecret":"******"`,
|
||||
`"appName":"update_test"`,
|
||||
|
||||
+2
-1
@@ -78,7 +78,8 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||
GetCertificate: certManager.GetCertificate,
|
||||
NextProtos: []string{acme.ALPNProto},
|
||||
},
|
||||
ReadTimeout: 60 * time.Second,
|
||||
ReadTimeout: 5 * time.Minute,
|
||||
ReadHeaderTimeout: 30 * time.Second,
|
||||
// WriteTimeout: 60 * time.Second, // breaks sse!
|
||||
Handler: router,
|
||||
Addr: mainAddr,
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import (
|
||||
type App interface {
|
||||
// Deprecated:
|
||||
// This method may get removed in the near future.
|
||||
// It is recommended to access the logs db instance from app.Dao().DB() or
|
||||
// It is recommended to access the app db instance from app.Dao().DB() or
|
||||
// if you want more flexibility - app.Dao().ConcurrentDB() and app.Dao().NonconcurrentDB().
|
||||
//
|
||||
// DB returns the default app database instance.
|
||||
|
||||
+17
-7
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
"github.com/pocketbase/pocketbase/tools/mailer"
|
||||
"github.com/pocketbase/pocketbase/tools/routine"
|
||||
"github.com/pocketbase/pocketbase/tools/store"
|
||||
"github.com/pocketbase/pocketbase/tools/subscriptions"
|
||||
)
|
||||
@@ -939,20 +940,29 @@ func (app *BaseApp) registerDefaultHooks() {
|
||||
|
||||
failed := fs.DeletePrefix(prefix)
|
||||
if len(failed) > 0 {
|
||||
return errors.New("Failed to delete the files at " + prefix)
|
||||
return errors.New("failed to delete the files at " + prefix)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// delete storage files from deleted Collection, Records, etc.
|
||||
// try to delete the storage files from deleted Collection, Records, etc. model
|
||||
app.OnModelAfterDelete().Add(func(e *ModelEvent) error {
|
||||
if m, ok := e.Model.(models.FilesManager); ok && m.BaseFilesPath() != "" {
|
||||
if err := deletePrefix(m.BaseFilesPath()); err != nil && app.IsDebug() {
|
||||
// non critical error - only log for debug
|
||||
// (usually could happen because of S3 api limits)
|
||||
log.Println(err)
|
||||
}
|
||||
prefix := m.BaseFilesPath()
|
||||
|
||||
// run in the background for "optimistic" delete to avoid
|
||||
// blocking the delete transaction
|
||||
//
|
||||
// @todo consider creating a bg process queue so that the
|
||||
// call could be "retried" in case of a failure.
|
||||
routine.FireAndForget(func() {
|
||||
if err := deletePrefix(prefix); err != nil && app.IsDebug() {
|
||||
// non critical error - only log for debug
|
||||
// (usually could happen because of S3 api limits)
|
||||
log.Println(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+2
-2
@@ -97,8 +97,8 @@ func (dao *Dao) IsAdminEmailUnique(email string, excludeIds ...string) bool {
|
||||
AndWhere(dbx.HashExp{"email": email}).
|
||||
Limit(1)
|
||||
|
||||
if len(excludeIds) > 0 {
|
||||
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(excludeIds)...))
|
||||
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
|
||||
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(uniqueExcludeIds)...))
|
||||
}
|
||||
|
||||
var exists bool
|
||||
|
||||
+5
-4
@@ -65,8 +65,7 @@ func (dao *Dao) IsCollectionNameUnique(name string, excludeIds ...string) bool {
|
||||
AndWhere(dbx.NewExp("LOWER([[name]])={:name}", dbx.Params{"name": strings.ToLower(name)})).
|
||||
Limit(1)
|
||||
|
||||
if len(excludeIds) > 0 {
|
||||
uniqueExcludeIds := list.NonzeroUniques(excludeIds)
|
||||
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
|
||||
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(uniqueExcludeIds)...))
|
||||
}
|
||||
|
||||
@@ -85,15 +84,17 @@ func (dao *Dao) FindCollectionReferences(collection *models.Collection, excludeI
|
||||
collections := []*models.Collection{}
|
||||
|
||||
query := dao.CollectionQuery()
|
||||
if len(excludeIds) > 0 {
|
||||
uniqueExcludeIds := list.NonzeroUniques(excludeIds)
|
||||
|
||||
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
|
||||
query.AndWhere(dbx.NotIn("id", list.ToInterfaceSlice(uniqueExcludeIds)...))
|
||||
}
|
||||
|
||||
if err := query.All(&collections); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := map[*models.Collection][]*schema.SchemaField{}
|
||||
|
||||
for _, c := range collections {
|
||||
for _, f := range c.Schema.Fields() {
|
||||
if f.Type != schema.FieldTypeRelation {
|
||||
|
||||
+15
-8
@@ -37,7 +37,7 @@ func TestFindCollectionsByType(t *testing.T) {
|
||||
{"", false, 0},
|
||||
{"unknown", false, 0},
|
||||
{models.CollectionTypeAuth, false, 3},
|
||||
{models.CollectionTypeBase, false, 4},
|
||||
{models.CollectionTypeBase, false, 5},
|
||||
}
|
||||
|
||||
for i, scenario := range scenarios {
|
||||
@@ -122,7 +122,13 @@ func TestFindCollectionReferences(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
result, err := app.Dao().FindCollectionReferences(collection, collection.Id)
|
||||
result, err := app.Dao().FindCollectionReferences(
|
||||
collection,
|
||||
collection.Id,
|
||||
// test whether "nonempty" exclude ids condition will be skipped
|
||||
"",
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -296,7 +302,7 @@ func TestImportCollections(t *testing.T) {
|
||||
name: "empty collections",
|
||||
jsonData: `[]`,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
},
|
||||
{
|
||||
name: "minimal collection import",
|
||||
@@ -306,7 +312,7 @@ func TestImportCollections(t *testing.T) {
|
||||
]`,
|
||||
deleteMissing: false,
|
||||
expectError: false,
|
||||
expectCollectionsCount: 9,
|
||||
expectCollectionsCount: 10,
|
||||
},
|
||||
{
|
||||
name: "minimal collection import + failed beforeRecordsSync",
|
||||
@@ -318,7 +324,7 @@ func TestImportCollections(t *testing.T) {
|
||||
},
|
||||
deleteMissing: false,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
},
|
||||
{
|
||||
name: "minimal collection import + successful beforeRecordsSync",
|
||||
@@ -330,7 +336,7 @@ func TestImportCollections(t *testing.T) {
|
||||
},
|
||||
deleteMissing: false,
|
||||
expectError: false,
|
||||
expectCollectionsCount: 8,
|
||||
expectCollectionsCount: 9,
|
||||
},
|
||||
{
|
||||
name: "new + update + delete system collection",
|
||||
@@ -366,7 +372,7 @@ func TestImportCollections(t *testing.T) {
|
||||
]`,
|
||||
deleteMissing: true,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
},
|
||||
{
|
||||
name: "new + update + delete non-system collection",
|
||||
@@ -495,7 +501,7 @@ func TestImportCollections(t *testing.T) {
|
||||
]`,
|
||||
deleteMissing: false,
|
||||
expectError: false,
|
||||
expectCollectionsCount: 8,
|
||||
expectCollectionsCount: 9,
|
||||
afterTestFunc: func(testApp *tests.TestApp, resultCollections []*models.Collection) {
|
||||
expectedCollectionFields := map[string]int{
|
||||
"nologin": 1,
|
||||
@@ -503,6 +509,7 @@ func TestImportCollections(t *testing.T) {
|
||||
"demo2": 2,
|
||||
"demo3": 2,
|
||||
"demo4": 11,
|
||||
"demo5": 5,
|
||||
"new_import": 1,
|
||||
}
|
||||
for name, expectedCount := range expectedCollectionFields {
|
||||
|
||||
+18
-47
@@ -3,7 +3,6 @@ package daos
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
@@ -84,7 +83,7 @@ func (dao *Dao) FindRecordsByIds(
|
||||
}
|
||||
}
|
||||
|
||||
rows := []dbx.NullStringMap{}
|
||||
rows := make([]dbx.NullStringMap, 0, len(recordIds))
|
||||
if err := query.All(&rows); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -192,8 +191,7 @@ func (dao *Dao) IsRecordValueUnique(
|
||||
AndWhere(expr).
|
||||
Limit(1)
|
||||
|
||||
if len(excludeIds) > 0 {
|
||||
uniqueExcludeIds := list.NonzeroUniques(excludeIds)
|
||||
if uniqueExcludeIds := list.NonzeroUniques(excludeIds); len(uniqueExcludeIds) > 0 {
|
||||
query.AndWhere(dbx.NotIn(collection.Name+".id", list.ToInterfaceSlice(uniqueExcludeIds)...))
|
||||
}
|
||||
|
||||
@@ -390,8 +388,7 @@ func (dao *Dao) DeleteRecord(record *models.Record) error {
|
||||
})
|
||||
}
|
||||
|
||||
// cascadeRecordDelete triggers cascade deletion for the provided references
|
||||
// and split the work to a batched set of go routines.
|
||||
// cascadeRecordDelete triggers cascade deletion for the provided references.
|
||||
//
|
||||
// NB! This method is expected to be called inside a transaction.
|
||||
func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.Collection][]*schema.SchemaField) error {
|
||||
@@ -401,20 +398,21 @@ func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.
|
||||
for _, field := range fields {
|
||||
recordTableName := inflector.Columnify(refCollection.Name)
|
||||
prefixedFieldName := recordTableName + "." + inflector.Columnify(field.Name)
|
||||
|
||||
// @todo optimize single relation lookup in v0.12+
|
||||
query := dao.RecordQuery(refCollection).
|
||||
Distinct(true).
|
||||
LeftJoin(fmt.Sprintf(
|
||||
// note: the case is used to normalize value access for single and multiple relations.
|
||||
AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": mainRecord.Id})).
|
||||
InnerJoin(fmt.Sprintf(
|
||||
// note: the case is used to normalize the value access
|
||||
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END) as {{%s}}`,
|
||||
prefixedFieldName, prefixedFieldName, prefixedFieldName, uniqueJsonEachAlias,
|
||||
), nil).
|
||||
AndWhere(dbx.Not(dbx.HashExp{recordTableName + ".id": mainRecord.Id})).
|
||||
AndWhere(dbx.HashExp{uniqueJsonEachAlias + ".value": mainRecord.Id})
|
||||
), dbx.HashExp{uniqueJsonEachAlias + ".value": mainRecord.Id})
|
||||
|
||||
// trigger cascade for each 1000 rel items until there is none
|
||||
batchSize := 1000
|
||||
// trigger cascade for each batchSize rel items until there is none
|
||||
batchSize := 4000
|
||||
rows := make([]dbx.NullStringMap, 0, batchSize)
|
||||
for {
|
||||
rows := make([]dbx.NullStringMap, 0, batchSize)
|
||||
if err := query.Limit(int64(batchSize)).All(&rows); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -424,45 +422,18 @@ func (dao *Dao) cascadeRecordDelete(mainRecord *models.Record, refs map[*models.
|
||||
break
|
||||
}
|
||||
|
||||
perWorker := 50
|
||||
workers := int(math.Ceil(float64(total) / float64(perWorker)))
|
||||
refRecords := models.NewRecordsFromNullStringMaps(refCollection, rows)
|
||||
|
||||
batchErr := func() error {
|
||||
ch := make(chan error)
|
||||
defer close(ch)
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
var chunks []dbx.NullStringMap
|
||||
if len(rows) <= perWorker {
|
||||
chunks = rows
|
||||
rows = nil
|
||||
} else {
|
||||
chunks = rows[:perWorker]
|
||||
rows = rows[perWorker:]
|
||||
}
|
||||
|
||||
go func() {
|
||||
refRecords := models.NewRecordsFromNullStringMaps(refCollection, chunks)
|
||||
ch <- dao.deleteRefRecords(mainRecord, refRecords, field)
|
||||
}()
|
||||
}
|
||||
|
||||
for i := 0; i < workers; i++ {
|
||||
if err := <-ch; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if batchErr != nil {
|
||||
return batchErr
|
||||
err := dao.deleteRefRecords(mainRecord, refRecords, field)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if total < batchSize {
|
||||
break // no more items
|
||||
}
|
||||
|
||||
rows = rows[:0] // keep allocated memory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -663,11 +663,11 @@ func TestDeleteRecord(t *testing.T) {
|
||||
}
|
||||
// ensure that the json rel fields were prefixed
|
||||
joinedQueries := strings.Join(calledQueries, " ")
|
||||
expectedRelManyJoin := "`demo1` LEFT JOIN json_each(CASE WHEN json_valid([[demo1.rel_many]]) THEN [[demo1.rel_many]] ELSE json_array([[demo1.rel_many]]) END)"
|
||||
expectedRelManyJoin := "`demo1` INNER JOIN json_each(CASE WHEN json_valid([[demo1.rel_many]]) THEN [[demo1.rel_many]] ELSE json_array([[demo1.rel_many]]) END)"
|
||||
if !strings.Contains(joinedQueries, expectedRelManyJoin) {
|
||||
t.Fatalf("(rec3) Expected the cascade delete to call the query \n%v, got \n%v", expectedRelManyJoin, calledQueries)
|
||||
}
|
||||
expectedRelOneJoin := "`demo1` LEFT JOIN json_each(CASE WHEN json_valid([[demo1.rel_one]]) THEN [[demo1.rel_one]] ELSE json_array([[demo1.rel_one]]) END)"
|
||||
expectedRelOneJoin := "`demo1` INNER JOIN json_each(CASE WHEN json_valid([[demo1.rel_one]]) THEN [[demo1.rel_one]] ELSE json_array([[demo1.rel_one]]) END)"
|
||||
if !strings.Contains(joinedQueries, expectedRelOneJoin) {
|
||||
t.Fatalf("(rec3) Expected the cascade delete to call the query \n%v, got \n%v", expectedRelOneJoin, calledQueries)
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ func (form *CollectionUpsert) ensureExistingRelationCollectionId(value any) erro
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := form.dao.FindCollectionByNameOrId(options.CollectionId); err != nil {
|
||||
if err := form.dao.FindById(&models.Collection{}, options.CollectionId); err != nil {
|
||||
return validation.Errors{fmt.Sprint(i): validation.NewError(
|
||||
"validation_field_invalid_relation",
|
||||
"The relation collection doesn't exist.",
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
"collections": []
|
||||
}`,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
expectEvents: nil,
|
||||
},
|
||||
{
|
||||
@@ -82,7 +82,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
]
|
||||
}`,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
expectEvents: map[string]int{
|
||||
"OnModelBeforeCreate": 2,
|
||||
},
|
||||
@@ -101,7 +101,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
]
|
||||
}`,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
expectEvents: map[string]int{
|
||||
"OnModelBeforeCreate": 2,
|
||||
},
|
||||
@@ -137,7 +137,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
]
|
||||
}`,
|
||||
expectError: false,
|
||||
expectCollectionsCount: 10,
|
||||
expectCollectionsCount: 11,
|
||||
expectEvents: map[string]int{
|
||||
"OnModelBeforeCreate": 3,
|
||||
"OnModelAfterCreate": 3,
|
||||
@@ -160,7 +160,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
]
|
||||
}`,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
expectEvents: map[string]int{
|
||||
"OnModelBeforeCreate": 1,
|
||||
},
|
||||
@@ -202,7 +202,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
]
|
||||
}`,
|
||||
expectError: true,
|
||||
expectCollectionsCount: 7,
|
||||
expectCollectionsCount: 8,
|
||||
expectEvents: map[string]int{
|
||||
"OnModelBeforeDelete": 5,
|
||||
},
|
||||
@@ -253,7 +253,7 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
]
|
||||
}`,
|
||||
expectError: false,
|
||||
expectCollectionsCount: 9,
|
||||
expectCollectionsCount: 10,
|
||||
expectEvents: map[string]int{
|
||||
"OnModelBeforeUpdate": 1,
|
||||
"OnModelAfterUpdate": 1,
|
||||
@@ -341,8 +341,8 @@ func TestCollectionsImportSubmit(t *testing.T) {
|
||||
"OnModelAfterUpdate": 2,
|
||||
"OnModelBeforeCreate": 1,
|
||||
"OnModelAfterCreate": 1,
|
||||
"OnModelBeforeDelete": 5,
|
||||
"OnModelAfterDelete": 5,
|
||||
"OnModelBeforeDelete": 6,
|
||||
"OnModelAfterDelete": 6,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
+40
-36
@@ -7,7 +7,6 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
@@ -219,11 +218,6 @@ func (form *RecordUpsert) extractMultipartFormData(
|
||||
// and lods it into the form.
|
||||
//
|
||||
// File upload is supported only via multipart/form-data.
|
||||
//
|
||||
// To DELETE previously uploaded file(s) you can suffix the field name
|
||||
// with the file index or filename (eg. `myfile.0`) and set it to null or empty string.
|
||||
// For single file upload fields, you can skip the index and directly
|
||||
// reset the field using its field name (eg. `myfile = null`).
|
||||
func (form *RecordUpsert) LoadRequest(r *http.Request, keyPrefix string) error {
|
||||
requestData, uploadedFiles, err := form.extractRequestData(r, keyPrefix)
|
||||
if err != nil {
|
||||
@@ -345,29 +339,24 @@ func (form *RecordUpsert) RemoveFiles(key string, toDelete ...string) error {
|
||||
}
|
||||
|
||||
// LoadData loads and normalizes the provided regular record data fields into the form.
|
||||
//
|
||||
// To DELETE previously uploaded file(s) you can suffix the field name
|
||||
// with the file index or filename (eg. `myfile.0`) and set it to null or empty string.
|
||||
// For single file upload fields, you can skip the index and directly
|
||||
// reset the field using its field name (eg. `myfile = null`).
|
||||
func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
||||
// load base system fields
|
||||
if v, ok := requestData["id"]; ok {
|
||||
if v, ok := requestData[schema.FieldNameId]; ok {
|
||||
form.Id = cast.ToString(v)
|
||||
}
|
||||
|
||||
// load auth system fields
|
||||
if form.record.Collection().IsAuth() {
|
||||
if v, ok := requestData["username"]; ok {
|
||||
if v, ok := requestData[schema.FieldNameUsername]; ok {
|
||||
form.Username = cast.ToString(v)
|
||||
}
|
||||
if v, ok := requestData["email"]; ok {
|
||||
if v, ok := requestData[schema.FieldNameEmail]; ok {
|
||||
form.Email = cast.ToString(v)
|
||||
}
|
||||
if v, ok := requestData["emailVisibility"]; ok {
|
||||
if v, ok := requestData[schema.FieldNameEmailVisibility]; ok {
|
||||
form.EmailVisibility = cast.ToBool(v)
|
||||
}
|
||||
if v, ok := requestData["verified"]; ok {
|
||||
if v, ok := requestData[schema.FieldNameVerified]; ok {
|
||||
form.Verified = cast.ToBool(v)
|
||||
}
|
||||
if v, ok := requestData["password"]; ok {
|
||||
@@ -381,8 +370,16 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
||||
}
|
||||
}
|
||||
|
||||
// extend the record schema data with the request data
|
||||
extendedData := form.record.SchemaData()
|
||||
// replace modifiers (if any)
|
||||
requestData = form.record.ReplaceModifers(requestData)
|
||||
|
||||
// create a shallow copy of form.data
|
||||
var extendedData = make(map[string]any, len(form.data))
|
||||
for k, v := range form.data {
|
||||
extendedData[k] = v
|
||||
}
|
||||
|
||||
// extend form.data with the request data
|
||||
rawData, err := json.Marshal(requestData)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -393,8 +390,7 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
||||
|
||||
for _, field := range form.record.Collection().Schema.Fields() {
|
||||
key := field.Name
|
||||
value := extendedData[key]
|
||||
value = field.PrepareValue(value)
|
||||
value := field.PrepareValue(extendedData[key])
|
||||
|
||||
if field.Type != schema.FieldTypeFile {
|
||||
form.data[key] = value
|
||||
@@ -405,30 +401,32 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
||||
// Delete previously uploaded file(s)
|
||||
// -----------------------------------------------------------
|
||||
|
||||
oldNames := list.ToUniqueStringSlice(form.data[key])
|
||||
oldNames := form.record.GetStringSlice(key)
|
||||
submittedNames := list.ToUniqueStringSlice(value)
|
||||
|
||||
// ensure that all submitted names are existing to prevent accidental files deletions
|
||||
if len(submittedNames) > len(oldNames) || len(list.SubtractSlice(submittedNames, oldNames)) != 0 {
|
||||
return validation.Errors{
|
||||
key: validation.NewError(
|
||||
"validation_unknown_filenames",
|
||||
"The field contains unknown filenames.",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// if empty value was set, mark all previously uploaded files for deletion
|
||||
if len(list.ToUniqueStringSlice(value)) == 0 && len(oldNames) > 0 {
|
||||
// otherwise check for "deleted" (aka. unsubmitted) file names
|
||||
if len(submittedNames) == 0 && len(oldNames) > 0 {
|
||||
form.RemoveFiles(key)
|
||||
} else if len(oldNames) > 0 {
|
||||
|
||||
toDelete := []string{}
|
||||
|
||||
// search for individual file name to delete (eg. "file.test.png = null")
|
||||
for _, name := range oldNames {
|
||||
if v, ok := extendedData[key+"."+name]; ok && cast.ToString(v) == "" {
|
||||
// submitted as a modifier or a new array
|
||||
if !list.ExistInSlice(name, submittedNames) {
|
||||
toDelete = append(toDelete, name)
|
||||
}
|
||||
}
|
||||
|
||||
// search for individual file index to delete (eg. "file.0 = null")
|
||||
keyExp, _ := regexp.Compile(`^` + regexp.QuoteMeta(key) + `\.\d+$`)
|
||||
for indexedKey := range extendedData {
|
||||
if keyExp.MatchString(indexedKey) && cast.ToString(extendedData[indexedKey]) == "" {
|
||||
index, indexErr := strconv.Atoi(indexedKey[len(key)+1:])
|
||||
if indexErr != nil || index >= len(oldNames) {
|
||||
continue
|
||||
}
|
||||
toDelete = append(toDelete, oldNames[index])
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,6 +434,12 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
||||
form.RemoveFiles(key, toDelete...)
|
||||
}
|
||||
}
|
||||
|
||||
// allow file key reasignments for file names sorting
|
||||
// (only if all submitted values already exists)
|
||||
if len(submittedNames) > 0 && len(list.SubtractSlice(submittedNames, oldNames)) == 0 {
|
||||
form.data[key] = submittedNames
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+16
-14
@@ -89,9 +89,10 @@ func TestRecordUpsertLoadRequestJson(t *testing.T) {
|
||||
"unknown": "test456",
|
||||
// file fields unset/delete
|
||||
"file_one": nil,
|
||||
"file_many.0": "", // delete by index
|
||||
"file_many.1": "test.png", // should be ignored
|
||||
"file_many.300_WlbFWSGmW9.png": nil, // delete by filename
|
||||
"file_many.0": "", // delete by index
|
||||
"file_many-": []string{"test_MaWC6mWyrP.txt", "test_tC1Yc87DfC.txt"}, // multiple delete with modifier
|
||||
"file_many.300_WlbFWSGmW9.png": nil, // delete by filename
|
||||
"file_many.2": "test.png", // should be ignored
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -149,11 +150,12 @@ func TestRecordUpsertLoadRequestMultipart(t *testing.T) {
|
||||
"a.b.text": "test123",
|
||||
"a.b.unknown": "test456",
|
||||
// file fields unset/delete
|
||||
"a.b.file_one": "",
|
||||
"a.b.file_many.0": "",
|
||||
"a.b.file_many.300_WlbFWSGmW9.png": "test.png", // delete by name
|
||||
"a.b.file_many.1": "test.png", // should be ignored
|
||||
}, "file_many")
|
||||
"a.b.file_one-": "test_d61b33QdDU.txt", // delete with modifier
|
||||
"a.b.file_many.0": "", // delete by index
|
||||
"a.b.file_many-": "test_tC1Yc87DfC.txt", // delete with modifier
|
||||
"a.b.file_many.300_WlbFWSGmW9.png": "", // delete by filename
|
||||
"a.b.file_many.2": "test.png", // should be ignored
|
||||
}, "a.b.file_many")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -191,7 +193,7 @@ func TestRecordUpsertLoadRequestMultipart(t *testing.T) {
|
||||
t.Fatal("Expect file_many field to be set")
|
||||
}
|
||||
manyfilesRemains := len(list.ToUniqueStringSlice(fileMany))
|
||||
expectedRemains := 2 // -2 from 3 removed + 1 new upload
|
||||
expectedRemains := 3 // 5 old; 3 deleted and 1 new uploaded
|
||||
if manyfilesRemains != expectedRemains {
|
||||
t.Fatalf("Expect file_many to be %d, got %d (%v)", expectedRemains, manyfilesRemains, fileMany)
|
||||
}
|
||||
@@ -465,7 +467,7 @@ func TestRecordUpsertSubmitFailure(t *testing.T) {
|
||||
if v := recordAfter.Get("email"); v == "invalid" {
|
||||
t.Fatalf("Expected record.email not to change, got %v", v)
|
||||
}
|
||||
if v := recordAfter.GetStringSlice("file_many"); len(v) != 3 {
|
||||
if v := recordAfter.GetStringSlice("file_many"); len(v) != 5 {
|
||||
t.Fatalf("Expected record.file_many not to change, got %v", v)
|
||||
}
|
||||
|
||||
@@ -537,8 +539,8 @@ func TestRecordUpsertSubmitSuccess(t *testing.T) {
|
||||
}
|
||||
|
||||
fileMany := (recordAfter.GetStringSlice("file_many"))
|
||||
if len(fileMany) != 4 { // 1 replace + 1 new
|
||||
t.Fatalf("Expected 4 record.file_many, got %d (%v)", len(fileMany), fileMany)
|
||||
if len(fileMany) != 6 { // 1 replace + 1 new
|
||||
t.Fatalf("Expected 6 record.file_many, got %d (%v)", len(fileMany), fileMany)
|
||||
}
|
||||
for _, f := range fileMany {
|
||||
if !hasRecordFile(app, recordAfter, f) {
|
||||
@@ -1009,7 +1011,7 @@ func TestRecordUpsertAddAndRemoveFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
fileMany := recordAfter.GetStringSlice("file_many")
|
||||
if len(fileMany) != 3 {
|
||||
t.Fatalf("Expected file_many to be 3, got %v", fileMany)
|
||||
if len(fileMany) != 5 {
|
||||
t.Fatalf("Expected file_many to be 5, got %v", fileMany)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.6
|
||||
github.com/aws/aws-sdk-go v1.44.161
|
||||
github.com/aws/aws-sdk-go v1.44.178
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/domodwyer/mailyak/v3 v3.3.4
|
||||
github.com/dop251/goja v0.0.0-20221118162653-d4bf6fde1b86
|
||||
github.com/dop251/goja_nodejs v0.0.0-20221009164102-3aa5028e57f6
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.1
|
||||
github.com/ganigeorgiev/fexpr v0.1.1
|
||||
github.com/ganigeorgiev/fexpr v0.3.0
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3
|
||||
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198
|
||||
@@ -19,22 +19,22 @@ require (
|
||||
github.com/pocketbase/dbx v1.8.0
|
||||
github.com/spf13/cast v1.5.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
gocloud.dev v0.27.0
|
||||
golang.org/x/crypto v0.4.0
|
||||
golang.org/x/net v0.4.0
|
||||
golang.org/x/oauth2 v0.3.0
|
||||
gocloud.dev v0.28.0
|
||||
golang.org/x/crypto v0.5.0
|
||||
golang.org/x/net v0.5.0
|
||||
golang.org/x/oauth2 v0.4.0
|
||||
golang.org/x/sync v0.1.0
|
||||
modernc.org/sqlite v1.20.0
|
||||
modernc.org/sqlite v1.20.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.44 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.47 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
|
||||
@@ -43,12 +43,13 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.29.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.12.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.18.0 // indirect
|
||||
github.com/aws/smithy-go v1.13.5 // indirect
|
||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
@@ -59,30 +60,30 @@ require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/image v0.2.0 // indirect
|
||||
golang.org/x/image v0.3.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/term v0.4.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.4.0 // indirect
|
||||
golang.org/x/tools v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.105.0 // indirect
|
||||
google.golang.org/api v0.106.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
google.golang.org/grpc v1.52.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
modernc.org/cc/v3 v3.40.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.13 // indirect
|
||||
modernc.org/libc v1.21.5 // indirect
|
||||
modernc.org/libc v1.22.2 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
|
||||
@@ -133,6 +133,7 @@ func init() {
|
||||
"image/png",
|
||||
"image/svg+xml",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
)
|
||||
|
||||
// This migration replaces for backward compatibility the default operators
|
||||
// (=, !=, >, etc.) with their any/opt equivalent (?=, ?=, ?>, etc.)
|
||||
// in any muli-rel expression collection rule.
|
||||
func init() {
|
||||
AppMigrations.Register(func(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
exprRegex := regexp.MustCompile(`([\@\'\"\w\.]+)\s*(=|!=|~|!~|>|>=|<|<=)\s*([\@\'\"\w\.]+)`)
|
||||
|
||||
collections := []*models.Collection{}
|
||||
if err := dao.CollectionQuery().All(&collections); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
findCollection := func(nameOrId string) *models.Collection {
|
||||
for _, c := range collections {
|
||||
if c.Id == nameOrId || c.Name == nameOrId {
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var isMultiRelLiteral func(mainCollection *models.Collection, literal string) bool
|
||||
isMultiRelLiteral = func(mainCollection *models.Collection, literal string) bool {
|
||||
if strings.HasPrefix(literal, "@collection.") {
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(literal, `"`) ||
|
||||
strings.HasPrefix(literal, `'`) ||
|
||||
strings.HasPrefix(literal, "@request.method") ||
|
||||
strings.HasPrefix(literal, "@request.data") ||
|
||||
strings.HasPrefix(literal, "@request.query") {
|
||||
return false
|
||||
}
|
||||
|
||||
parts := strings.Split(literal, ".")
|
||||
if len(parts) <= 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.HasPrefix(literal, "@request.auth") && len(parts) >= 4 {
|
||||
// check each auth collection
|
||||
for _, c := range collections {
|
||||
if c.IsAuth() && isMultiRelLiteral(c, strings.Join(parts[2:], ".")) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
activeCollection := mainCollection
|
||||
|
||||
for i, p := range parts {
|
||||
f := activeCollection.Schema.GetFieldByName(p)
|
||||
if f == nil || f.Type != schema.FieldTypeRelation {
|
||||
return false // not a relation field
|
||||
}
|
||||
|
||||
// is multi-relation and not the last prop
|
||||
opt, ok := f.Options.(*schema.RelationOptions)
|
||||
if ok && (opt.MaxSelect == nil || *opt.MaxSelect != 1) && i != len(parts)-1 {
|
||||
return true
|
||||
}
|
||||
|
||||
activeCollection = findCollection(opt.CollectionId)
|
||||
if activeCollection == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// replace all multi-match operators to their any/opt equivalent, eg. "=" => "?="
|
||||
migrateRule := func(collection *models.Collection, rule *string) (*string, error) {
|
||||
if rule == nil || *rule == "" {
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
newRule := *rule
|
||||
parts := exprRegex.FindAllStringSubmatch(newRule, -1)
|
||||
|
||||
for _, p := range parts {
|
||||
if isMultiRelLiteral(collection, p[1]) || isMultiRelLiteral(collection, p[3]) {
|
||||
newRule = strings.ReplaceAll(newRule, p[0], p[1]+" ?"+p[2]+" "+p[3])
|
||||
}
|
||||
}
|
||||
|
||||
return &newRule, nil
|
||||
}
|
||||
|
||||
var ruleErr error
|
||||
for _, c := range collections {
|
||||
c.ListRule, ruleErr = migrateRule(c, c.ListRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.ViewRule, ruleErr = migrateRule(c, c.ViewRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.CreateRule, ruleErr = migrateRule(c, c.CreateRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.UpdateRule, ruleErr = migrateRule(c, c.UpdateRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.DeleteRule, ruleErr = migrateRule(c, c.DeleteRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
if c.IsAuth() {
|
||||
opt := c.AuthOptions()
|
||||
opt.ManageRule, ruleErr = migrateRule(c, opt.ManageRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
c.SetOptions(opt)
|
||||
}
|
||||
|
||||
if err := dao.Save(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}, func(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
collections := []*models.Collection{}
|
||||
if err := dao.CollectionQuery().All(&collections); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
anyOpRegex := regexp.MustCompile(`\?(=|!=|~|!~|>|>=|<|<=)`)
|
||||
|
||||
// replace any/opt operators to their old versions, eg. "?=" => "="
|
||||
revertRule := func(rule *string) (*string, error) {
|
||||
if rule == nil || *rule == "" {
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
newRule := *rule
|
||||
newRule = anyOpRegex.ReplaceAllString(newRule, "${1}")
|
||||
|
||||
return &newRule, nil
|
||||
}
|
||||
|
||||
var ruleErr error
|
||||
for _, c := range collections {
|
||||
c.ListRule, ruleErr = revertRule(c.ListRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.ViewRule, ruleErr = revertRule(c.ViewRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.CreateRule, ruleErr = revertRule(c.CreateRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.UpdateRule, ruleErr = revertRule(c.UpdateRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
c.DeleteRule, ruleErr = revertRule(c.DeleteRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
|
||||
if c.IsAuth() {
|
||||
opt := c.AuthOptions()
|
||||
opt.ManageRule, ruleErr = revertRule(opt.ManageRule)
|
||||
if ruleErr != nil {
|
||||
return ruleErr
|
||||
}
|
||||
c.SetOptions(opt)
|
||||
}
|
||||
|
||||
if err := dao.Save(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
+107
-6
@@ -4,6 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
@@ -57,7 +59,7 @@ func nullStringMapValue(data dbx.NullStringMap, key string) any {
|
||||
// NewRecordFromNullStringMap initializes a single new Record model
|
||||
// with data loaded from the provided NullStringMap.
|
||||
func NewRecordFromNullStringMap(collection *Collection, data dbx.NullStringMap) *Record {
|
||||
resultMap := map[string]any{}
|
||||
resultMap := make(map[string]any, len(data))
|
||||
|
||||
// load schema fields
|
||||
for _, field := range collection.Schema.Fields() {
|
||||
@@ -139,6 +141,10 @@ func (m *Record) SetExpand(expand map[string]any) {
|
||||
// then both old and new records will be merged into a new slice (aka. a :merge: [b,c] => [a,b,c]).
|
||||
// Otherwise the "old" expanded record will be replace with the "new" one (aka. a :merge: aNew => aNew).
|
||||
func (m *Record) MergeExpand(expand map[string]any) {
|
||||
if m.expand == nil && len(expand) > 0 {
|
||||
m.expand = make(map[string]any, len(expand))
|
||||
}
|
||||
|
||||
for key, new := range expand {
|
||||
old, ok := m.expand[key]
|
||||
if !ok {
|
||||
@@ -174,7 +180,7 @@ func (m *Record) MergeExpand(expand map[string]any) {
|
||||
continue
|
||||
}
|
||||
|
||||
oldIndexed := map[string]*Record{}
|
||||
oldIndexed := make(map[string]*Record, len(oldSlice))
|
||||
for _, oldRecord := range oldSlice {
|
||||
oldIndexed[oldRecord.Id] = oldRecord
|
||||
}
|
||||
@@ -190,7 +196,7 @@ func (m *Record) MergeExpand(expand map[string]any) {
|
||||
}
|
||||
}
|
||||
|
||||
if wasOldSlice || wasNewSlice {
|
||||
if wasOldSlice || wasNewSlice || len(oldSlice) == 0 {
|
||||
m.expand[key] = oldSlice
|
||||
} else {
|
||||
m.expand[key] = oldSlice[0]
|
||||
@@ -200,7 +206,7 @@ func (m *Record) MergeExpand(expand map[string]any) {
|
||||
|
||||
// SchemaData returns a shallow copy ONLY of the defined record schema fields data.
|
||||
func (m *Record) SchemaData() map[string]any {
|
||||
result := map[string]any{}
|
||||
result := make(map[string]any, len(m.collection.Schema.Fields()))
|
||||
|
||||
for _, field := range m.collection.Schema.Fields() {
|
||||
if v, ok := m.data[field.Name]; ok {
|
||||
@@ -366,7 +372,7 @@ func (m *Record) Load(data map[string]any) {
|
||||
|
||||
// ColumnValueMap implements [ColumnValueMapper] interface.
|
||||
func (m *Record) ColumnValueMap() map[string]any {
|
||||
result := map[string]any{}
|
||||
result := make(map[string]any, len(m.collection.Schema.Fields())+3)
|
||||
|
||||
// export schema field values
|
||||
for _, field := range m.collection.Schema.Fields() {
|
||||
@@ -392,7 +398,7 @@ func (m *Record) ColumnValueMap() map[string]any {
|
||||
//
|
||||
// Fields marked as hidden will be exported only if `m.IgnoreEmailVisibility(true)` is set.
|
||||
func (m *Record) PublicExport() map[string]any {
|
||||
result := map[string]any{}
|
||||
result := make(map[string]any, len(m.collection.Schema.Fields())+5)
|
||||
|
||||
// export unknown data fields if allowed
|
||||
if m.exportUnknown {
|
||||
@@ -453,6 +459,101 @@ func (m *Record) UnmarshalJSON(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReplaceModifers returns a new map with applied modifier
|
||||
// values based on the current record and the specified data.
|
||||
//
|
||||
// The resolved modifier keys will be removed.
|
||||
//
|
||||
// Multiple modifiers will be applied one after another,
|
||||
// while reusing the previous base key value result (eg. 1; -5; +2 => -2).
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// newData := record.ReplaceModifers(data)
|
||||
// // record: {"field": 10}
|
||||
// // data: {"field+": 5}
|
||||
// // newData: {"field": 15}
|
||||
func (m *Record) ReplaceModifers(data map[string]any) map[string]any {
|
||||
var clone = shallowCopy(data)
|
||||
if len(clone) == 0 {
|
||||
return clone
|
||||
}
|
||||
|
||||
var recordDataCache map[string]any
|
||||
|
||||
// export recordData lazily
|
||||
recordData := func() map[string]any {
|
||||
if recordDataCache == nil {
|
||||
recordDataCache = m.SchemaData()
|
||||
}
|
||||
return recordDataCache
|
||||
}
|
||||
|
||||
modifiers := schema.FieldValueModifiers()
|
||||
|
||||
for _, field := range m.Collection().Schema.Fields() {
|
||||
key := field.Name
|
||||
|
||||
for _, m := range modifiers {
|
||||
if mv, mOk := clone[key+m]; mOk {
|
||||
if _, ok := clone[key]; !ok {
|
||||
// get base value from the merged data
|
||||
clone[key] = recordData()[key]
|
||||
}
|
||||
|
||||
clone[key] = field.PrepareValueWithModifier(clone[key], m, mv)
|
||||
delete(clone, key+m)
|
||||
}
|
||||
}
|
||||
|
||||
if field.Type != schema.FieldTypeFile {
|
||||
continue
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// legacy file field modifiers (kept for backward compatability)
|
||||
// -----------------------------------------------------------
|
||||
|
||||
var oldNames []string
|
||||
var toDelete []string
|
||||
if _, ok := clone[key]; ok {
|
||||
oldNames = list.ToUniqueStringSlice(clone[key])
|
||||
} else {
|
||||
// get oldNames from the model
|
||||
oldNames = list.ToUniqueStringSlice(recordData()[key])
|
||||
}
|
||||
|
||||
// search for individual file name to delete (eg. "file.test.png = null")
|
||||
for _, name := range oldNames {
|
||||
suffixedKey := key + "." + name
|
||||
if v, ok := clone[suffixedKey]; ok && cast.ToString(v) == "" {
|
||||
toDelete = append(toDelete, name)
|
||||
delete(clone, suffixedKey)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// search for individual file index to delete (eg. "file.0 = null")
|
||||
keyExp, _ := regexp.Compile(`^` + regexp.QuoteMeta(key) + `\.\d+$`)
|
||||
for indexedKey := range clone {
|
||||
if keyExp.MatchString(indexedKey) && cast.ToString(clone[indexedKey]) == "" {
|
||||
index, indexErr := strconv.Atoi(indexedKey[len(key)+1:])
|
||||
if indexErr != nil || index < 0 || index >= len(oldNames) {
|
||||
continue
|
||||
}
|
||||
toDelete = append(toDelete, oldNames[index])
|
||||
delete(clone, indexedKey)
|
||||
}
|
||||
}
|
||||
|
||||
if toDelete != nil {
|
||||
clone[key] = field.PrepareValue(list.SubtractSlice(oldNames, toDelete))
|
||||
}
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// getNormalizeDataValueForDB returns the "key" data value formatted for db storage.
|
||||
func (m *Record) getNormalizeDataValueForDB(key string) any {
|
||||
var val any
|
||||
|
||||
+148
-4
@@ -138,7 +138,7 @@ func TestNewRecordFromNullStringMap(t *testing.T) {
|
||||
Valid: true,
|
||||
},
|
||||
"field5": sql.NullString{
|
||||
String: `["test1","test2"]`, // will select only the first elem
|
||||
String: `["test1","test2"]`, // will select only the last elem
|
||||
Valid: true,
|
||||
},
|
||||
"field6": sql.NullString{
|
||||
@@ -157,11 +157,11 @@ func TestNewRecordFromNullStringMap(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
models.CollectionTypeBase,
|
||||
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test1","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z"}`,
|
||||
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test2","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z"}`,
|
||||
},
|
||||
{
|
||||
models.CollectionTypeAuth,
|
||||
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","email":"test_email","emailVisibility":true,"field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test1","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z","username":"test_username","verified":false}`,
|
||||
`{"collectionId":"","collectionName":"test","created":"2022-01-01 10:00:00.123Z","email":"test_email","emailVisibility":true,"field1":"test","field2":"","field3":true,"field4":123.123,"field5":"test2","field6":["test"],"id":"test_id","updated":"2022-01-01 10:00:00.456Z","username":"test_username","verified":false}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ func TestRecordSetAndGetExpand(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordMergeExpandDirect(t *testing.T) {
|
||||
func TestRecordMergeExpand(t *testing.T) {
|
||||
collection := &models.Collection{}
|
||||
m := models.NewRecord(collection)
|
||||
m.Id = "m"
|
||||
@@ -467,6 +467,47 @@ func TestRecordMergeExpandDirect(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordMergeExpandNilCheck(t *testing.T) {
|
||||
collection := &models.Collection{}
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
expand map[string]any
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"nil expand",
|
||||
nil,
|
||||
`{"collectionId":"","collectionName":"","created":"","id":"","updated":""}`,
|
||||
},
|
||||
{
|
||||
"empty expand",
|
||||
map[string]any{},
|
||||
`{"collectionId":"","collectionName":"","created":"","id":"","updated":""}`,
|
||||
},
|
||||
{
|
||||
"non-empty expand",
|
||||
map[string]any{"test": models.NewRecord(collection)},
|
||||
`{"collectionId":"","collectionName":"","created":"","expand":{"test":{"collectionId":"","collectionName":"","created":"","id":"","updated":""}},"id":"","updated":""}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
m := models.NewRecord(collection)
|
||||
m.MergeExpand(s.expand)
|
||||
|
||||
raw, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rawStr := string(raw)
|
||||
|
||||
if rawStr != s.expected {
|
||||
t.Fatalf("[%s] Expected \n%v, \ngot \n%v", s.name, s.expected, rawStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordSchemaData(t *testing.T) {
|
||||
collection := &models.Collection{
|
||||
Type: models.CollectionTypeAuth,
|
||||
@@ -1384,6 +1425,109 @@ func TestRecordUnmarshalJSON(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordReplaceModifers(t *testing.T) {
|
||||
collection := &models.Collection{
|
||||
Schema: schema.NewSchema(
|
||||
&schema.SchemaField{
|
||||
Name: "text",
|
||||
Type: schema.FieldTypeText,
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "number",
|
||||
Type: schema.FieldTypeNumber,
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "rel_one",
|
||||
Type: schema.FieldTypeRelation,
|
||||
Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "rel_many",
|
||||
Type: schema.FieldTypeRelation,
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "select_one",
|
||||
Type: schema.FieldTypeSelect,
|
||||
Options: &schema.SelectOptions{MaxSelect: 1},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "select_many",
|
||||
Type: schema.FieldTypeSelect,
|
||||
Options: &schema.SelectOptions{MaxSelect: 10},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "file_one",
|
||||
Type: schema.FieldTypeFile,
|
||||
Options: &schema.FileOptions{MaxSelect: 1},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "file_one_index",
|
||||
Type: schema.FieldTypeFile,
|
||||
Options: &schema.FileOptions{MaxSelect: 1},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "file_one_name",
|
||||
Type: schema.FieldTypeFile,
|
||||
Options: &schema.FileOptions{MaxSelect: 1},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "file_many",
|
||||
Type: schema.FieldTypeFile,
|
||||
Options: &schema.FileOptions{MaxSelect: 10},
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
record := models.NewRecord(collection)
|
||||
|
||||
record.Load(map[string]any{
|
||||
"text": "test",
|
||||
"number": 10,
|
||||
"rel_one": "a",
|
||||
"rel_many": []string{"a", "b"},
|
||||
"select_one": "a",
|
||||
"select_many": []string{"a", "b", "c"},
|
||||
"file_one": "a",
|
||||
"file_one_index": "b",
|
||||
"file_one_name": "c",
|
||||
"file_many": []string{"a", "b", "c", "d", "e", "f"},
|
||||
})
|
||||
|
||||
result := record.ReplaceModifers(map[string]any{
|
||||
"text-": "m-",
|
||||
"text+": "m+",
|
||||
"number-": 3,
|
||||
"number+": 5,
|
||||
"rel_one-": "a",
|
||||
"rel_one+": "b",
|
||||
"rel_many-": []string{"a"},
|
||||
"rel_many+": []string{"c", "d", "e"},
|
||||
"select_one-": "a",
|
||||
"select_one+": "c",
|
||||
"select_many-": []string{"b", "c"},
|
||||
"select_many+": []string{"d", "e"},
|
||||
"file_one+": "skip", // should be ignored
|
||||
"file_one-": "a",
|
||||
"file_one_index.0": "",
|
||||
"file_one_name.c": "",
|
||||
"file_many+": []string{"e", "f"}, // should be ignored
|
||||
"file_many-": []string{"c", "d"},
|
||||
"file_many.f": nil,
|
||||
"file_many.0": nil,
|
||||
})
|
||||
|
||||
raw, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `{"file_many":["b","e"],"file_one":"","file_one_index":"","file_one_name":"","number":12,"rel_many":["b","c","d","e"],"rel_one":"b","select_many":["a","d","e"],"select_one":"c","text":"test"}`
|
||||
|
||||
if v := string(raw); v != expected {
|
||||
t.Fatalf("Expected \n%s, \ngot \n%s", expected, v)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Auth helpers:
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
)
|
||||
|
||||
// RequestData defines a HTTP request data struct, usually used
|
||||
// as part of the `@request.*` filter resolver.
|
||||
type RequestData struct {
|
||||
@@ -9,3 +15,18 @@ type RequestData struct {
|
||||
AuthRecord *Record `json:"authRecord"`
|
||||
Admin *Admin `json:"admin"`
|
||||
}
|
||||
|
||||
// HasModifierDataKeys loosely checks if the current struct has any modifier Data keys.
|
||||
func (r *RequestData) HasModifierDataKeys() bool {
|
||||
allModifiers := schema.FieldValueModifiers()
|
||||
|
||||
for key := range r.Data {
|
||||
for _, m := range allModifiers {
|
||||
if strings.HasSuffix(key, m) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package models_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
)
|
||||
|
||||
func TestRequestDataHasModifierDataKeys(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
requestData *models.RequestData
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
"empty",
|
||||
&models.RequestData{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Data with regular fields",
|
||||
&models.RequestData{
|
||||
Query: map[string]any{"data+": "demo"}, // should be ignored
|
||||
Data: map[string]any{"a": 123, "b": "test", "c.d": false},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Data with +modifier fields",
|
||||
&models.RequestData{
|
||||
Data: map[string]any{"a+": 123, "b": "test", "c.d": false},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Data with -modifier fields",
|
||||
&models.RequestData{
|
||||
Data: map[string]any{"a": 123, "b-": "test", "c.d": false},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Data with mixed modifier fields",
|
||||
&models.RequestData{
|
||||
Data: map[string]any{"a": 123, "b-": "test", "c.d+": false},
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
result := s.requestData.HasModifierDataKeys()
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("[%s] Expected %v, got %v", s.name, s.expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,22 +15,36 @@ import (
|
||||
|
||||
var schemaFieldNameRegex = regexp.MustCompile(`^\w+$`)
|
||||
|
||||
// field value modifiers
|
||||
const (
|
||||
FieldValueModifierAdd string = "+"
|
||||
FieldValueModifierSubtract string = "-"
|
||||
)
|
||||
|
||||
// FieldValueModifiers returns a list with all available field modifier tokens.
|
||||
func FieldValueModifiers() []string {
|
||||
return []string{
|
||||
FieldValueModifierAdd,
|
||||
FieldValueModifierSubtract,
|
||||
}
|
||||
}
|
||||
|
||||
// commonly used field names
|
||||
const (
|
||||
FieldNameId = "id"
|
||||
FieldNameCreated = "created"
|
||||
FieldNameUpdated = "updated"
|
||||
FieldNameCollectionId = "collectionId"
|
||||
FieldNameCollectionName = "collectionName"
|
||||
FieldNameExpand = "expand"
|
||||
FieldNameUsername = "username"
|
||||
FieldNameEmail = "email"
|
||||
FieldNameEmailVisibility = "emailVisibility"
|
||||
FieldNameVerified = "verified"
|
||||
FieldNameTokenKey = "tokenKey"
|
||||
FieldNamePasswordHash = "passwordHash"
|
||||
FieldNameLastResetSentAt = "lastResetSentAt"
|
||||
FieldNameLastVerificationSentAt = "lastVerificationSentAt"
|
||||
FieldNameId string = "id"
|
||||
FieldNameCreated string = "created"
|
||||
FieldNameUpdated string = "updated"
|
||||
FieldNameCollectionId string = "collectionId"
|
||||
FieldNameCollectionName string = "collectionName"
|
||||
FieldNameExpand string = "expand"
|
||||
FieldNameUsername string = "username"
|
||||
FieldNameEmail string = "email"
|
||||
FieldNameEmailVisibility string = "emailVisibility"
|
||||
FieldNameVerified string = "verified"
|
||||
FieldNameTokenKey string = "tokenKey"
|
||||
FieldNamePasswordHash string = "passwordHash"
|
||||
FieldNameLastResetSentAt string = "lastResetSentAt"
|
||||
FieldNameLastVerificationSentAt string = "lastVerificationSentAt"
|
||||
)
|
||||
|
||||
// BaseModelFieldNames returns the field names that all models have (id, created, updated).
|
||||
@@ -168,8 +182,8 @@ func (f SchemaField) Validate() error {
|
||||
f.InitOptions()
|
||||
|
||||
excludeNames := BaseModelFieldNames()
|
||||
// exclude filter literals
|
||||
excludeNames = append(excludeNames, "null", "true", "false")
|
||||
// exclude special filter literals
|
||||
excludeNames = append(excludeNames, "null", "true", "false", "isset")
|
||||
// exclude system literals
|
||||
excludeNames = append(excludeNames, SystemFieldNames()...)
|
||||
|
||||
@@ -276,7 +290,7 @@ func (f *SchemaField) PrepareValue(value any) any {
|
||||
options, _ := f.Options.(*SelectOptions)
|
||||
if options.MaxSelect <= 1 {
|
||||
if len(val) > 0 {
|
||||
return val[0]
|
||||
return val[len(val)-1] // the last selected
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -288,7 +302,7 @@ func (f *SchemaField) PrepareValue(value any) any {
|
||||
options, _ := f.Options.(*FileOptions)
|
||||
if options.MaxSelect <= 1 {
|
||||
if len(val) > 0 {
|
||||
return val[0]
|
||||
return val[len(val)-1] // the last selected
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -300,7 +314,7 @@ func (f *SchemaField) PrepareValue(value any) any {
|
||||
options, _ := f.Options.(*RelationOptions)
|
||||
if options.MaxSelect != nil && *options.MaxSelect <= 1 {
|
||||
if len(ids) > 0 {
|
||||
return ids[0]
|
||||
return ids[len(ids)-1] // the last selected
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -311,6 +325,46 @@ func (f *SchemaField) PrepareValue(value any) any {
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareValueWithModifier returns normalized and properly formatted field value
|
||||
// by "merging" baseValue with the modifierValue based on the specified modifier (+ or -).
|
||||
func (f *SchemaField) PrepareValueWithModifier(baseValue any, modifier string, modifierValue any) any {
|
||||
resolvedValue := baseValue
|
||||
|
||||
switch f.Type {
|
||||
case FieldTypeNumber:
|
||||
switch modifier {
|
||||
case FieldValueModifierAdd:
|
||||
resolvedValue = cast.ToFloat64(baseValue) + cast.ToFloat64(modifierValue)
|
||||
case FieldValueModifierSubtract:
|
||||
resolvedValue = cast.ToFloat64(baseValue) - cast.ToFloat64(modifierValue)
|
||||
}
|
||||
case FieldTypeSelect, FieldTypeRelation:
|
||||
switch modifier {
|
||||
case FieldValueModifierAdd:
|
||||
resolvedValue = append(
|
||||
list.ToUniqueStringSlice(baseValue),
|
||||
list.ToUniqueStringSlice(modifierValue)...,
|
||||
)
|
||||
case FieldValueModifierSubtract:
|
||||
resolvedValue = list.SubtractSlice(
|
||||
list.ToUniqueStringSlice(baseValue),
|
||||
list.ToUniqueStringSlice(modifierValue),
|
||||
)
|
||||
}
|
||||
case FieldTypeFile:
|
||||
// note: file for now supports only the subtract modifier
|
||||
switch modifier {
|
||||
case FieldValueModifierSubtract:
|
||||
resolvedValue = list.SubtractSlice(
|
||||
list.ToUniqueStringSlice(baseValue),
|
||||
list.ToUniqueStringSlice(modifierValue),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return f.PrepareValue(resolvedValue)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// FieldOptions interfaces that defines common methods that every field options struct has.
|
||||
|
||||
@@ -603,7 +603,7 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
|
||||
{schema.SchemaField{Type: schema.FieldTypeSelect}, "", `""`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeSelect}, 123, `"123"`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeSelect}, "test", `"test"`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeSelect}, []string{"test1", "test2"}, `"test1"`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeSelect}, []string{"test1", "test2"}, `"test2"`},
|
||||
{
|
||||
// no values validation/filtering
|
||||
schema.SchemaField{
|
||||
@@ -680,7 +680,7 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
|
||||
{schema.SchemaField{Type: schema.FieldTypeFile}, "", `""`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeFile}, 123, `"123"`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeFile}, "test", `"test"`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeFile}, []string{"test1", "test2"}, `"test1"`},
|
||||
{schema.SchemaField{Type: schema.FieldTypeFile}, []string{"test1", "test2"}, `"test2"`},
|
||||
// file (multiple)
|
||||
{
|
||||
schema.SchemaField{
|
||||
@@ -785,7 +785,7 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
[]string{"1ba88b4f-e9da-42f0-9764-9a55c953e724", "2ba88b4f-e9da-42f0-9764-9a55c953e724"},
|
||||
`"1ba88b4f-e9da-42f0-9764-9a55c953e724"`,
|
||||
`"2ba88b4f-e9da-42f0-9764-9a55c953e724"`,
|
||||
},
|
||||
// relation (multiple)
|
||||
{
|
||||
@@ -863,6 +863,696 @@ func TestSchemaFieldPrepareValue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSchemaFieldPrepareValueWithModifier(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
field schema.SchemaField
|
||||
baseValue any
|
||||
modifier string
|
||||
modifierValue any
|
||||
expectJson string
|
||||
}{
|
||||
// text
|
||||
{
|
||||
"text with '+' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeText},
|
||||
"base",
|
||||
"+",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"text with '-' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeText},
|
||||
"base",
|
||||
"-",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"text with unknown modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeText},
|
||||
"base",
|
||||
"?",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"text cast check",
|
||||
schema.SchemaField{Type: schema.FieldTypeText},
|
||||
123,
|
||||
"?",
|
||||
"new",
|
||||
`"123"`,
|
||||
},
|
||||
|
||||
// number
|
||||
{
|
||||
"number with '+' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeNumber},
|
||||
1,
|
||||
"+",
|
||||
4,
|
||||
`5`,
|
||||
},
|
||||
{
|
||||
"number with '-' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeNumber},
|
||||
1,
|
||||
"-",
|
||||
4,
|
||||
`-3`,
|
||||
},
|
||||
{
|
||||
"number with unknown modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeNumber},
|
||||
"1",
|
||||
"?",
|
||||
4,
|
||||
`1`,
|
||||
},
|
||||
{
|
||||
"number cast check",
|
||||
schema.SchemaField{Type: schema.FieldTypeNumber},
|
||||
"test",
|
||||
"+",
|
||||
"4",
|
||||
`4`,
|
||||
},
|
||||
|
||||
// bool
|
||||
{
|
||||
"bool with '+' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeBool},
|
||||
true,
|
||||
"+",
|
||||
false,
|
||||
`true`,
|
||||
},
|
||||
{
|
||||
"bool with '-' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeBool},
|
||||
true,
|
||||
"-",
|
||||
false,
|
||||
`true`,
|
||||
},
|
||||
{
|
||||
"bool with unknown modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeBool},
|
||||
true,
|
||||
"?",
|
||||
false,
|
||||
`true`,
|
||||
},
|
||||
{
|
||||
"bool cast check",
|
||||
schema.SchemaField{Type: schema.FieldTypeBool},
|
||||
"true",
|
||||
"?",
|
||||
false,
|
||||
`true`,
|
||||
},
|
||||
|
||||
// email
|
||||
{
|
||||
"email with '+' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeEmail},
|
||||
"base",
|
||||
"+",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"email with '-' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeEmail},
|
||||
"base",
|
||||
"-",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"email with unknown modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeEmail},
|
||||
"base",
|
||||
"?",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"email cast check",
|
||||
schema.SchemaField{Type: schema.FieldTypeEmail},
|
||||
123,
|
||||
"?",
|
||||
"new",
|
||||
`"123"`,
|
||||
},
|
||||
|
||||
// url
|
||||
{
|
||||
"url with '+' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeUrl},
|
||||
"base",
|
||||
"+",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"url with '-' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeUrl},
|
||||
"base",
|
||||
"-",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"url with unknown modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeUrl},
|
||||
"base",
|
||||
"?",
|
||||
"new",
|
||||
`"base"`,
|
||||
},
|
||||
{
|
||||
"url cast check",
|
||||
schema.SchemaField{Type: schema.FieldTypeUrl},
|
||||
123,
|
||||
"-",
|
||||
"new",
|
||||
`"123"`,
|
||||
},
|
||||
|
||||
// date
|
||||
{
|
||||
"date with '+' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeDate},
|
||||
"2023-01-01 00:00:00.123",
|
||||
"+",
|
||||
"2023-02-01 00:00:00.456",
|
||||
`"2023-01-01 00:00:00.123Z"`,
|
||||
},
|
||||
{
|
||||
"date with '-' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeDate},
|
||||
"2023-01-01 00:00:00.123Z",
|
||||
"-",
|
||||
"2023-02-01 00:00:00.456Z",
|
||||
`"2023-01-01 00:00:00.123Z"`,
|
||||
},
|
||||
{
|
||||
"date with unknown modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeDate},
|
||||
"2023-01-01 00:00:00.123",
|
||||
"?",
|
||||
"2023-01-01 00:00:00.456",
|
||||
`"2023-01-01 00:00:00.123Z"`,
|
||||
},
|
||||
{
|
||||
"date cast check",
|
||||
schema.SchemaField{Type: schema.FieldTypeDate},
|
||||
1672524000, // 2022-12-31 22:00:00.000Z
|
||||
"+",
|
||||
100,
|
||||
`"2022-12-31 22:00:00.000Z"`,
|
||||
},
|
||||
|
||||
// json
|
||||
{
|
||||
"json with '+' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeJson},
|
||||
10,
|
||||
"+",
|
||||
5,
|
||||
`10`,
|
||||
},
|
||||
{
|
||||
"json with '+' modifier (slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeJson},
|
||||
[]string{"a", "b"},
|
||||
"+",
|
||||
"c",
|
||||
`["a","b"]`,
|
||||
},
|
||||
{
|
||||
"json with '-' modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeJson},
|
||||
10,
|
||||
"-",
|
||||
5,
|
||||
`10`,
|
||||
},
|
||||
{
|
||||
"json with '-' modifier (slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeJson},
|
||||
`["a","b"]`,
|
||||
"-",
|
||||
"c",
|
||||
`["a","b"]`,
|
||||
},
|
||||
{
|
||||
"json with unknown modifier",
|
||||
schema.SchemaField{Type: schema.FieldTypeJson},
|
||||
`"base"`,
|
||||
"?",
|
||||
`"new"`,
|
||||
`"base"`,
|
||||
},
|
||||
|
||||
// single select
|
||||
{
|
||||
"single select with '+' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"",
|
||||
"+",
|
||||
"b",
|
||||
`"b"`,
|
||||
},
|
||||
{
|
||||
"single select with '+' modifier (nonempty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"+",
|
||||
"b",
|
||||
`"b"`,
|
||||
},
|
||||
{
|
||||
"single select with '-' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"",
|
||||
"-",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single select with '-' modifier (nonempty base and empty modifier value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
"",
|
||||
`"a"`,
|
||||
},
|
||||
{
|
||||
"single select with '-' modifier (nonempty base and different value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
"b",
|
||||
`"a"`,
|
||||
},
|
||||
{
|
||||
"single select with '-' modifier (nonempty base and matching value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single select with '-' modifier (nonempty base and matching value in a slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
[]string{"b", "a", "c", "123"},
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single select with unknown modifier (nonempty)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 1}},
|
||||
"",
|
||||
"?",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
|
||||
// multi select
|
||||
{
|
||||
"multi select with '+' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
nil,
|
||||
"+",
|
||||
"b",
|
||||
`["b"]`,
|
||||
},
|
||||
{
|
||||
"multi select with '+' modifier (nonempty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
[]string{"a"},
|
||||
"+",
|
||||
[]string{"b", "c"},
|
||||
`["a","b","c"]`,
|
||||
},
|
||||
{
|
||||
"multi select with '+' modifier (nonempty base; already existing value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b"},
|
||||
"+",
|
||||
"b",
|
||||
`["a","b"]`,
|
||||
},
|
||||
{
|
||||
"multi select with '-' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
nil,
|
||||
"-",
|
||||
[]string{"a"},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
"multi select with '-' modifier (nonempty base and empty modifier value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
"a",
|
||||
"-",
|
||||
"",
|
||||
`["a"]`,
|
||||
},
|
||||
{
|
||||
"multi select with '-' modifier (nonempty base and different value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
"a",
|
||||
"-",
|
||||
"b",
|
||||
`["a"]`,
|
||||
},
|
||||
{
|
||||
"multi select with '-' modifier (nonempty base and matching value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
"-",
|
||||
"c",
|
||||
`["a","b","d"]`,
|
||||
},
|
||||
{
|
||||
"multi select with '-' modifier (nonempty base and matching value in a slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
"-",
|
||||
[]string{"b", "a", "123"},
|
||||
`["c","d"]`,
|
||||
},
|
||||
{
|
||||
"multi select with unknown modifier (nonempty)",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Options: &schema.SelectOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b"},
|
||||
"?",
|
||||
"a",
|
||||
`["a","b"]`,
|
||||
},
|
||||
|
||||
// single relation
|
||||
{
|
||||
"single relation with '+' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"",
|
||||
"+",
|
||||
"b",
|
||||
`"b"`,
|
||||
},
|
||||
{
|
||||
"single relation with '+' modifier (nonempty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"a",
|
||||
"+",
|
||||
"b",
|
||||
`"b"`,
|
||||
},
|
||||
{
|
||||
"single relation with '-' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"",
|
||||
"-",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single relation with '-' modifier (nonempty base and empty modifier value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"a",
|
||||
"-",
|
||||
"",
|
||||
`"a"`,
|
||||
},
|
||||
{
|
||||
"single relation with '-' modifier (nonempty base and different value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"a",
|
||||
"-",
|
||||
"b",
|
||||
`"a"`,
|
||||
},
|
||||
{
|
||||
"single relation with '-' modifier (nonempty base and matching value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"a",
|
||||
"-",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single relation with '-' modifier (nonempty base and matching value in a slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"a",
|
||||
"-",
|
||||
[]string{"b", "a", "c", "123"},
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single relation with unknown modifier (nonempty)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"",
|
||||
"?",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
|
||||
// multi relation
|
||||
{
|
||||
"multi relation with '+' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
nil,
|
||||
"+",
|
||||
"b",
|
||||
`["b"]`,
|
||||
},
|
||||
{
|
||||
"multi relation with '+' modifier (nonempty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
[]string{"a"},
|
||||
"+",
|
||||
[]string{"b", "c"},
|
||||
`["a","b","c"]`,
|
||||
},
|
||||
{
|
||||
"multi relation with '+' modifier (nonempty base; already existing value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
[]string{"a", "b"},
|
||||
"+",
|
||||
"b",
|
||||
`["a","b"]`,
|
||||
},
|
||||
{
|
||||
"multi relation with '-' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
nil,
|
||||
"-",
|
||||
[]string{"a"},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
"multi relation with '-' modifier (nonempty base and empty modifier value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
"a",
|
||||
"-",
|
||||
"",
|
||||
`["a"]`,
|
||||
},
|
||||
{
|
||||
"multi relation with '-' modifier (nonempty base and different value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
"a",
|
||||
"-",
|
||||
"b",
|
||||
`["a"]`,
|
||||
},
|
||||
{
|
||||
"multi relation with '-' modifier (nonempty base and matching value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
"-",
|
||||
"c",
|
||||
`["a","b","d"]`,
|
||||
},
|
||||
{
|
||||
"multi relation with '-' modifier (nonempty base and matching value in a slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
"-",
|
||||
[]string{"b", "a", "123"},
|
||||
`["c","d"]`,
|
||||
},
|
||||
{
|
||||
"multi relation with unknown modifier (nonempty)",
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
[]string{"a", "b"},
|
||||
"?",
|
||||
"a",
|
||||
`["a","b"]`,
|
||||
},
|
||||
|
||||
// single file
|
||||
{
|
||||
"single file with '+' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"",
|
||||
"+",
|
||||
"b",
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single file with '+' modifier (nonempty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"+",
|
||||
"b",
|
||||
`"a"`,
|
||||
},
|
||||
{
|
||||
"single file with '-' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"",
|
||||
"-",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single file with '-' modifier (nonempty base and empty modifier value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
"",
|
||||
`"a"`,
|
||||
},
|
||||
{
|
||||
"single file with '-' modifier (nonempty base and different value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
"b",
|
||||
`"a"`,
|
||||
},
|
||||
{
|
||||
"single file with '-' modifier (nonempty base and matching value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single file with '-' modifier (nonempty base and matching value in a slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"a",
|
||||
"-",
|
||||
[]string{"b", "a", "c", "123"},
|
||||
`""`,
|
||||
},
|
||||
{
|
||||
"single file with unknown modifier (nonempty)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 1}},
|
||||
"",
|
||||
"?",
|
||||
"a",
|
||||
`""`,
|
||||
},
|
||||
|
||||
// multi file
|
||||
{
|
||||
"multi file with '+' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
nil,
|
||||
"+",
|
||||
"b",
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
"multi file with '+' modifier (nonempty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
[]string{"a"},
|
||||
"+",
|
||||
[]string{"b", "c"},
|
||||
`["a"]`,
|
||||
},
|
||||
{
|
||||
"multi file with '+' modifier (nonempty base; already existing value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b"},
|
||||
"+",
|
||||
"b",
|
||||
`["a","b"]`,
|
||||
},
|
||||
{
|
||||
"multi file with '-' modifier (empty base)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
nil,
|
||||
"-",
|
||||
[]string{"a"},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
"multi file with '-' modifier (nonempty base and empty modifier value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
"a",
|
||||
"-",
|
||||
"",
|
||||
`["a"]`,
|
||||
},
|
||||
{
|
||||
"multi file with '-' modifier (nonempty base and different value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
"a",
|
||||
"-",
|
||||
"b",
|
||||
`["a"]`,
|
||||
},
|
||||
{
|
||||
"multi file with '-' modifier (nonempty base and matching value)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
"-",
|
||||
"c",
|
||||
`["a","b","d"]`,
|
||||
},
|
||||
{
|
||||
"multi file with '-' modifier (nonempty base and matching value in a slice)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b", "c", "d"},
|
||||
"-",
|
||||
[]string{"b", "a", "123"},
|
||||
`["c","d"]`,
|
||||
},
|
||||
{
|
||||
"multi file with unknown modifier (nonempty)",
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Options: &schema.FileOptions{MaxSelect: 10}},
|
||||
[]string{"a", "b"},
|
||||
"?",
|
||||
"a",
|
||||
`["a","b"]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
result := s.field.PrepareValueWithModifier(s.baseValue, s.modifier, s.modifierValue)
|
||||
|
||||
encoded, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Fatalf("[%s] %v", s.name, err)
|
||||
}
|
||||
|
||||
if string(encoded) != s.expectJson {
|
||||
t.Fatalf("[%s], Expected %v, got %v", s.name, s.expectJson, string(encoded))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type fieldOptionsScenario struct {
|
||||
@@ -1306,7 +1996,7 @@ func TestRelationOptionsValidate(t *testing.T) {
|
||||
[]string{"maxSelect"},
|
||||
},
|
||||
{
|
||||
"MaxSelect > 0 && non-empty CollectionId",
|
||||
"MaxSelect > 0 && nonempty CollectionId",
|
||||
schema.RelationOptions{
|
||||
CollectionId: "abc",
|
||||
MaxSelect: types.Pointer(1),
|
||||
|
||||
@@ -44,6 +44,8 @@ type Settings struct {
|
||||
SpotifyAuth AuthProviderConfig `form:"spotifyAuth" json:"spotifyAuth"`
|
||||
KakaoAuth AuthProviderConfig `form:"kakaoAuth" json:"kakaoAuth"`
|
||||
TwitchAuth AuthProviderConfig `form:"twitchAuth" json:"twitchAuth"`
|
||||
StravaAuth AuthProviderConfig `form:"stravaAuth" json:"stravaAuth"`
|
||||
GiteeAuth AuthProviderConfig `form:"giteeAuth" json:"giteeAuth"`
|
||||
}
|
||||
|
||||
// New creates and returns a new default Settings instance.
|
||||
@@ -124,6 +126,12 @@ func New() *Settings {
|
||||
TwitchAuth: AuthProviderConfig{
|
||||
Enabled: false,
|
||||
},
|
||||
StravaAuth: AuthProviderConfig{
|
||||
Enabled: false,
|
||||
},
|
||||
GiteeAuth: AuthProviderConfig{
|
||||
Enabled: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +161,8 @@ func (s *Settings) Validate() error {
|
||||
validation.Field(&s.SpotifyAuth),
|
||||
validation.Field(&s.KakaoAuth),
|
||||
validation.Field(&s.TwitchAuth),
|
||||
validation.Field(&s.StravaAuth),
|
||||
validation.Field(&s.GiteeAuth),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -207,6 +217,8 @@ func (s *Settings) RedactClone() (*Settings, error) {
|
||||
&clone.SpotifyAuth.ClientSecret,
|
||||
&clone.KakaoAuth.ClientSecret,
|
||||
&clone.TwitchAuth.ClientSecret,
|
||||
&clone.StravaAuth.ClientSecret,
|
||||
&clone.GiteeAuth.ClientSecret,
|
||||
}
|
||||
|
||||
// mask all sensitive fields
|
||||
@@ -236,6 +248,8 @@ func (s *Settings) NamedAuthProviderConfigs() map[string]AuthProviderConfig {
|
||||
auth.NameSpotify: s.SpotifyAuth,
|
||||
auth.NameKakao: s.KakaoAuth,
|
||||
auth.NameTwitch: s.TwitchAuth,
|
||||
auth.NameStrava: s.StravaAuth,
|
||||
auth.NameGitee: s.GiteeAuth,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ func TestSettingsValidate(t *testing.T) {
|
||||
s.KakaoAuth.ClientId = ""
|
||||
s.TwitchAuth.Enabled = true
|
||||
s.TwitchAuth.ClientId = ""
|
||||
s.StravaAuth.Enabled = true
|
||||
s.StravaAuth.ClientId = ""
|
||||
s.GiteeAuth.Enabled = true
|
||||
s.GiteeAuth.ClientId = ""
|
||||
|
||||
// check if Validate() is triggering the members validate methods.
|
||||
err := s.Validate()
|
||||
@@ -76,6 +80,8 @@ func TestSettingsValidate(t *testing.T) {
|
||||
`"spotifyAuth":{`,
|
||||
`"kakaoAuth":{`,
|
||||
`"twitchAuth":{`,
|
||||
`"stravaAuth":{`,
|
||||
`"giteeAuth":{`,
|
||||
}
|
||||
|
||||
errBytes, _ := json.Marshal(err)
|
||||
@@ -124,6 +130,10 @@ func TestSettingsMerge(t *testing.T) {
|
||||
s2.KakaoAuth.ClientId = "kakao_test"
|
||||
s2.TwitchAuth.Enabled = true
|
||||
s2.TwitchAuth.ClientId = "twitch_test"
|
||||
s2.StravaAuth.Enabled = true
|
||||
s2.StravaAuth.ClientId = "strava_test"
|
||||
s2.GiteeAuth.Enabled = true
|
||||
s2.GiteeAuth.ClientId = "gitee_test"
|
||||
|
||||
if err := s1.Merge(s2); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -195,6 +205,8 @@ func TestSettingsRedactClone(t *testing.T) {
|
||||
s1.SpotifyAuth.ClientSecret = "test123"
|
||||
s1.KakaoAuth.ClientSecret = "test123"
|
||||
s1.TwitchAuth.ClientSecret = "test123"
|
||||
s1.StravaAuth.ClientSecret = "test123"
|
||||
s1.GiteeAuth.ClientSecret = "test123"
|
||||
|
||||
s2, err := s1.RedactClone()
|
||||
if err != nil {
|
||||
@@ -206,7 +218,7 @@ func TestSettingsRedactClone(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `{"meta":{"appName":"test123","appUrl":"http://localhost:8090","hideControls":false,"senderName":"Support","senderAddress":"support@example.com","verificationTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eThank you for joining us at {APP_NAME}.\u003c/p\u003e\n\u003cp\u003eClick on the button below to verify your email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eVerify\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Verify your {APP_NAME} email","actionUrl":"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}"},"resetPasswordTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to reset your password.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eReset password\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to reset your password, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Reset your {APP_NAME} password","actionUrl":"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}"},"confirmEmailChangeTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to confirm your new email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eConfirm new email\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to change your email address, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Confirm your {APP_NAME} new email address","actionUrl":"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"}},"logs":{"maxDays":5},"smtp":{"enabled":false,"host":"smtp.example.com","port":587,"username":"","password":"******","authMethod":"","tls":true},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","secret":"******","forcePathStyle":false},"adminAuthToken":{"secret":"******","duration":1209600},"adminPasswordResetToken":{"secret":"******","duration":1800},"recordAuthToken":{"secret":"******","duration":1209600},"recordPasswordResetToken":{"secret":"******","duration":1800},"recordEmailChangeToken":{"secret":"******","duration":1800},"recordVerificationToken":{"secret":"******","duration":604800},"emailAuth":{"enabled":false,"exceptDomains":null,"onlyDomains":null,"minPasswordLength":0},"googleAuth":{"enabled":false,"clientSecret":"******"},"facebookAuth":{"enabled":false,"clientSecret":"******"},"githubAuth":{"enabled":false,"clientSecret":"******"},"gitlabAuth":{"enabled":false,"clientSecret":"******"},"discordAuth":{"enabled":false,"clientSecret":"******"},"twitterAuth":{"enabled":false,"clientSecret":"******"},"microsoftAuth":{"enabled":false,"clientSecret":"******"},"spotifyAuth":{"enabled":false,"clientSecret":"******"},"kakaoAuth":{"enabled":false,"clientSecret":"******"},"twitchAuth":{"enabled":false,"clientSecret":"******"}}`
|
||||
expected := `{"meta":{"appName":"test123","appUrl":"http://localhost:8090","hideControls":false,"senderName":"Support","senderAddress":"support@example.com","verificationTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eThank you for joining us at {APP_NAME}.\u003c/p\u003e\n\u003cp\u003eClick on the button below to verify your email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eVerify\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Verify your {APP_NAME} email","actionUrl":"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}"},"resetPasswordTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to reset your password.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eReset password\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to reset your password, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Reset your {APP_NAME} password","actionUrl":"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}"},"confirmEmailChangeTemplate":{"body":"\u003cp\u003eHello,\u003c/p\u003e\n\u003cp\u003eClick on the button below to confirm your new email address.\u003c/p\u003e\n\u003cp\u003e\n \u003ca class=\"btn\" href=\"{ACTION_URL}\" target=\"_blank\" rel=\"noopener\"\u003eConfirm new email\u003c/a\u003e\n\u003c/p\u003e\n\u003cp\u003e\u003ci\u003eIf you didn't ask to change your email address, you can ignore this email.\u003c/i\u003e\u003c/p\u003e\n\u003cp\u003e\n Thanks,\u003cbr/\u003e\n {APP_NAME} team\n\u003c/p\u003e","subject":"Confirm your {APP_NAME} new email address","actionUrl":"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}"}},"logs":{"maxDays":5},"smtp":{"enabled":false,"host":"smtp.example.com","port":587,"username":"","password":"******","authMethod":"","tls":true},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","secret":"******","forcePathStyle":false},"adminAuthToken":{"secret":"******","duration":1209600},"adminPasswordResetToken":{"secret":"******","duration":1800},"recordAuthToken":{"secret":"******","duration":1209600},"recordPasswordResetToken":{"secret":"******","duration":1800},"recordEmailChangeToken":{"secret":"******","duration":1800},"recordVerificationToken":{"secret":"******","duration":604800},"emailAuth":{"enabled":false,"exceptDomains":null,"onlyDomains":null,"minPasswordLength":0},"googleAuth":{"enabled":false,"clientSecret":"******"},"facebookAuth":{"enabled":false,"clientSecret":"******"},"githubAuth":{"enabled":false,"clientSecret":"******"},"gitlabAuth":{"enabled":false,"clientSecret":"******"},"discordAuth":{"enabled":false,"clientSecret":"******"},"twitterAuth":{"enabled":false,"clientSecret":"******"},"microsoftAuth":{"enabled":false,"clientSecret":"******"},"spotifyAuth":{"enabled":false,"clientSecret":"******"},"kakaoAuth":{"enabled":false,"clientSecret":"******"},"twitchAuth":{"enabled":false,"clientSecret":"******"},"stravaAuth":{"enabled":false,"clientSecret":"******"},"giteeAuth":{"enabled":false,"clientSecret":"******"}}`
|
||||
|
||||
if encodedStr := string(encoded); encodedStr != expected {
|
||||
t.Fatalf("Expected\n%v\ngot\n%v", expected, encodedStr)
|
||||
@@ -227,6 +239,8 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
|
||||
s.SpotifyAuth.ClientId = "spotify_test"
|
||||
s.KakaoAuth.ClientId = "kakao_test"
|
||||
s.TwitchAuth.ClientId = "twitch_test"
|
||||
s.StravaAuth.ClientId = "strava_test"
|
||||
s.GiteeAuth.ClientId = "gitee_test"
|
||||
|
||||
result := s.NamedAuthProviderConfigs()
|
||||
|
||||
@@ -247,6 +261,8 @@ func TestNamedAuthProviderConfigs(t *testing.T) {
|
||||
`"twitter":{"enabled":false,"clientId":"twitter_test"}`,
|
||||
`"kakao":{"enabled":false,"clientId":"kakao_test"}`,
|
||||
`"twitch":{"enabled":false,"clientId":"twitch_test"}`,
|
||||
`"strava":{"enabled":false,"clientId":"strava_test"}`,
|
||||
`"gitee":{"enabled":false,"clientId":"gitee_test"}`,
|
||||
}
|
||||
for _, p := range expectedParts {
|
||||
if !strings.Contains(encodedStr, p) {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package jsvm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/dop251/goja_nodejs/console"
|
||||
"github.com/dop251/goja_nodejs/require"
|
||||
@@ -75,7 +77,7 @@ func RegisterMigrations(app core.App, options *MigrationsOptions) error {
|
||||
|
||||
_, err := vm.RunString(string(content))
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to run migration %s: %w", file, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +99,8 @@ func readDirFiles(dirPath string) (map[string][]byte, error) {
|
||||
result := map[string][]byte{}
|
||||
|
||||
for _, f := range files {
|
||||
if f.IsDir() {
|
||||
continue
|
||||
if f.IsDir() || !strings.HasSuffix(f.Name(), ".js") {
|
||||
continue // not a .js file
|
||||
}
|
||||
raw, err := os.ReadFile(filepath.Join(dirPath, f.Name()))
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
)
|
||||
|
||||
var _ dbx.Expression = (*multiMatchSubquery)(nil)
|
||||
|
||||
// join defines the specification for a single SQL JOIN clause.
|
||||
type join struct {
|
||||
tableName string
|
||||
tableAlias string
|
||||
on dbx.Expression
|
||||
}
|
||||
|
||||
// multiMatchSubquery defines a record multi-match subquery expression.
|
||||
type multiMatchSubquery struct {
|
||||
baseTableAlias string
|
||||
fromTableName string
|
||||
fromTableAlias string
|
||||
valueIdentifier string
|
||||
joins []*join
|
||||
params dbx.Params
|
||||
}
|
||||
|
||||
// Build converts the expression into a SQL fragment.
|
||||
//
|
||||
// Implements [dbx.Expression] interface.
|
||||
func (m *multiMatchSubquery) Build(db *dbx.DB, params dbx.Params) string {
|
||||
if m.baseTableAlias == "" || m.fromTableName == "" || m.fromTableAlias == "" {
|
||||
return "0=1"
|
||||
}
|
||||
|
||||
if params == nil {
|
||||
params = m.params
|
||||
} else {
|
||||
// merge by updating the parent params
|
||||
for k, v := range m.params {
|
||||
params[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
var mergedJoins strings.Builder
|
||||
for i, j := range m.joins {
|
||||
if i > 0 {
|
||||
mergedJoins.WriteString(" ")
|
||||
}
|
||||
mergedJoins.WriteString("LEFT JOIN ")
|
||||
mergedJoins.WriteString(db.QuoteTableName(j.tableName))
|
||||
mergedJoins.WriteString(" ")
|
||||
mergedJoins.WriteString(db.QuoteTableName(j.tableAlias))
|
||||
if j.on != nil {
|
||||
mergedJoins.WriteString(" ON ")
|
||||
mergedJoins.WriteString(j.on.Build(db, params))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
`SELECT %s as [[multiMatchValue]] FROM %s %s %s WHERE %s = %s`,
|
||||
db.QuoteColumnName(m.valueIdentifier),
|
||||
db.QuoteTableName(m.fromTableName),
|
||||
db.QuoteTableName(m.fromTableAlias),
|
||||
mergedJoins.String(),
|
||||
db.QuoteColumnName(m.fromTableAlias+".id"),
|
||||
db.QuoteColumnName(m.baseTableAlias+".id"),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,589 @@
|
||||
package resolvers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
"github.com/pocketbase/pocketbase/tools/inflector"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/search"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
// parseAndRun starts a new one-off RecordFieldResolver.Resolve execution.
|
||||
func parseAndRun(fieldName string, resolver *RecordFieldResolver) (*search.ResolverResult, error) {
|
||||
r := &runner{
|
||||
fieldName: fieldName,
|
||||
resolver: resolver,
|
||||
}
|
||||
|
||||
return r.run()
|
||||
}
|
||||
|
||||
type runner struct {
|
||||
used bool // indicates whether the runner was already executed
|
||||
resolver *RecordFieldResolver // resolver is the shared expression fields resolver
|
||||
fieldName string // the name of the single field expression the runner is responsible for
|
||||
|
||||
// shared processing state
|
||||
// ---------------------------------------------------------------
|
||||
activeProps []string // holds the active props that remains to be processed
|
||||
activeCollectionName string // the last used collection name
|
||||
activeTableAlias string // the last used table alias
|
||||
allowHiddenFields bool // indicates whether hidden fields (eg. email) should be allowed without extra conditions
|
||||
nullifyMisingField bool // indicating whether to return null on missing field or return an error
|
||||
withMultiMatch bool // indicates whether to attach a multiMatchSubquery condition to the ResolverResult
|
||||
multiMatchActiveTableAlias string // the last used multi-match table alias
|
||||
multiMatch *multiMatchSubquery // the multi-match subquery expression generated from the fieldName
|
||||
}
|
||||
|
||||
func (r *runner) run() (*search.ResolverResult, error) {
|
||||
if r.used {
|
||||
return nil, fmt.Errorf("the runner was already used")
|
||||
}
|
||||
|
||||
if len(r.resolver.allowedFields) > 0 && !list.ExistInSliceWithRegex(r.fieldName, r.resolver.allowedFields) {
|
||||
return nil, fmt.Errorf("failed to resolve field %q", r.fieldName)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
r.used = true
|
||||
}()
|
||||
|
||||
r.prepare()
|
||||
|
||||
// check for @collection field (aka. non-relational join)
|
||||
// must be in the format "@collection.COLLECTION_NAME.FIELD[.FIELD2....]"
|
||||
if r.activeProps[0] == "@collection" {
|
||||
return r.processCollectionField()
|
||||
}
|
||||
|
||||
if r.activeProps[0] == "@request" {
|
||||
if r.resolver.requestData == nil {
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.fieldName, "@request.auth.") {
|
||||
return r.processRequestAuthField()
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.fieldName, "@request.data.") && len(r.activeProps) > 2 {
|
||||
name, modifier, err := splitModifier(r.activeProps[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dataField := r.resolver.baseCollection.Schema.GetFieldByName(name)
|
||||
if dataField == nil {
|
||||
return r.resolver.resolveStaticRequestField(r.activeProps[1:]...)
|
||||
}
|
||||
|
||||
dataField.InitOptions()
|
||||
|
||||
// check for data relation field
|
||||
if dataField.Type == schema.FieldTypeRelation && len(r.activeProps) > 3 {
|
||||
return r.processRequestDataRelationField(dataField)
|
||||
}
|
||||
|
||||
// check for select:each field
|
||||
if modifier == eachModifier && dataField.Type == schema.FieldTypeSelect && len(r.activeProps) == 3 {
|
||||
return r.processRequestDataSelectEachModifier(dataField)
|
||||
}
|
||||
|
||||
// check for data arrayble fields ":length" modifier
|
||||
if modifier == lengthModifier && list.ExistInSlice(dataField.Type, schema.ArraybleFieldTypes()) && len(r.activeProps) == 3 {
|
||||
return r.processRequestDataLengthModifier(dataField)
|
||||
}
|
||||
}
|
||||
|
||||
// some other @request.* static field
|
||||
return r.resolver.resolveStaticRequestField(r.activeProps[1:]...)
|
||||
}
|
||||
|
||||
// regular field
|
||||
return r.processActiveProps()
|
||||
}
|
||||
|
||||
func (r *runner) prepare() {
|
||||
r.activeProps = strings.Split(r.fieldName, ".")
|
||||
|
||||
r.activeCollectionName = r.resolver.baseCollection.Name
|
||||
r.activeTableAlias = inflector.Columnify(r.activeCollectionName)
|
||||
|
||||
r.allowHiddenFields = r.resolver.allowHiddenFields
|
||||
// always allow hidden fields since the @.* filter is a system one
|
||||
if r.activeProps[0] == "@collection" || r.activeProps[0] == "@request" {
|
||||
r.allowHiddenFields = true
|
||||
}
|
||||
|
||||
// enable the ignore flag for missing @request.* fields for backward
|
||||
// compatibility and consistency with all @request.* filter fields and types
|
||||
r.nullifyMisingField = r.activeProps[0] == "@request"
|
||||
|
||||
// prepare a multi-match subquery
|
||||
r.multiMatch = &multiMatchSubquery{
|
||||
baseTableAlias: r.activeTableAlias,
|
||||
params: dbx.Params{},
|
||||
}
|
||||
r.multiMatch.fromTableName = inflector.Columnify(r.activeCollectionName)
|
||||
r.multiMatch.fromTableAlias = "__mm_" + r.activeTableAlias
|
||||
r.multiMatchActiveTableAlias = r.multiMatch.fromTableAlias
|
||||
r.withMultiMatch = false
|
||||
}
|
||||
|
||||
func (r *runner) processCollectionField() (*search.ResolverResult, error) {
|
||||
if len(r.activeProps) < 3 {
|
||||
return nil, fmt.Errorf("invalid @collection field path in %q", r.fieldName)
|
||||
}
|
||||
|
||||
collection, err := r.resolver.loadCollection(r.activeProps[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load collection %q from field path %q", r.activeProps[1], r.fieldName)
|
||||
}
|
||||
|
||||
r.activeCollectionName = collection.Name
|
||||
r.activeTableAlias = inflector.Columnify("__collection_" + r.activeCollectionName)
|
||||
|
||||
r.withMultiMatch = true
|
||||
|
||||
// join the collection to the main query
|
||||
r.resolver.registerJoin(inflector.Columnify(collection.Name), r.activeTableAlias, nil)
|
||||
|
||||
// join the collection to the multi-match subquery
|
||||
r.multiMatchActiveTableAlias = "__mm" + r.activeTableAlias
|
||||
r.multiMatch.joins = append(r.multiMatch.joins, &join{
|
||||
tableName: inflector.Columnify(collection.Name),
|
||||
tableAlias: r.multiMatchActiveTableAlias,
|
||||
})
|
||||
|
||||
// leave only the collection fields
|
||||
// aka. @collection.someCollection.fieldA.fieldB -> fieldA.fieldB
|
||||
r.activeProps = r.activeProps[2:]
|
||||
|
||||
return r.processActiveProps()
|
||||
}
|
||||
|
||||
func (r *runner) processRequestAuthField() (*search.ResolverResult, error) {
|
||||
// plain auth field
|
||||
// ---
|
||||
if list.ExistInSlice(r.fieldName, plainRequestAuthFields) {
|
||||
return r.resolver.resolveStaticRequestField(r.activeProps[1:]...)
|
||||
}
|
||||
|
||||
// resolve the auth collection field
|
||||
// ---
|
||||
if r.resolver.requestData == nil || r.resolver.requestData.AuthRecord == nil || r.resolver.requestData.AuthRecord.Collection() == nil {
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
}
|
||||
|
||||
collection := r.resolver.requestData.AuthRecord.Collection()
|
||||
r.resolver.loadedCollections = append(r.resolver.loadedCollections, collection)
|
||||
|
||||
r.activeCollectionName = collection.Name
|
||||
r.activeTableAlias = "__auth_" + inflector.Columnify(r.activeCollectionName)
|
||||
|
||||
// join the auth collection to the main query
|
||||
r.resolver.registerJoin(
|
||||
inflector.Columnify(r.activeCollectionName),
|
||||
r.activeTableAlias,
|
||||
dbx.HashExp{
|
||||
// aka. __auth_users.id = :userId
|
||||
(r.activeTableAlias + ".id"): r.resolver.requestData.AuthRecord.Id,
|
||||
},
|
||||
)
|
||||
|
||||
// join the auth collection to the multi-match subquery
|
||||
r.multiMatchActiveTableAlias = "__mm_" + r.activeTableAlias
|
||||
r.multiMatch.joins = append(
|
||||
r.multiMatch.joins,
|
||||
&join{
|
||||
tableName: inflector.Columnify(r.activeCollectionName),
|
||||
tableAlias: r.multiMatchActiveTableAlias,
|
||||
on: dbx.HashExp{
|
||||
(r.multiMatchActiveTableAlias + ".id"): r.resolver.requestData.AuthRecord.Id,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// leave only the auth relation fields
|
||||
// aka. @request.auth.fieldA.fieldB -> fieldA.fieldB
|
||||
r.activeProps = r.activeProps[2:]
|
||||
|
||||
return r.processActiveProps()
|
||||
}
|
||||
|
||||
func (r *runner) processRequestDataLengthModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||
dataItems := list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
||||
|
||||
result := &search.ResolverResult{
|
||||
Identifier: fmt.Sprintf("%d", len(dataItems)),
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *runner) processRequestDataSelectEachModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||
options, ok := dataField.Options.(*schema.SelectOptions)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to initialize field %q options", dataField.Name)
|
||||
}
|
||||
|
||||
dataItems := list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
||||
rawJson, err := json.Marshal(dataItems)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot marshalize the data select item for field %q", r.activeProps[2])
|
||||
}
|
||||
|
||||
placeholder := "dataSelect" + security.PseudorandomString(4)
|
||||
cleanFieldName := inflector.Columnify(dataField.Name)
|
||||
jeTable := fmt.Sprintf("json_each({:%s})", placeholder)
|
||||
jeAlias := "__dataSelect_" + cleanFieldName + "_je"
|
||||
r.resolver.registerJoin(jeTable, jeAlias, nil)
|
||||
|
||||
result := &search.ResolverResult{
|
||||
Identifier: fmt.Sprintf("[[%s.value]]", jeAlias),
|
||||
Params: dbx.Params{placeholder: rawJson},
|
||||
}
|
||||
|
||||
if options.MaxSelect != 1 {
|
||||
r.withMultiMatch = true
|
||||
}
|
||||
|
||||
if r.withMultiMatch {
|
||||
placeholder2 := "mm" + placeholder
|
||||
jeTable2 := fmt.Sprintf("json_each({:%s})", placeholder2)
|
||||
jeAlias2 := "__mm" + jeAlias
|
||||
|
||||
r.multiMatch.joins = append(r.multiMatch.joins, &join{
|
||||
tableName: jeTable2,
|
||||
tableAlias: jeAlias2,
|
||||
})
|
||||
r.multiMatch.params[placeholder2] = rawJson
|
||||
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.value]]", jeAlias2)
|
||||
|
||||
result.MultiMatchSubQuery = r.multiMatch
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *runner) processRequestDataRelationField(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||
options, ok := dataField.Options.(*schema.RelationOptions)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to initialize data field %q options", dataField.Name)
|
||||
}
|
||||
|
||||
dataRelCollection, err := r.resolver.loadCollection(options.CollectionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load collection %q from data field %q", options.CollectionId, dataField.Name)
|
||||
}
|
||||
|
||||
var dataRelIds []string
|
||||
if r.resolver.requestData != nil && len(r.resolver.requestData.Data) != 0 {
|
||||
dataRelIds = list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
||||
}
|
||||
if len(dataRelIds) == 0 {
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
}
|
||||
|
||||
r.activeCollectionName = dataRelCollection.Name
|
||||
r.activeTableAlias = inflector.Columnify("__data_" + dataRelCollection.Name)
|
||||
|
||||
// join the data rel collection to the main collection
|
||||
r.resolver.registerJoin(
|
||||
inflector.Columnify(r.activeCollectionName),
|
||||
r.activeTableAlias,
|
||||
dbx.In(
|
||||
fmt.Sprintf("[[%s.id]]", inflector.Columnify(r.activeTableAlias)),
|
||||
list.ToInterfaceSlice(dataRelIds)...,
|
||||
),
|
||||
)
|
||||
|
||||
if options.MaxSelect == nil || *options.MaxSelect != 1 {
|
||||
r.withMultiMatch = true
|
||||
}
|
||||
|
||||
// join the data rel collection to the multi-match subquery
|
||||
r.multiMatchActiveTableAlias = inflector.Columnify("__data_mm_" + dataRelCollection.Name)
|
||||
r.multiMatch.joins = append(
|
||||
r.multiMatch.joins,
|
||||
&join{
|
||||
tableName: inflector.Columnify(r.activeCollectionName),
|
||||
tableAlias: r.multiMatchActiveTableAlias,
|
||||
on: dbx.In(r.multiMatchActiveTableAlias+".id", list.ToInterfaceSlice(dataRelIds)...),
|
||||
},
|
||||
)
|
||||
|
||||
// leave only the data relation fields
|
||||
// aka. @request.data.someRel.fieldA.fieldB -> fieldA.fieldB
|
||||
r.activeProps = r.activeProps[3:]
|
||||
|
||||
return r.processActiveProps()
|
||||
}
|
||||
|
||||
func (r *runner) processActiveProps() (*search.ResolverResult, error) {
|
||||
totalProps := len(r.activeProps)
|
||||
|
||||
for i, prop := range r.activeProps {
|
||||
collection, err := r.resolver.loadCollection(r.activeCollectionName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve field %q", prop)
|
||||
}
|
||||
|
||||
// last prop
|
||||
if i == totalProps-1 {
|
||||
// system field, aka. internal model prop
|
||||
// (always available but not part of the collection schema)
|
||||
// -------------------------------------------------------
|
||||
if list.ExistInSlice(prop, resolvableSystemFieldNames(collection)) {
|
||||
result := &search.ResolverResult{
|
||||
Identifier: fmt.Sprintf("[[%s.%s]]", r.activeTableAlias, inflector.Columnify(prop)),
|
||||
}
|
||||
|
||||
// allow querying only auth records with emails marked as public
|
||||
if prop == schema.FieldNameEmail && !r.allowHiddenFields {
|
||||
result.AfterBuild = func(expr dbx.Expression) dbx.Expression {
|
||||
return dbx.And(expr, dbx.NewExp(fmt.Sprintf(
|
||||
"[[%s.%s]] = TRUE",
|
||||
r.activeTableAlias,
|
||||
schema.FieldNameEmailVisibility,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
if r.withMultiMatch {
|
||||
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.%s]]", r.multiMatchActiveTableAlias, inflector.Columnify(prop))
|
||||
result.MultiMatchSubQuery = r.multiMatch
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
name, modifier, err := splitModifier(prop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
field := collection.Schema.GetFieldByName(name)
|
||||
if field == nil {
|
||||
if r.nullifyMisingField {
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown field %q", name)
|
||||
}
|
||||
|
||||
cleanFieldName := inflector.Columnify(field.Name)
|
||||
|
||||
// arrayble fields ":length" modifier
|
||||
// -------------------------------------------------------
|
||||
if modifier == lengthModifier && list.ExistInSlice(field.Type, schema.ArraybleFieldTypes()) {
|
||||
jePair := r.activeTableAlias + "." + cleanFieldName
|
||||
|
||||
result := &search.ResolverResult{
|
||||
Identifier: jsonArrayLength(jePair),
|
||||
}
|
||||
|
||||
if r.withMultiMatch {
|
||||
jePair2 := r.multiMatchActiveTableAlias + "." + cleanFieldName
|
||||
r.multiMatch.valueIdentifier = jsonArrayLength(jePair2)
|
||||
result.MultiMatchSubQuery = r.multiMatch
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// select field with ":each" modifier
|
||||
// -------------------------------------------------------
|
||||
if field.Type == schema.FieldTypeSelect && modifier == eachModifier {
|
||||
jePair := r.activeTableAlias + "." + cleanFieldName
|
||||
jeAlias := r.activeTableAlias + "_" + cleanFieldName + "_je"
|
||||
r.resolver.registerJoin(jsonEach(jePair), jeAlias, nil)
|
||||
|
||||
result := &search.ResolverResult{
|
||||
Identifier: fmt.Sprintf("[[%s.value]]", jeAlias),
|
||||
}
|
||||
|
||||
field.InitOptions()
|
||||
options, ok := field.Options.(*schema.SelectOptions)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to initialize field %q options", prop)
|
||||
}
|
||||
|
||||
if options.MaxSelect != 1 {
|
||||
r.withMultiMatch = true
|
||||
}
|
||||
|
||||
if r.withMultiMatch {
|
||||
jePair2 := r.multiMatchActiveTableAlias + "." + cleanFieldName
|
||||
jeAlias2 := r.multiMatchActiveTableAlias + "_" + cleanFieldName + "_je"
|
||||
|
||||
r.multiMatch.joins = append(r.multiMatch.joins, &join{
|
||||
tableName: jsonEach(jePair2),
|
||||
tableAlias: jeAlias2,
|
||||
})
|
||||
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.value]]", jeAlias2)
|
||||
|
||||
result.MultiMatchSubQuery = r.multiMatch
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// default
|
||||
// -------------------------------------------------------
|
||||
result := &search.ResolverResult{
|
||||
Identifier: fmt.Sprintf("[[%s.%s]]", r.activeTableAlias, cleanFieldName),
|
||||
}
|
||||
|
||||
if r.withMultiMatch {
|
||||
r.multiMatch.valueIdentifier = fmt.Sprintf("[[%s.%s]]", r.multiMatchActiveTableAlias, cleanFieldName)
|
||||
result.MultiMatchSubQuery = r.multiMatch
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
field := collection.Schema.GetFieldByName(prop)
|
||||
if field == nil {
|
||||
if r.nullifyMisingField {
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown field %q", prop)
|
||||
}
|
||||
|
||||
// check if it is a json field
|
||||
if field.Type == schema.FieldTypeJson {
|
||||
var jsonPath strings.Builder
|
||||
jsonPath.WriteString("$")
|
||||
for _, p := range r.activeProps[i+1:] {
|
||||
if _, err := strconv.Atoi(p); err == nil {
|
||||
jsonPath.WriteString("[")
|
||||
jsonPath.WriteString(inflector.Columnify(p))
|
||||
jsonPath.WriteString("]")
|
||||
} else {
|
||||
jsonPath.WriteString(".")
|
||||
jsonPath.WriteString(inflector.Columnify(p))
|
||||
}
|
||||
}
|
||||
|
||||
result := &search.ResolverResult{
|
||||
Identifier: fmt.Sprintf(
|
||||
"JSON_EXTRACT([[%s.%s]], '%s')",
|
||||
r.activeTableAlias,
|
||||
inflector.Columnify(prop),
|
||||
jsonPath.String(),
|
||||
),
|
||||
}
|
||||
|
||||
if r.withMultiMatch {
|
||||
r.multiMatch.valueIdentifier = fmt.Sprintf(
|
||||
"JSON_EXTRACT([[%s.%s]], '%s')",
|
||||
r.multiMatchActiveTableAlias,
|
||||
inflector.Columnify(prop),
|
||||
jsonPath.String(),
|
||||
)
|
||||
result.MultiMatchSubQuery = r.multiMatch
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// check if it is a relation field
|
||||
if field.Type != schema.FieldTypeRelation {
|
||||
return nil, fmt.Errorf("field %q is not a valid relation", prop)
|
||||
}
|
||||
|
||||
// join the relation to the main query
|
||||
// ---
|
||||
field.InitOptions()
|
||||
options, ok := field.Options.(*schema.RelationOptions)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to initialize field %q options", prop)
|
||||
}
|
||||
|
||||
relCollection, relErr := r.resolver.loadCollection(options.CollectionId)
|
||||
if relErr != nil {
|
||||
return nil, fmt.Errorf("failed to find field %q collection", prop)
|
||||
}
|
||||
|
||||
cleanFieldName := inflector.Columnify(field.Name)
|
||||
newCollectionName := relCollection.Name
|
||||
newTableAlias := r.activeTableAlias + "_" + cleanFieldName
|
||||
|
||||
jeAlias := r.activeTableAlias + "_" + cleanFieldName + "_je"
|
||||
jePair := r.activeTableAlias + "." + cleanFieldName
|
||||
r.resolver.registerJoin(jsonEach(jePair), jeAlias, nil)
|
||||
r.resolver.registerJoin(
|
||||
inflector.Columnify(newCollectionName),
|
||||
newTableAlias,
|
||||
dbx.NewExp(fmt.Sprintf("[[%s.id]] = [[%s.value]]", newTableAlias, jeAlias)),
|
||||
)
|
||||
r.activeCollectionName = newCollectionName
|
||||
r.activeTableAlias = newTableAlias
|
||||
// ---
|
||||
|
||||
// join the relation to the multi-match subquery
|
||||
// ---
|
||||
if options.MaxSelect == nil || *options.MaxSelect != 1 {
|
||||
r.withMultiMatch = true
|
||||
}
|
||||
|
||||
newTableAlias2 := r.multiMatchActiveTableAlias + "_" + cleanFieldName
|
||||
jeAlias2 := r.multiMatchActiveTableAlias + "_" + cleanFieldName + "_je"
|
||||
jePair2 := r.multiMatchActiveTableAlias + "." + cleanFieldName
|
||||
r.multiMatchActiveTableAlias = newTableAlias2
|
||||
|
||||
r.multiMatch.joins = append(
|
||||
r.multiMatch.joins,
|
||||
&join{
|
||||
tableName: jsonEach(jePair2),
|
||||
tableAlias: jeAlias2,
|
||||
},
|
||||
&join{
|
||||
tableName: inflector.Columnify(newCollectionName),
|
||||
tableAlias: newTableAlias2,
|
||||
on: dbx.NewExp(fmt.Sprintf("[[%s.id]] = [[%s.value]]", newTableAlias2, jeAlias2)),
|
||||
},
|
||||
)
|
||||
// ---
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to resolve field %q", r.fieldName)
|
||||
}
|
||||
|
||||
func jsonArrayLength(tableColumnPair string) string {
|
||||
return fmt.Sprintf(
|
||||
// note: the case is used to normalize value access for single and multiple relations.
|
||||
`json_array_length(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
|
||||
tableColumnPair, tableColumnPair, tableColumnPair,
|
||||
)
|
||||
}
|
||||
|
||||
func jsonEach(tableColumnPair string) string {
|
||||
return fmt.Sprintf(
|
||||
// note: the case is used to normalize value access for single and multiple relations.
|
||||
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
|
||||
tableColumnPair, tableColumnPair, tableColumnPair,
|
||||
)
|
||||
}
|
||||
|
||||
func resolvableSystemFieldNames(collection *models.Collection) []string {
|
||||
result := schema.BaseModelFieldNames()
|
||||
|
||||
if collection.IsAuth() {
|
||||
result = append(
|
||||
result,
|
||||
schema.FieldNameUsername,
|
||||
schema.FieldNameVerified,
|
||||
schema.FieldNameEmailVisibility,
|
||||
schema.FieldNameEmail,
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
+129
-284
@@ -10,18 +10,20 @@ import (
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
"github.com/pocketbase/pocketbase/tools/inflector"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/search"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// ensure that `search.FieldResolver` interface is implemented
|
||||
var _ search.FieldResolver = (*RecordFieldResolver)(nil)
|
||||
// filter modifiers
|
||||
const (
|
||||
eachModifier string = "each"
|
||||
issetModifier string = "isset"
|
||||
lengthModifier string = "length"
|
||||
)
|
||||
|
||||
// list of auth filter fields that don't require join with the auth
|
||||
// collection or any other extra checks to be resolved
|
||||
// collection or any other extra checks to be resolved.
|
||||
var plainRequestAuthFields = []string{
|
||||
"@request.auth." + schema.FieldNameId,
|
||||
"@request.auth." + schema.FieldNameCollectionId,
|
||||
@@ -34,32 +36,28 @@ var plainRequestAuthFields = []string{
|
||||
"@request.auth." + schema.FieldNameUpdated,
|
||||
}
|
||||
|
||||
type join struct {
|
||||
id string
|
||||
table string
|
||||
on dbx.Expression
|
||||
}
|
||||
// ensure that `search.FieldResolver` interface is implemented
|
||||
var _ search.FieldResolver = (*RecordFieldResolver)(nil)
|
||||
|
||||
// RecordFieldResolver defines a custom search resolver struct for
|
||||
// managing Record model search fields.
|
||||
//
|
||||
// Usually used together with `search.Provider`. Example:
|
||||
// resolver := resolvers.NewRecordFieldResolver(
|
||||
// app.Dao(),
|
||||
// myCollection,
|
||||
// &models.RequestData{...},
|
||||
// true,
|
||||
// )
|
||||
// provider := search.NewProvider(resolver)
|
||||
// ...
|
||||
// resolver := resolvers.NewRecordFieldResolver(
|
||||
// app.Dao(),
|
||||
// myCollection,
|
||||
// &models.RequestData{...},
|
||||
// true,
|
||||
// )
|
||||
// provider := search.NewProvider(resolver)
|
||||
// ...
|
||||
type RecordFieldResolver struct {
|
||||
dao *daos.Dao
|
||||
baseCollection *models.Collection
|
||||
allowHiddenFields bool
|
||||
allowedFields []string
|
||||
loadedCollections []*models.Collection
|
||||
joins []join // we cannot use a map because the insertion order is not preserved
|
||||
exprs []dbx.Expression
|
||||
joins []*join // we cannot use a map because the insertion order is not preserved
|
||||
requestData *models.RequestData
|
||||
staticRequestData map[string]any
|
||||
}
|
||||
@@ -76,20 +74,18 @@ func NewRecordFieldResolver(
|
||||
baseCollection: baseCollection,
|
||||
requestData: requestData,
|
||||
allowHiddenFields: allowHiddenFields,
|
||||
joins: []join{},
|
||||
exprs: []dbx.Expression{},
|
||||
joins: []*join{},
|
||||
loadedCollections: []*models.Collection{baseCollection},
|
||||
allowedFields: []string{
|
||||
`^\w+[\w\.]*$`,
|
||||
`^\w+[\w\.\:]*$`,
|
||||
`^\@request\.method$`,
|
||||
`^\@request\.auth\.\w+[\w\.]*$`,
|
||||
`^\@request\.data\.\w+[\w\.]*$`,
|
||||
`^\@request\.query\.\w+[\w\.]*$`,
|
||||
`^\@collection\.\w+\.\w+[\w\.]*$`,
|
||||
`^\@request\.auth\.[\w\.\:]*\w+$`,
|
||||
`^\@request\.data\.[\w\.\:]*\w+$`,
|
||||
`^\@request\.query\.[\w\.\:]*\w+$`,
|
||||
`^\@collection\.\w+\.[\w\.\:]*\w+$`,
|
||||
},
|
||||
}
|
||||
|
||||
// @todo remove after IN operator and multi-match filter enhancements
|
||||
r.staticRequestData = map[string]any{}
|
||||
if r.requestData != nil {
|
||||
r.staticRequestData["method"] = r.requestData.Method
|
||||
@@ -115,13 +111,10 @@ func (r *RecordFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
|
||||
query.Distinct(true)
|
||||
|
||||
for _, join := range r.joins {
|
||||
query.LeftJoin(join.table, join.on)
|
||||
}
|
||||
}
|
||||
|
||||
for _, expr := range r.exprs {
|
||||
if expr != nil {
|
||||
query.AndWhere(expr)
|
||||
query.LeftJoin(
|
||||
(join.tableName + " " + join.tableAlias),
|
||||
join.on,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,225 +123,63 @@ func (r *RecordFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
|
||||
|
||||
// Resolve implements `search.FieldResolver` interface.
|
||||
//
|
||||
// Example of resolvable field formats:
|
||||
// id
|
||||
// project.screen.status
|
||||
// @request.status
|
||||
// @request.auth.someRelation.name
|
||||
// @collection.product.name
|
||||
func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, placeholderParams dbx.Params, err error) {
|
||||
if len(r.allowedFields) > 0 && !list.ExistInSliceWithRegex(fieldName, r.allowedFields) {
|
||||
return "", nil, fmt.Errorf("Failed to resolve field %q", fieldName)
|
||||
}
|
||||
|
||||
props := strings.Split(fieldName, ".")
|
||||
|
||||
currentCollectionName := r.baseCollection.Name
|
||||
currentTableAlias := inflector.Columnify(currentCollectionName)
|
||||
|
||||
// flag indicating whether to return null on missing field or return on an error
|
||||
nullifyMisingField := false
|
||||
|
||||
allowHiddenFields := r.allowHiddenFields
|
||||
|
||||
// check for @collection field (aka. non-relational join)
|
||||
// must be in the format "@collection.COLLECTION_NAME.FIELD[.FIELD2....]"
|
||||
if props[0] == "@collection" {
|
||||
if len(props) < 3 {
|
||||
return "", nil, fmt.Errorf("Invalid @collection field path in %q.", fieldName)
|
||||
}
|
||||
|
||||
currentCollectionName = props[1]
|
||||
currentTableAlias = inflector.Columnify("__collection_" + currentCollectionName)
|
||||
|
||||
collection, err := r.loadCollection(currentCollectionName)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("Failed to load collection %q from field path %q.", currentCollectionName, fieldName)
|
||||
}
|
||||
|
||||
// always allow hidden fields since the @collection.* filter is a system one
|
||||
allowHiddenFields = true
|
||||
|
||||
r.registerJoin(inflector.Columnify(collection.Name), currentTableAlias, nil)
|
||||
|
||||
props = props[2:] // leave only the collection fields
|
||||
} else if props[0] == "@request" {
|
||||
if len(props) == 1 {
|
||||
return "", nil, fmt.Errorf("Invalid @request data field path in %q.", fieldName)
|
||||
}
|
||||
|
||||
if r.requestData == nil {
|
||||
return "NULL", nil, nil
|
||||
}
|
||||
|
||||
// plain @request.* field
|
||||
if !strings.HasPrefix(fieldName, "@request.auth.") || list.ExistInSlice(fieldName, plainRequestAuthFields) {
|
||||
return r.resolveStaticRequestField(props[1:]...)
|
||||
}
|
||||
|
||||
// always allow hidden fields since the @request.* filter is a system one
|
||||
allowHiddenFields = true
|
||||
|
||||
// enable the ignore flag for missing @request.auth.* fields
|
||||
// for consistency with @request.data.* and @request.query.*
|
||||
nullifyMisingField = true
|
||||
|
||||
// resolve the auth collection fields
|
||||
// ---
|
||||
if r.requestData == nil || r.requestData.AuthRecord == nil || r.requestData.AuthRecord.Collection() == nil {
|
||||
return "NULL", nil, nil
|
||||
}
|
||||
|
||||
collection := r.requestData.AuthRecord.Collection()
|
||||
r.loadedCollections = append(r.loadedCollections, collection)
|
||||
|
||||
currentCollectionName = collection.Name
|
||||
currentTableAlias = "__auth_" + inflector.Columnify(currentCollectionName)
|
||||
|
||||
authIdParamKey := "auth" + security.PseudorandomString(5)
|
||||
authIdParams := dbx.Params{authIdParamKey: r.requestData.AuthRecord.Id}
|
||||
// ---
|
||||
|
||||
// join the auth collection
|
||||
r.registerJoin(
|
||||
inflector.Columnify(collection.Name),
|
||||
currentTableAlias,
|
||||
dbx.NewExp(fmt.Sprintf(
|
||||
// aka. __auth_users.id = :userId
|
||||
"[[%s.id]] = {:%s}",
|
||||
inflector.Columnify(currentTableAlias),
|
||||
authIdParamKey,
|
||||
), authIdParams),
|
||||
)
|
||||
|
||||
props = props[2:] // leave only the auth relation fields
|
||||
}
|
||||
|
||||
totalProps := len(props)
|
||||
|
||||
for i, prop := range props {
|
||||
collection, err := r.loadCollection(currentCollectionName)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("Failed to resolve field %q.", prop)
|
||||
}
|
||||
|
||||
systemFieldNames := schema.BaseModelFieldNames()
|
||||
if collection.IsAuth() {
|
||||
systemFieldNames = append(
|
||||
systemFieldNames,
|
||||
schema.FieldNameUsername,
|
||||
schema.FieldNameVerified,
|
||||
schema.FieldNameEmailVisibility,
|
||||
schema.FieldNameEmail,
|
||||
)
|
||||
}
|
||||
|
||||
// internal model prop (always available but not part of the collection schema)
|
||||
if list.ExistInSlice(prop, systemFieldNames) {
|
||||
// allow querying only auth records with emails marked as public
|
||||
if prop == schema.FieldNameEmail && !allowHiddenFields {
|
||||
r.registerExpr(dbx.NewExp(fmt.Sprintf(
|
||||
"[[%s.%s]] = TRUE",
|
||||
currentTableAlias,
|
||||
inflector.Columnify(schema.FieldNameEmailVisibility),
|
||||
)))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[[%s.%s]]", currentTableAlias, inflector.Columnify(prop)), nil, nil
|
||||
}
|
||||
|
||||
field := collection.Schema.GetFieldByName(prop)
|
||||
if field == nil {
|
||||
if nullifyMisingField {
|
||||
return "NULL", nil, nil
|
||||
}
|
||||
|
||||
return "", nil, fmt.Errorf("Unrecognized field %q.", prop)
|
||||
}
|
||||
|
||||
// last prop
|
||||
if i == totalProps-1 {
|
||||
return fmt.Sprintf("[[%s.%s]]", currentTableAlias, inflector.Columnify(prop)), nil, nil
|
||||
}
|
||||
|
||||
// check if it is a json field
|
||||
if field.Type == schema.FieldTypeJson {
|
||||
var jsonPath strings.Builder
|
||||
jsonPath.WriteString("$")
|
||||
for _, p := range props[i+1:] {
|
||||
if _, err := strconv.Atoi(p); err == nil {
|
||||
jsonPath.WriteString("[")
|
||||
jsonPath.WriteString(inflector.Columnify(p))
|
||||
jsonPath.WriteString("]")
|
||||
} else {
|
||||
jsonPath.WriteString(".")
|
||||
jsonPath.WriteString(inflector.Columnify(p))
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"JSON_EXTRACT([[%s.%s]], '%s')",
|
||||
currentTableAlias,
|
||||
inflector.Columnify(prop),
|
||||
jsonPath.String(),
|
||||
), nil, nil
|
||||
}
|
||||
|
||||
// check if it is a relation field
|
||||
if field.Type != schema.FieldTypeRelation {
|
||||
return "", nil, fmt.Errorf("Field %q is not a valid relation.", prop)
|
||||
}
|
||||
|
||||
// auto join the relation
|
||||
// ---
|
||||
field.InitOptions()
|
||||
options, ok := field.Options.(*schema.RelationOptions)
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("Failed to initialize field %q options.", prop)
|
||||
}
|
||||
|
||||
relCollection, relErr := r.loadCollection(options.CollectionId)
|
||||
if relErr != nil {
|
||||
return "", nil, fmt.Errorf("Failed to find field %q collection.", prop)
|
||||
}
|
||||
|
||||
cleanFieldName := inflector.Columnify(field.Name)
|
||||
newCollectionName := relCollection.Name
|
||||
newTableAlias := currentTableAlias + "_" + cleanFieldName
|
||||
|
||||
jeTable := currentTableAlias + "_" + cleanFieldName + "_je"
|
||||
jePair := currentTableAlias + "." + cleanFieldName
|
||||
|
||||
r.registerJoin(
|
||||
fmt.Sprintf(
|
||||
// note: the case is used to normalize value access for single and multiple relations.
|
||||
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
|
||||
jePair, jePair, jePair,
|
||||
),
|
||||
jeTable,
|
||||
nil,
|
||||
)
|
||||
r.registerJoin(
|
||||
inflector.Columnify(newCollectionName),
|
||||
newTableAlias,
|
||||
dbx.NewExp(fmt.Sprintf("[[%s.id]] = [[%s.value]]", newTableAlias, jeTable)),
|
||||
)
|
||||
|
||||
currentCollectionName = newCollectionName
|
||||
currentTableAlias = newTableAlias
|
||||
}
|
||||
|
||||
return "", nil, fmt.Errorf("Failed to resolve field %q.", fieldName)
|
||||
// Example of some resolvable fieldName formats:
|
||||
//
|
||||
// id
|
||||
// someSelect.each
|
||||
// project.screen.status
|
||||
// @request.status
|
||||
// @request.query.filter
|
||||
// @request.auth.someRelation.name
|
||||
// @request.data.someRelation.name
|
||||
// @request.data.someField
|
||||
// @request.data.someSelect:each
|
||||
// @request.data.someField:isset
|
||||
// @collection.product.name
|
||||
func (r *RecordFieldResolver) Resolve(fieldName string) (*search.ResolverResult, error) {
|
||||
return parseAndRun(fieldName, r)
|
||||
}
|
||||
|
||||
func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (resultName string, placeholderParams dbx.Params, err error) {
|
||||
// ignore error because requestData is dynamic and some of the
|
||||
// lookup keys may not be defined for the request
|
||||
resultVal, _ := extractNestedMapVal(r.staticRequestData, path...)
|
||||
func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (*search.ResolverResult, error) {
|
||||
if len(path) == 0 {
|
||||
return nil, fmt.Errorf("at least one path key should be provided")
|
||||
}
|
||||
|
||||
lastProp, modifier, err := splitModifier(path[len(path)-1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
path[len(path)-1] = lastProp
|
||||
|
||||
// extract value
|
||||
resultVal, err := extractNestedMapVal(r.staticRequestData, path...)
|
||||
|
||||
if modifier == issetModifier {
|
||||
if err != nil {
|
||||
return &search.ResolverResult{Identifier: "FALSE"}, nil
|
||||
}
|
||||
return &search.ResolverResult{Identifier: "TRUE"}, nil
|
||||
}
|
||||
|
||||
// note: we are ignoring the error because requestData is dynamic
|
||||
// and some of the lookup keys may not be defined for the request
|
||||
|
||||
switch v := resultVal.(type) {
|
||||
case nil:
|
||||
return "NULL", nil, nil
|
||||
case string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
case string:
|
||||
// check if it is a number field and explicitly try to cast to
|
||||
// float in case of a numeric string value was used
|
||||
// (this usually the case when the data is from a multipart/form-data request)
|
||||
field := r.baseCollection.Schema.GetFieldByName(path[len(path)-1])
|
||||
if field != nil && field.Type == schema.FieldTypeNumber {
|
||||
if nv, err := strconv.ParseFloat(v, 64); err == nil {
|
||||
resultVal = nv
|
||||
}
|
||||
}
|
||||
// otherwise - no further processing is needed...
|
||||
case bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64:
|
||||
// no further processing is needed...
|
||||
default:
|
||||
// non-plain value
|
||||
@@ -367,33 +198,11 @@ func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (resultN
|
||||
}
|
||||
|
||||
placeholder := "f" + security.PseudorandomString(5)
|
||||
name := fmt.Sprintf("{:%s}", placeholder)
|
||||
params := dbx.Params{placeholder: resultVal}
|
||||
|
||||
return name, params, nil
|
||||
}
|
||||
|
||||
func extractNestedMapVal(m map[string]any, keys ...string) (result any, err error) {
|
||||
var ok bool
|
||||
|
||||
if len(keys) == 0 {
|
||||
return nil, fmt.Errorf("At least one key should be provided.")
|
||||
}
|
||||
|
||||
if result, ok = m[keys[0]]; !ok {
|
||||
return nil, fmt.Errorf("Invalid key path - missing key %q.", keys[0])
|
||||
}
|
||||
|
||||
// end key reached
|
||||
if len(keys) == 1 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if m, ok = result.(map[string]any); !ok {
|
||||
return nil, fmt.Errorf("Expected map structure, got %#v.", result)
|
||||
}
|
||||
|
||||
return extractNestedMapVal(m, keys[1:]...)
|
||||
return &search.ResolverResult{
|
||||
Identifier: "{:" + placeholder + "}",
|
||||
Params: dbx.Params{placeholder: resultVal},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *RecordFieldResolver) loadCollection(collectionNameOrId string) (*models.Collection, error) {
|
||||
@@ -415,17 +224,15 @@ func (r *RecordFieldResolver) loadCollection(collectionNameOrId string) (*models
|
||||
}
|
||||
|
||||
func (r *RecordFieldResolver) registerJoin(tableName string, tableAlias string, on dbx.Expression) {
|
||||
tableExpr := (tableName + " " + tableAlias)
|
||||
|
||||
join := join{
|
||||
id: tableAlias,
|
||||
table: tableExpr,
|
||||
on: on,
|
||||
join := &join{
|
||||
tableName: tableName,
|
||||
tableAlias: tableAlias,
|
||||
on: on,
|
||||
}
|
||||
|
||||
// replace existing join
|
||||
for i, j := range r.joins {
|
||||
if j.id == join.id {
|
||||
if j.tableAlias == join.tableAlias {
|
||||
r.joins[i] = join
|
||||
return
|
||||
}
|
||||
@@ -435,6 +242,44 @@ func (r *RecordFieldResolver) registerJoin(tableName string, tableAlias string,
|
||||
r.joins = append(r.joins, join)
|
||||
}
|
||||
|
||||
func (r *RecordFieldResolver) registerExpr(expr dbx.Expression) {
|
||||
r.exprs = append(r.exprs, expr)
|
||||
func extractNestedMapVal(m map[string]any, keys ...string) (any, error) {
|
||||
if len(keys) == 0 {
|
||||
return nil, fmt.Errorf("at least one key should be provided")
|
||||
}
|
||||
|
||||
var result any
|
||||
var ok bool
|
||||
|
||||
if result, ok = m[keys[0]]; !ok {
|
||||
return nil, fmt.Errorf("invalid key path - missing key %q", keys[0])
|
||||
}
|
||||
|
||||
// end key reached
|
||||
if len(keys) == 1 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if m, ok = result.(map[string]any); !ok {
|
||||
return nil, fmt.Errorf("expected map, got %#v", result)
|
||||
}
|
||||
|
||||
return extractNestedMapVal(m, keys[1:]...)
|
||||
}
|
||||
|
||||
func splitModifier(combined string) (string, string, error) {
|
||||
parts := strings.Split(combined, ":")
|
||||
|
||||
if len(parts) != 2 {
|
||||
return combined, "", nil
|
||||
}
|
||||
|
||||
// validate modifier
|
||||
switch parts[1] {
|
||||
case issetModifier,
|
||||
eachModifier,
|
||||
lengthModifier:
|
||||
return parts[0], parts[1], nil
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("unknown modifier in %q", combined)
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
|
||||
test
|
||||
@@ -0,0 +1 @@
|
||||
{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":{"original-filename":"test.txt"},"md5":"2Oj8otwPiW/Xy0ywAxuiSQ=="}
|
||||
@@ -0,0 +1 @@
|
||||
test
|
||||
@@ -0,0 +1 @@
|
||||
{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"text/plain; charset=utf-8","user.metadata":{"original-filename":"test.txt"},"md5":"2Oj8otwPiW/Xy0ywAxuiSQ=="}
|
||||
+12
-7
@@ -9,13 +9,14 @@ import (
|
||||
|
||||
// AuthUser defines a standardized oauth2 user data structure.
|
||||
type AuthUser struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
RawUser map[string]any `json:"rawUser"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
AvatarUrl string `json:"avatarUrl"`
|
||||
RawUser map[string]any `json:"rawUser"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
// Provider defines a common interface for an OAuth2 client.
|
||||
@@ -105,6 +106,10 @@ func NewProviderByName(name string) (Provider, error) {
|
||||
return NewKakaoProvider(), nil
|
||||
case NameTwitch:
|
||||
return NewTwitchProvider(), nil
|
||||
case NameStrava:
|
||||
return NewStravaProvider(), nil
|
||||
case NameGitee:
|
||||
return NewGiteeProvider(), nil
|
||||
default:
|
||||
return nil, errors.New("Missing provider " + name)
|
||||
}
|
||||
|
||||
@@ -108,4 +108,22 @@ func TestNewProviderByName(t *testing.T) {
|
||||
if _, ok := p.(*auth.Twitch); !ok {
|
||||
t.Error("Expected to be instance of *auth.Twitch")
|
||||
}
|
||||
|
||||
// strava
|
||||
p, err = auth.NewProviderByName(auth.NameStrava)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil, got error %v", err)
|
||||
}
|
||||
if _, ok := p.(*auth.Strava); !ok {
|
||||
t.Error("Expected to be instance of *auth.Strava")
|
||||
}
|
||||
|
||||
// gitee
|
||||
p, err = auth.NewProviderByName(auth.NameGitee)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil, got error %v", err)
|
||||
}
|
||||
if _, ok := p.(*auth.Gitee); !ok {
|
||||
t.Error("Expected to be instance of *auth.Gitee")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,12 +63,13 @@ func (p *Discord) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
username := fmt.Sprintf("%s#%s", extracted.Username, extracted.Discriminator)
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Id,
|
||||
Name: username,
|
||||
Username: extracted.Username,
|
||||
AvatarUrl: avatarUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: extracted.Id,
|
||||
Name: username,
|
||||
Username: extracted.Username,
|
||||
AvatarUrl: avatarUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
if extracted.Verified {
|
||||
user.Email = extracted.Email
|
||||
|
||||
@@ -54,12 +54,13 @@ func (p *Facebook) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.Picture.Data.Url,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.Picture.Data.Url,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var _ Provider = (*Gitee)(nil)
|
||||
|
||||
// NameGitee is the unique name of the Gitee provider.
|
||||
const NameGitee string = "gitee"
|
||||
|
||||
// Gitee allows authentication via Gitee OAuth2.
|
||||
type Gitee struct {
|
||||
*baseProvider
|
||||
}
|
||||
|
||||
// NewGiteeProvider creates new Gitee provider instance with some defaults.
|
||||
func NewGiteeProvider() *Gitee {
|
||||
return &Gitee{&baseProvider{
|
||||
scopes: []string{"user_info", "emails"},
|
||||
authUrl: "https://gitee.com/oauth/authorize",
|
||||
tokenUrl: "https://gitee.com/oauth/token",
|
||||
userApiUrl: "https://gitee.com/api/v5/user",
|
||||
}}
|
||||
}
|
||||
|
||||
// FetchAuthUser returns an AuthUser instance based the Gitee's user api.
|
||||
//
|
||||
// API reference: https://gitee.com/api/v5/swagger#/getV5User
|
||||
func (p *Gitee) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
data, err := p.FetchRawUserData(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawUser := map[string]any{}
|
||||
if err := json.Unmarshal(data, &rawUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extracted := struct {
|
||||
Login string `json:"login"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
AvatarUrl string `json:"avatar_url"`
|
||||
}{}
|
||||
if err := json.Unmarshal(data, &extracted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Name: extracted.Name,
|
||||
Username: extracted.Login,
|
||||
AvatarUrl: extracted.AvatarUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
if extracted.Email != "" && is.EmailFormat.Validate(extracted.Email) == nil {
|
||||
// valid public primary email
|
||||
user.Email = extracted.Email
|
||||
} else {
|
||||
// send an additional optional request to retrieve the email
|
||||
email, err := p.fetchPrimaryEmail(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.Email = email
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// fetchPrimaryEmail sends an API request to retrieve the verified primary email,
|
||||
// in case the user hasn't set "Public email address" or has unchecked
|
||||
// the "Access your emails data" permission during authentication.
|
||||
//
|
||||
// NB! This method can succeed and still return an empty email.
|
||||
// Error responses that are result of insufficient scopes permissions are ignored.
|
||||
//
|
||||
// API reference: https://gitee.com/api/v5/swagger#/getV5Emails
|
||||
func (p *Gitee) fetchPrimaryEmail(token *oauth2.Token) (string, error) {
|
||||
client := p.Client(token)
|
||||
|
||||
response, err := client.Get("https://gitee.com/api/v5/emails")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
// ignore common http errors caused by insufficient scope permissions
|
||||
if response.StatusCode == 401 || response.StatusCode == 403 || response.StatusCode == 404 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
emails := []struct {
|
||||
Email string
|
||||
State string
|
||||
Scope []string
|
||||
}{}
|
||||
if err := json.Unmarshal(content, &emails); err != nil {
|
||||
// ignore unmarshal error in case "Keep my email address private"
|
||||
// was set because response.Body will be something like:
|
||||
// {"email":"12285415+test@user.noreply.gitee.com"}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// extract the first verified primary email
|
||||
for _, email := range emails {
|
||||
for _, scope := range email.Scope {
|
||||
if email.State == "confirmed" && scope == "primary" && is.EmailFormat.Validate(email.Email) == nil {
|
||||
return email.Email, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
+59
-36
@@ -55,48 +55,71 @@ func (p *Github) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Name: extracted.Name,
|
||||
Username: extracted.Login,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.AvatarUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Name: extracted.Name,
|
||||
Username: extracted.Login,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.AvatarUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
// in case user set "Keep my email address private",
|
||||
// email should be retrieved via extra API request
|
||||
// in case user has set "Keep my email address private", send an
|
||||
// **optional** API request to retrieve the verified primary email
|
||||
if user.Email == "" {
|
||||
client := p.Client(token)
|
||||
|
||||
response, err := client.Get(p.userApiUrl + "/emails")
|
||||
email, err := p.fetchPrimaryEmail(token)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
content, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
emails := []struct {
|
||||
Email string
|
||||
Verified bool
|
||||
Primary bool
|
||||
}{}
|
||||
if err := json.Unmarshal(content, &emails); err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
// extract the verified primary email
|
||||
for _, email := range emails {
|
||||
if email.Verified && email.Primary {
|
||||
user.Email = email.Email
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
user.Email = email
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// fetchPrimaryEmail sends an API request to retrieve the verified
|
||||
// primary email, in case "Keep my email address private" was set.
|
||||
//
|
||||
// NB! This method can succeed and still return an empty email.
|
||||
// Error responses that are result of insufficient scopes permissions are ignored.
|
||||
//
|
||||
// API reference: https://docs.github.com/en/rest/users/emails?apiVersion=2022-11-28
|
||||
func (p *Github) fetchPrimaryEmail(token *oauth2.Token) (string, error) {
|
||||
client := p.Client(token)
|
||||
|
||||
response, err := client.Get(p.userApiUrl + "/emails")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
// ignore common http errors caused by insufficient scope permissions
|
||||
// (the email field is optional, aka. return the auth user without it)
|
||||
if response.StatusCode == 401 || response.StatusCode == 403 || response.StatusCode == 404 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
emails := []struct {
|
||||
Email string
|
||||
Verified bool
|
||||
Primary bool
|
||||
}{}
|
||||
if err := json.Unmarshal(content, &emails); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// extract the verified primary email
|
||||
for _, email := range emails {
|
||||
if email.Verified && email.Primary {
|
||||
return email.Email, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -53,13 +53,14 @@ func (p *Gitlab) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Name: extracted.Name,
|
||||
Username: extracted.Username,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.AvatarUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Name: extracted.Name,
|
||||
Username: extracted.Username,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.AvatarUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
||||
@@ -52,12 +52,13 @@ func (p *Google) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.Picture,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
Email: extracted.Email,
|
||||
AvatarUrl: extracted.Picture,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
||||
+6
-5
@@ -59,11 +59,12 @@ func (p *Kakao) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Username: extracted.Profile.Nickname,
|
||||
AvatarUrl: extracted.Profile.ImageUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Username: extracted.Profile.Nickname,
|
||||
AvatarUrl: extracted.Profile.ImageUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
if extracted.KakaoAccount.IsEmailValid && extracted.KakaoAccount.IsEmailVerified {
|
||||
user.Email = extracted.KakaoAccount.Email
|
||||
|
||||
@@ -53,11 +53,12 @@ func (p *Microsoft) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
Email: extracted.Email,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
Email: extracted.Email,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
||||
@@ -61,10 +61,11 @@ func (p *Spotify) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: extracted.Id,
|
||||
Name: extracted.Name,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
if len(extracted.Images) > 0 {
|
||||
user.AvatarUrl = extracted.Images[0].Url
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var _ Provider = (*Strava)(nil)
|
||||
|
||||
// NameStrava is the unique name of the Strava provider.
|
||||
const NameStrava string = "strava"
|
||||
|
||||
// Strava allows authentication via Strava OAuth2.
|
||||
type Strava struct {
|
||||
*baseProvider
|
||||
}
|
||||
|
||||
// NewStravaProvider creates new Strava provider instance with some defaults.
|
||||
func NewStravaProvider() *Strava {
|
||||
return &Strava{&baseProvider{
|
||||
scopes: []string{
|
||||
"profile:read_all",
|
||||
},
|
||||
authUrl: "https://www.strava.com/oauth/authorize",
|
||||
tokenUrl: "https://www.strava.com/api/v3/oauth/token",
|
||||
userApiUrl: "https://www.strava.com/api/v3/athlete",
|
||||
}}
|
||||
}
|
||||
|
||||
// FetchAuthUser returns an AuthUser instance based on the Strava's user api.
|
||||
//
|
||||
// API reference: https://developers.strava.com/docs/authentication/
|
||||
func (p *Strava) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
data, err := p.FetchRawUserData(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawUser := map[string]any{}
|
||||
if err := json.Unmarshal(data, &rawUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extracted := struct {
|
||||
Id int `json:"id"`
|
||||
FirstName string `json:"firstname"`
|
||||
LastName string `json:"lastname"`
|
||||
Username string `json:"username"`
|
||||
ProfileImageUrl string `json:"profile"`
|
||||
|
||||
// At the time of writing, Strava OAuth2 doesn't support returning the user email address
|
||||
// Email string `json:"email"`
|
||||
}{}
|
||||
if err := json.Unmarshal(data, &extracted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: strconv.Itoa(extracted.Id),
|
||||
Name: extracted.FirstName + " " + extracted.LastName,
|
||||
Username: extracted.Username,
|
||||
AvatarUrl: extracted.ProfileImageUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -61,13 +61,14 @@ func (p *Twitch) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Data[0].Id,
|
||||
Name: extracted.Data[0].DisplayName,
|
||||
Username: extracted.Data[0].Login,
|
||||
Email: extracted.Data[0].Email,
|
||||
AvatarUrl: extracted.Data[0].ProfileImageUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: extracted.Data[0].Id,
|
||||
Name: extracted.Data[0].DisplayName,
|
||||
Username: extracted.Data[0].Login,
|
||||
Email: extracted.Data[0].Email,
|
||||
AvatarUrl: extracted.Data[0].ProfileImageUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
||||
@@ -63,12 +63,13 @@ func (p *Twitter) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Data.Id,
|
||||
Name: extracted.Data.Name,
|
||||
Username: extracted.Data.Username,
|
||||
AvatarUrl: extracted.Data.ProfileImageUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
Id: extracted.Data.Id,
|
||||
Name: extracted.Data.Name,
|
||||
Username: extracted.Data.Username,
|
||||
AvatarUrl: extracted.Data.ProfileImageUrl,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
@@ -46,6 +48,23 @@ func NewFileFromPath(path string) (*File, error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// NewFileFromBytes creates a new File instance from the provided byte slice.
|
||||
func NewFileFromBytes(b []byte, name string) (*File, error) {
|
||||
size := len(b)
|
||||
if size == 0 {
|
||||
return nil, errors.New("cannot create an empty file")
|
||||
}
|
||||
|
||||
f := &File{}
|
||||
|
||||
f.Reader = &BytesReader{b}
|
||||
f.Size = int64(size)
|
||||
f.OriginalName = name
|
||||
f.Name = normalizeName(f.Reader, f.OriginalName)
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// NewFileFromMultipart creates a new File instace from the provided multipart header.
|
||||
func NewFileFromMultipart(mh *multipart.FileHeader) (*File, error) {
|
||||
f := &File{}
|
||||
@@ -62,7 +81,7 @@ func NewFileFromMultipart(mh *multipart.FileHeader) (*File, error) {
|
||||
|
||||
var _ FileReader = (*MultipartReader)(nil)
|
||||
|
||||
// MultipartReader defines a [multipart.FileHeader] reader.
|
||||
// MultipartReader defines a FileReader from [multipart.FileHeader].
|
||||
type MultipartReader struct {
|
||||
Header *multipart.FileHeader
|
||||
}
|
||||
@@ -76,6 +95,7 @@ func (r *MultipartReader) Open() (io.ReadSeekCloser, error) {
|
||||
|
||||
var _ FileReader = (*PathReader)(nil)
|
||||
|
||||
// PathReader defines a FileReader from a local file path.
|
||||
type PathReader struct {
|
||||
Path string
|
||||
}
|
||||
@@ -87,6 +107,29 @@ func (r *PathReader) Open() (io.ReadSeekCloser, error) {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
var _ FileReader = (*BytesReader)(nil)
|
||||
|
||||
// BytesReader defines a FileReader from bytes content.
|
||||
type BytesReader struct {
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
// Open implements the [filesystem.FileReader] interface.
|
||||
func (r *BytesReader) Open() (io.ReadSeekCloser, error) {
|
||||
return &bytesReadSeekCloser{bytes.NewReader(r.Bytes)}, nil
|
||||
}
|
||||
|
||||
type bytesReadSeekCloser struct {
|
||||
*bytes.Reader
|
||||
}
|
||||
|
||||
// Close implements the [io.ReadSeekCloser] interface.
|
||||
func (r *bytesReadSeekCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
var extInvalidCharsRegex = regexp.MustCompile(`[^\w\.\*\-\+\=\#]+`)
|
||||
|
||||
func normalizeName(fr FileReader, name string) string {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
)
|
||||
|
||||
func TestNewFileFromFromPath(t *testing.T) {
|
||||
func TestNewFileFromPath(t *testing.T) {
|
||||
testDir := createTestDir(t)
|
||||
defer os.RemoveAll(testDir)
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestNewFileFromFromPath(t *testing.T) {
|
||||
t.Fatalf("Expected nil error, got %v", err)
|
||||
}
|
||||
if f.OriginalName != originalName {
|
||||
t.Fatalf("Expected originalName %q, got %q", originalName, f.OriginalName)
|
||||
t.Fatalf("Expected OriginalName %q, got %q", originalName, f.OriginalName)
|
||||
}
|
||||
if match, _ := regexp.Match(normalizedNamePattern, []byte(f.Name)); !match {
|
||||
t.Fatalf("Expected Name to match %v, got %q (%v)", normalizedNamePattern, f.Name, err)
|
||||
@@ -43,6 +43,34 @@ func TestNewFileFromFromPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFileFromBytes(t *testing.T) {
|
||||
// nil bytes
|
||||
if _, err := filesystem.NewFileFromBytes(nil, "photo.jpg"); err == nil {
|
||||
t.Fatal("Expected error, got nil")
|
||||
}
|
||||
|
||||
// zero bytes
|
||||
if _, err := filesystem.NewFileFromBytes([]byte{}, "photo.jpg"); err == nil {
|
||||
t.Fatal("Expected error, got nil")
|
||||
}
|
||||
|
||||
originalName := "image_! noext"
|
||||
normalizedNamePattern := regexp.QuoteMeta("image_noext_") + `\w{10}` + regexp.QuoteMeta(".txt")
|
||||
f, err := filesystem.NewFileFromBytes([]byte("text\n"), originalName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if f.Size != 5 {
|
||||
t.Fatalf("Expected Size %v, got %v", 5, f.Size)
|
||||
}
|
||||
if f.OriginalName != originalName {
|
||||
t.Fatalf("Expected OriginalName %q, got %q", originalName, f.OriginalName)
|
||||
}
|
||||
if match, _ := regexp.Match(normalizedNamePattern, []byte(f.Name)); !match {
|
||||
t.Fatalf("Expected Name to match %v, got %q (%v)", normalizedNamePattern, f.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewFileFromMultipart(t *testing.T) {
|
||||
formData, mp, err := tests.MockMultipartData(nil, "test")
|
||||
if err != nil {
|
||||
|
||||
@@ -142,7 +142,7 @@ func (s *System) UploadFile(file *File, fileKey string) error {
|
||||
opts := &blob.WriterOptions{
|
||||
ContentType: mt.String(),
|
||||
Metadata: map[string]string{
|
||||
"original_filename": originalName,
|
||||
"original-filename": originalName,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ func (s *System) UploadMultipart(fh *multipart.FileHeader, fileKey string) error
|
||||
opts := &blob.WriterOptions{
|
||||
ContentType: mt.String(),
|
||||
Metadata: map[string]string{
|
||||
"original_filename": originalName,
|
||||
"original-filename": originalName,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -225,13 +225,11 @@ func (s *System) DeletePrefix(prefix string) []error {
|
||||
})
|
||||
for {
|
||||
obj, err := iter.Next(s.ctx)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
failed = append(failed, err)
|
||||
continue
|
||||
if err != io.EOF {
|
||||
failed = append(failed, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if err := s.Delete(obj.Key); err != nil {
|
||||
|
||||
@@ -170,15 +170,52 @@ func TestFileSystemUploadMultipart(t *testing.T) {
|
||||
}
|
||||
|
||||
if exists, _ := fs.Exists(fileKey); !exists {
|
||||
t.Fatalf("Expected newdir/newkey.txt to exist")
|
||||
t.Fatalf("Expected %q to exist", fileKey)
|
||||
}
|
||||
|
||||
attrs, err := fs.Attributes(fileKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to fetch file attributes: %v", err)
|
||||
}
|
||||
if name, ok := attrs.Metadata["original_filename"]; !ok || name != "test" {
|
||||
t.Fatalf("Expected original_filename to be %q, got %q", "test", name)
|
||||
if name, ok := attrs.Metadata["original-filename"]; !ok || name != "test" {
|
||||
t.Fatalf("Expected original-filename to be %q, got %q", "test", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileSystemUploadFile(t *testing.T) {
|
||||
dir := createTestDir(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
fs, err := filesystem.NewLocal(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fs.Close()
|
||||
|
||||
fileKey := "newdir/newkey.txt"
|
||||
|
||||
file, err := filesystem.NewFileFromPath(filepath.Join(dir, "image.svg"))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load test file: %v", err)
|
||||
}
|
||||
|
||||
file.OriginalName = "test.txt"
|
||||
|
||||
uploadErr := fs.UploadFile(file, fileKey)
|
||||
if uploadErr != nil {
|
||||
t.Fatal(uploadErr)
|
||||
}
|
||||
|
||||
if exists, _ := fs.Exists(fileKey); !exists {
|
||||
t.Fatalf("Expected %q to exist", fileKey)
|
||||
}
|
||||
|
||||
attrs, err := fs.Attributes(fileKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to fetch file attributes: %v", err)
|
||||
}
|
||||
if name, ok := attrs.Metadata["original-filename"]; !ok || name != file.OriginalName {
|
||||
t.Fatalf("Expected original-filename to be %q, got %q", file.OriginalName, name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,13 +229,15 @@ func TestFileSystemUpload(t *testing.T) {
|
||||
}
|
||||
defer fs.Close()
|
||||
|
||||
uploadErr := fs.Upload([]byte("demo"), "newdir/newkey.txt")
|
||||
fileKey := "newdir/newkey.txt"
|
||||
|
||||
uploadErr := fs.Upload([]byte("demo"), fileKey)
|
||||
if uploadErr != nil {
|
||||
t.Fatal(uploadErr)
|
||||
}
|
||||
|
||||
if exists, _ := fs.Exists("newdir/newkey.txt"); !exists {
|
||||
t.Fatalf("Expected newdir/newkey.txt to exist")
|
||||
if exists, _ := fs.Exists(fileKey); !exists {
|
||||
t.Fatalf("Expected %s to exist", fileKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,20 @@ import (
|
||||
|
||||
var cachedPatterns = map[string]*regexp.Regexp{}
|
||||
|
||||
// SubtractSlice returns a new slice with only the "base" elements
|
||||
// that don't exist in "subtract".
|
||||
func SubtractSlice[T comparable](base []T, subtract []T) []T {
|
||||
var result = make([]T, 0, len(base))
|
||||
|
||||
for _, b := range base {
|
||||
if !ExistInSlice(b, subtract) {
|
||||
result = append(result, b)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// ExistInSlice checks whether a comparable element exists in a slice of the same type.
|
||||
func ExistInSlice[T comparable](item T, list []T) bool {
|
||||
if len(list) == 0 {
|
||||
|
||||
@@ -1,12 +1,111 @@
|
||||
package list_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
func TestSubtractSliceString(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
base []string
|
||||
subtract []string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
[]string{},
|
||||
[]string{},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
[]string{},
|
||||
[]string{"1", "2", "3", "4"},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
[]string{"1", "2", "3", "4"},
|
||||
[]string{},
|
||||
`["1","2","3","4"]`,
|
||||
},
|
||||
{
|
||||
[]string{"1", "2", "3", "4"},
|
||||
[]string{"1", "2", "3", "4"},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
[]string{"1", "2", "3", "4", "7"},
|
||||
[]string{"2", "4", "5", "6"},
|
||||
`["1","3","7"]`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
result := list.SubtractSlice(s.base, s.subtract)
|
||||
|
||||
raw, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Fatalf("(%d) Failed to serialize: %v", i, err)
|
||||
}
|
||||
|
||||
strResult := string(raw)
|
||||
|
||||
if strResult != s.expected {
|
||||
t.Fatalf("(%d) Expected %v, got %v", i, s.expected, strResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubtractSliceInt(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
base []int
|
||||
subtract []int
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
[]int{},
|
||||
[]int{},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
[]int{},
|
||||
[]int{1, 2, 3, 4},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
[]int{1, 2, 3, 4},
|
||||
[]int{},
|
||||
`[1,2,3,4]`,
|
||||
},
|
||||
{
|
||||
[]int{1, 2, 3, 4},
|
||||
[]int{1, 2, 3, 4},
|
||||
`[]`,
|
||||
},
|
||||
{
|
||||
[]int{1, 2, 3, 4, 7},
|
||||
[]int{2, 4, 5, 6},
|
||||
`[1,3,7]`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
result := list.SubtractSlice(s.base, s.subtract)
|
||||
|
||||
raw, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Fatalf("(%d) Failed to serialize: %v", i, err)
|
||||
}
|
||||
|
||||
strResult := string(raw)
|
||||
|
||||
if strResult != s.expected {
|
||||
t.Fatalf("(%d) Expected %v, got %v", i, s.expected, strResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExistInSliceString(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
item string
|
||||
|
||||
+59
-25
@@ -2,6 +2,7 @@ package migrate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
@@ -72,9 +73,19 @@ func (r *Runner) Run(args ...string) error {
|
||||
}
|
||||
}
|
||||
|
||||
names, err := r.lastAppliedMigrations(toRevertCount)
|
||||
if err != nil {
|
||||
color.Red(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
confirm := false
|
||||
prompt := &survey.Confirm{
|
||||
Message: fmt.Sprintf("Do you really want to revert the last %d applied migration(s)?", toRevertCount),
|
||||
Message: fmt.Sprintf(
|
||||
"\n%v\nDo you really want to revert the last %d applied migration(s)?",
|
||||
strings.Join(names, "\n"),
|
||||
toRevertCount,
|
||||
),
|
||||
}
|
||||
survey.AskOne(prompt, &confirm)
|
||||
if !confirm {
|
||||
@@ -138,38 +149,43 @@ func (r *Runner) Up() ([]string, error) {
|
||||
return applied, nil
|
||||
}
|
||||
|
||||
// Down reverts the last `toRevertCount` applied migrations.
|
||||
// Down reverts the last `toRevertCount` applied migrations
|
||||
// (in the order they were applied).
|
||||
//
|
||||
// On success returns list with the reverted migrations file names.
|
||||
func (r *Runner) Down(toRevertCount int) ([]string, error) {
|
||||
reverted := make([]string, 0, toRevertCount)
|
||||
|
||||
names, appliedErr := r.lastAppliedMigrations(toRevertCount)
|
||||
if appliedErr != nil {
|
||||
return nil, appliedErr
|
||||
}
|
||||
|
||||
err := r.db.Transactional(func(tx *dbx.Tx) error {
|
||||
for i := len(r.migrationsList.Items()) - 1; i >= 0; i-- {
|
||||
m := r.migrationsList.Item(i)
|
||||
|
||||
// skip unapplied
|
||||
if !r.isMigrationApplied(tx, m.File) {
|
||||
continue
|
||||
}
|
||||
|
||||
// revert limit reached
|
||||
if toRevertCount-len(reverted) <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// ignore empty Down action
|
||||
if m.Down != nil {
|
||||
if err := m.Down(tx); err != nil {
|
||||
return fmt.Errorf("Failed to revert migration %s: %w", m.File, err)
|
||||
for _, name := range names {
|
||||
for _, m := range r.migrationsList.Items() {
|
||||
if m.File != name {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.saveRevertedMigration(tx, m.File); err != nil {
|
||||
return fmt.Errorf("Failed to save reverted migration info for %s: %w", m.File, err)
|
||||
}
|
||||
// revert limit reached
|
||||
if toRevertCount-len(reverted) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
reverted = append(reverted, m.File)
|
||||
// ignore empty Down action
|
||||
if m.Down != nil {
|
||||
if err := m.Down(tx); err != nil {
|
||||
return fmt.Errorf("Failed to revert migration %s: %w", m.File, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.saveRevertedMigration(tx, m.File); err != nil {
|
||||
return fmt.Errorf("Failed to save reverted migration info for %s: %w", m.File, err)
|
||||
}
|
||||
|
||||
reverted = append(reverted, m.File)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -178,6 +194,7 @@ func (r *Runner) Down(toRevertCount int) ([]string, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reverted, nil
|
||||
}
|
||||
|
||||
@@ -207,7 +224,7 @@ func (r *Runner) isMigrationApplied(tx dbx.Builder, file string) bool {
|
||||
func (r *Runner) saveAppliedMigration(tx dbx.Builder, file string) error {
|
||||
_, err := tx.Insert(r.tableName, dbx.Params{
|
||||
"file": file,
|
||||
"applied": time.Now().Unix(),
|
||||
"applied": time.Now().UnixMicro(),
|
||||
}).Execute()
|
||||
|
||||
return err
|
||||
@@ -218,3 +235,20 @@ func (r *Runner) saveRevertedMigration(tx dbx.Builder, file string) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Runner) lastAppliedMigrations(limit int) ([]string, error) {
|
||||
var files = make([]string, 0, limit)
|
||||
|
||||
err := r.db.Select("file").
|
||||
From(r.tableName).
|
||||
Where(dbx.Not(dbx.HashExp{"applied": nil})).
|
||||
OrderBy("applied DESC", "file DESC").
|
||||
Limit(int64(limit)).
|
||||
Column(&files)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package migrate
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -52,73 +53,88 @@ func TestRunnerUpAndDown(t *testing.T) {
|
||||
}
|
||||
defer testDB.Close()
|
||||
|
||||
var test1UpCalled bool
|
||||
var test1DownCalled bool
|
||||
var test2UpCalled bool
|
||||
var test2DownCalled bool
|
||||
callsOrder := []string{}
|
||||
|
||||
l := MigrationsList{}
|
||||
l.Register(func(db dbx.Builder) error {
|
||||
test1UpCalled = true
|
||||
callsOrder = append(callsOrder, "up2")
|
||||
return nil
|
||||
}, func(db dbx.Builder) error {
|
||||
test1DownCalled = true
|
||||
return nil
|
||||
}, "1_test")
|
||||
l.Register(func(db dbx.Builder) error {
|
||||
test2UpCalled = true
|
||||
return nil
|
||||
}, func(db dbx.Builder) error {
|
||||
test2DownCalled = true
|
||||
callsOrder = append(callsOrder, "down2")
|
||||
return nil
|
||||
}, "2_test")
|
||||
l.Register(func(db dbx.Builder) error {
|
||||
callsOrder = append(callsOrder, "up3")
|
||||
return nil
|
||||
}, func(db dbx.Builder) error {
|
||||
callsOrder = append(callsOrder, "down3")
|
||||
return nil
|
||||
}, "3_test")
|
||||
l.Register(func(db dbx.Builder) error {
|
||||
callsOrder = append(callsOrder, "up1")
|
||||
return nil
|
||||
}, func(db dbx.Builder) error {
|
||||
callsOrder = append(callsOrder, "down1")
|
||||
return nil
|
||||
}, "1_test")
|
||||
|
||||
r, err := NewRunner(testDB.DB, l)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// simulate partially run migration
|
||||
r.saveAppliedMigration(testDB, r.migrationsList.Item(0).File)
|
||||
// simulate partially out-of-order run migration
|
||||
r.saveAppliedMigration(testDB, "2_test")
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Up()
|
||||
// ---
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
if _, err := r.Up(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test1UpCalled {
|
||||
t.Fatalf("Didn't expect 1_test to be called")
|
||||
}
|
||||
expectedUpCallsOrder := `["up1","up3"]` // skip up2 since it was applied previously
|
||||
|
||||
if !test2UpCalled {
|
||||
t.Fatalf("Expected 2_test to be called")
|
||||
}
|
||||
|
||||
// simulate unrun migration
|
||||
var test3DownCalled bool
|
||||
r.migrationsList.Register(nil, func(db dbx.Builder) error {
|
||||
test3DownCalled = true
|
||||
return nil
|
||||
}, "3_test")
|
||||
|
||||
// Down()
|
||||
// ---
|
||||
// revert one migration
|
||||
if _, err := r.Down(1); err != nil {
|
||||
upCallsOrder, err := json.Marshal(callsOrder)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test3DownCalled {
|
||||
t.Fatal("Didn't expect 3_test to be reverted.")
|
||||
if v := string(upCallsOrder); v != expectedUpCallsOrder {
|
||||
t.Fatalf("Expected Up() calls order %s, got %s", expectedUpCallsOrder, upCallsOrder)
|
||||
}
|
||||
|
||||
if !test2DownCalled {
|
||||
t.Fatal("Expected 2_test to be reverted.")
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// reset callsOrder
|
||||
callsOrder = []string{}
|
||||
|
||||
// simulate unrun migration
|
||||
r.migrationsList.Register(nil, func(db dbx.Builder) error {
|
||||
callsOrder = append(callsOrder, "down4")
|
||||
return nil
|
||||
}, "4_test")
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Down()
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
if _, err := r.Down(2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if test1DownCalled {
|
||||
t.Fatal("Didn't expect 1_test to be reverted.")
|
||||
expectedDownCallsOrder := `["down3","down1"]` // revert in the applied order
|
||||
|
||||
downCallsOrder, err := json.Marshal(callsOrder)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if v := string(downCallsOrder); v != expectedDownCallsOrder {
|
||||
t.Fatalf("Expected Down() calls order %s, got %s", expectedDownCallsOrder, downCallsOrder)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+275
-66
@@ -85,72 +85,119 @@ func (f FilterData) build(data []fexpr.ExprGroup, fieldResolver FieldResolver) (
|
||||
}
|
||||
|
||||
func (f FilterData) resolveTokenizedExpr(expr fexpr.Expr, fieldResolver FieldResolver) (dbx.Expression, error) {
|
||||
lName, lParams, lErr := f.resolveToken(expr.Left, fieldResolver)
|
||||
if lName == "" || lErr != nil {
|
||||
return nil, fmt.Errorf("Invalid left operand %q - %v.", expr.Left.Literal, lErr)
|
||||
lResult, lErr := resolveToken(expr.Left, fieldResolver)
|
||||
if lErr != nil || lResult.Identifier == "" {
|
||||
return nil, fmt.Errorf("invalid left operand %q - %v", expr.Left.Literal, lErr)
|
||||
}
|
||||
|
||||
rName, rParams, rErr := f.resolveToken(expr.Right, fieldResolver)
|
||||
if rName == "" || rErr != nil {
|
||||
return nil, fmt.Errorf("Invalid right operand %q - %v.", expr.Right.Literal, rErr)
|
||||
rResult, rErr := resolveToken(expr.Right, fieldResolver)
|
||||
if rErr != nil || rResult.Identifier == "" {
|
||||
return nil, fmt.Errorf("invalid right operand %q - %v", expr.Right.Literal, rErr)
|
||||
}
|
||||
|
||||
switch expr.Op {
|
||||
case fexpr.SignEq:
|
||||
return dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') = COALESCE(%s, '')", lName, rName), mergeParams(lParams, rParams)), nil
|
||||
case fexpr.SignNeq:
|
||||
return dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') != COALESCE(%s, '')", lName, rName), mergeParams(lParams, rParams)), nil
|
||||
case fexpr.SignLike:
|
||||
// the right side is a column and therefor wrap it with "%" for contains like behavior
|
||||
if len(rParams) == 0 {
|
||||
return dbx.NewExp(fmt.Sprintf("%s LIKE ('%%' || %s || '%%') ESCAPE '\\'", lName, rName), lParams), nil
|
||||
}
|
||||
|
||||
return dbx.NewExp(fmt.Sprintf("%s LIKE %s ESCAPE '\\'", lName, rName), mergeParams(lParams, wrapLikeParams(rParams))), nil
|
||||
case fexpr.SignNlike:
|
||||
// the right side is a column and therefor wrap it with "%" for not-contains like behavior
|
||||
if len(rParams) == 0 {
|
||||
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE ('%%' || %s || '%%') ESCAPE '\\'", lName, rName), lParams), nil
|
||||
}
|
||||
|
||||
// normalize operands and switch sides if the left operand is a number/text, but the right one is a column
|
||||
// (usually this shouldn't be needed, but it's kept for backward compatibility)
|
||||
if len(lParams) > 0 && len(rParams) == 0 {
|
||||
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s ESCAPE '\\'", rName, lName), wrapLikeParams(lParams)), nil
|
||||
}
|
||||
|
||||
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s ESCAPE '\\'", lName, rName), mergeParams(lParams, wrapLikeParams(rParams))), nil
|
||||
case fexpr.SignLt:
|
||||
return dbx.NewExp(fmt.Sprintf("%s < %s", lName, rName), mergeParams(lParams, rParams)), nil
|
||||
case fexpr.SignLte:
|
||||
return dbx.NewExp(fmt.Sprintf("%s <= %s", lName, rName), mergeParams(lParams, rParams)), nil
|
||||
case fexpr.SignGt:
|
||||
return dbx.NewExp(fmt.Sprintf("%s > %s", lName, rName), mergeParams(lParams, rParams)), nil
|
||||
case fexpr.SignGte:
|
||||
return dbx.NewExp(fmt.Sprintf("%s >= %s", lName, rName), mergeParams(lParams, rParams)), nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Unknown expression operator %q", expr.Op)
|
||||
return buildExpr(lResult, expr.Op, rResult)
|
||||
}
|
||||
|
||||
func (f FilterData) resolveToken(token fexpr.Token, fieldResolver FieldResolver) (name string, params dbx.Params, err error) {
|
||||
func buildExpr(
|
||||
left *ResolverResult,
|
||||
op fexpr.SignOp,
|
||||
right *ResolverResult,
|
||||
) (dbx.Expression, error) {
|
||||
var expr dbx.Expression
|
||||
|
||||
switch op {
|
||||
case fexpr.SignEq, fexpr.SignAnyEq:
|
||||
expr = dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') = COALESCE(%s, '')", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
|
||||
case fexpr.SignNeq, fexpr.SignAnyNeq:
|
||||
expr = dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') != COALESCE(%s, '')", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
|
||||
case fexpr.SignLike, fexpr.SignAnyLike:
|
||||
// the right side is a column and therefor wrap it with "%" for contains like behavior
|
||||
if len(right.Params) == 0 {
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s LIKE ('%%' || %s || '%%') ESCAPE '\\'", left.Identifier, right.Identifier), left.Params)
|
||||
} else {
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s LIKE %s ESCAPE '\\'", left.Identifier, right.Identifier), mergeParams(left.Params, wrapLikeParams(right.Params)))
|
||||
}
|
||||
case fexpr.SignNlike, fexpr.SignAnyNlike:
|
||||
// the right side is a column and therefor wrap it with "%" for not-contains like behavior
|
||||
if len(right.Params) == 0 {
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s NOT LIKE ('%%' || %s || '%%') ESCAPE '\\'", left.Identifier, right.Identifier), left.Params)
|
||||
} else {
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s ESCAPE '\\'", left.Identifier, right.Identifier), mergeParams(left.Params, wrapLikeParams(right.Params)))
|
||||
}
|
||||
case fexpr.SignLt, fexpr.SignAnyLt:
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s < %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
|
||||
case fexpr.SignLte, fexpr.SignAnyLte:
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s <= %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
|
||||
case fexpr.SignGt, fexpr.SignAnyGt:
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s > %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
|
||||
case fexpr.SignGte, fexpr.SignAnyGte:
|
||||
expr = dbx.NewExp(fmt.Sprintf("%s >= %s", left.Identifier, right.Identifier), mergeParams(left.Params, right.Params))
|
||||
}
|
||||
|
||||
if expr == nil {
|
||||
return nil, fmt.Errorf("unknown expression operator %q", op)
|
||||
}
|
||||
|
||||
// multi-match expressions
|
||||
if !isAnyMatchOp(op) {
|
||||
if left.MultiMatchSubQuery != nil && right.MultiMatchSubQuery != nil {
|
||||
mm := &manyVsManyExpr{
|
||||
leftSubQuery: left.MultiMatchSubQuery,
|
||||
rightSubQuery: right.MultiMatchSubQuery,
|
||||
op: op,
|
||||
}
|
||||
|
||||
expr = dbx.And(expr, mm)
|
||||
} else if left.MultiMatchSubQuery != nil {
|
||||
mm := &manyVsOneExpr{
|
||||
subQuery: left.MultiMatchSubQuery,
|
||||
op: op,
|
||||
otherOperand: right,
|
||||
}
|
||||
|
||||
expr = dbx.And(expr, mm)
|
||||
} else if right.MultiMatchSubQuery != nil {
|
||||
mm := &manyVsOneExpr{
|
||||
subQuery: right.MultiMatchSubQuery,
|
||||
op: op,
|
||||
otherOperand: left,
|
||||
inverse: true,
|
||||
}
|
||||
|
||||
expr = dbx.And(expr, mm)
|
||||
}
|
||||
}
|
||||
|
||||
if left.AfterBuild != nil {
|
||||
expr = left.AfterBuild(expr)
|
||||
}
|
||||
|
||||
if right.AfterBuild != nil {
|
||||
expr = right.AfterBuild(expr)
|
||||
}
|
||||
|
||||
return expr, nil
|
||||
}
|
||||
|
||||
func resolveToken(token fexpr.Token, fieldResolver FieldResolver) (*ResolverResult, error) {
|
||||
switch token.Type {
|
||||
case fexpr.TokenIdentifier:
|
||||
// current datetime constant
|
||||
// ---
|
||||
if token.Literal == "@now" {
|
||||
placeholder := "t" + security.PseudorandomString(8)
|
||||
name := fmt.Sprintf("{:%s}", placeholder)
|
||||
params := dbx.Params{placeholder: types.NowDateTime().String()}
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
|
||||
return name, params, nil
|
||||
return &ResolverResult{
|
||||
Identifier: "{:" + placeholder + "}",
|
||||
Params: dbx.Params{placeholder: types.NowDateTime().String()},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// custom resolver
|
||||
// ---
|
||||
name, params, err := fieldResolver.Resolve(token.Literal)
|
||||
result, err := fieldResolver.Resolve(token.Literal)
|
||||
|
||||
if name == "" || err != nil {
|
||||
if err != nil || result.Identifier == "" {
|
||||
m := map[string]string{
|
||||
// if `null` field is missing, treat `null` identifier as NULL token
|
||||
"null": "NULL",
|
||||
@@ -160,27 +207,46 @@ func (f FilterData) resolveToken(token fexpr.Token, fieldResolver FieldResolver)
|
||||
"false": "0",
|
||||
}
|
||||
if v, ok := m[strings.ToLower(token.Literal)]; ok {
|
||||
return v, nil, nil
|
||||
return &ResolverResult{Identifier: v}, nil
|
||||
}
|
||||
return "", nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return name, params, err
|
||||
return result, err
|
||||
case fexpr.TokenText:
|
||||
placeholder := "t" + security.PseudorandomString(8)
|
||||
name := fmt.Sprintf("{:%s}", placeholder)
|
||||
params := dbx.Params{placeholder: token.Literal}
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
|
||||
return name, params, nil
|
||||
return &ResolverResult{
|
||||
Identifier: "{:" + placeholder + "}",
|
||||
Params: dbx.Params{placeholder: token.Literal},
|
||||
}, nil
|
||||
case fexpr.TokenNumber:
|
||||
placeholder := "t" + security.PseudorandomString(8)
|
||||
name := fmt.Sprintf("{:%s}", placeholder)
|
||||
params := dbx.Params{placeholder: cast.ToFloat64(token.Literal)}
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
|
||||
return name, params, nil
|
||||
return &ResolverResult{
|
||||
Identifier: "{:" + placeholder + "}",
|
||||
Params: dbx.Params{placeholder: cast.ToFloat64(token.Literal)},
|
||||
}, nil
|
||||
}
|
||||
|
||||
return "", nil, errors.New("Unresolvable token type.")
|
||||
return nil, errors.New("unresolvable token type")
|
||||
}
|
||||
|
||||
func isAnyMatchOp(op fexpr.SignOp) bool {
|
||||
switch op {
|
||||
case
|
||||
fexpr.SignAnyEq,
|
||||
fexpr.SignAnyNeq,
|
||||
fexpr.SignAnyLike,
|
||||
fexpr.SignAnyNlike,
|
||||
fexpr.SignAnyLt,
|
||||
fexpr.SignAnyLte,
|
||||
fexpr.SignAnyGt,
|
||||
fexpr.SignAnyGte:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// mergeParams returns new dbx.Params where each provided params item
|
||||
@@ -218,18 +284,24 @@ func wrapLikeParams(params dbx.Params) dbx.Params {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
var _ dbx.Expression = (*opExpr)(nil)
|
||||
|
||||
// opExpr defines an expression that contains a raw sql operator string.
|
||||
type opExpr struct {
|
||||
op string
|
||||
}
|
||||
|
||||
// Build converts an expression into a SQL fragment.
|
||||
// Build converts the expression into a SQL fragment.
|
||||
//
|
||||
// Implements [dbx.Expression] interface.
|
||||
func (e *opExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
return e.op
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
var _ dbx.Expression = (*concatExpr)(nil)
|
||||
|
||||
// concatExpr defines an expression that concatenates multiple
|
||||
// other expressions with a specified separator.
|
||||
type concatExpr struct {
|
||||
@@ -237,7 +309,7 @@ type concatExpr struct {
|
||||
separator string
|
||||
}
|
||||
|
||||
// Build converts an expression into a SQL fragment.
|
||||
// Build converts the expression into a SQL fragment.
|
||||
//
|
||||
// Implements [dbx.Expression] interface.
|
||||
func (e *concatExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
@@ -247,12 +319,12 @@ func (e *concatExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
|
||||
stringParts := make([]string, 0, len(e.parts))
|
||||
|
||||
for _, a := range e.parts {
|
||||
if a == nil {
|
||||
for _, p := range e.parts {
|
||||
if p == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if sql := a.Build(db, params); sql != "" {
|
||||
if sql := p.Build(db, params); sql != "" {
|
||||
stringParts = append(stringParts, sql)
|
||||
}
|
||||
}
|
||||
@@ -267,3 +339,140 @@ func (e *concatExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
|
||||
return "(" + strings.Join(stringParts, e.separator) + ")"
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
var _ dbx.Expression = (*manyVsManyExpr)(nil)
|
||||
|
||||
// manyVsManyExpr constructs a multi-match many<->many db where expression.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return "0=1"
|
||||
}
|
||||
|
||||
lAlias := "__ml" + security.PseudorandomString(5)
|
||||
rAlias := "__mr" + security.PseudorandomString(5)
|
||||
|
||||
whereExpr, buildErr := buildExpr(
|
||||
&ResolverResult{
|
||||
Identifier: "[[" + lAlias + ".multiMatchValue]]",
|
||||
},
|
||||
e.op,
|
||||
&ResolverResult{
|
||||
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
|
||||
AfterBuild: multiMatchAfterBuildFunc(e.op, lAlias, rAlias),
|
||||
},
|
||||
)
|
||||
|
||||
if buildErr != nil {
|
||||
return "0=1"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"NOT EXISTS (SELECT 1 FROM (%s) {{%s}} LEFT JOIN (%s) {{%s}} WHERE %s)",
|
||||
e.leftSubQuery.Build(db, params),
|
||||
lAlias,
|
||||
e.rightSubQuery.Build(db, params),
|
||||
rAlias,
|
||||
whereExpr.Build(db, params),
|
||||
)
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
var _ dbx.Expression = (*manyVsOneExpr)(nil)
|
||||
|
||||
// manyVsManyExpr constructs a multi-match many<->one db where expression.
|
||||
//
|
||||
// Expects subQuery to return a subquery with a single "multiMatchValue" column.
|
||||
//
|
||||
// You can set inverse=false to reverse the condition sides (aka. one<->many).
|
||||
type manyVsOneExpr struct {
|
||||
subQuery dbx.Expression
|
||||
op fexpr.SignOp
|
||||
otherOperand *ResolverResult
|
||||
inverse bool
|
||||
}
|
||||
|
||||
// Build converts the expression into a SQL fragment.
|
||||
//
|
||||
// Implements [dbx.Expression] interface.
|
||||
func (e *manyVsOneExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
if e.subQuery == nil {
|
||||
return "0=1"
|
||||
}
|
||||
|
||||
alias := "__sm" + security.PseudorandomString(5)
|
||||
|
||||
r1 := &ResolverResult{
|
||||
Identifier: "[[" + alias + ".multiMatchValue]]",
|
||||
AfterBuild: multiMatchAfterBuildFunc(e.op, alias),
|
||||
}
|
||||
|
||||
r2 := &ResolverResult{
|
||||
Identifier: e.otherOperand.Identifier,
|
||||
Params: e.otherOperand.Params,
|
||||
}
|
||||
|
||||
var whereExpr dbx.Expression
|
||||
var buildErr error
|
||||
|
||||
if e.inverse {
|
||||
whereExpr, buildErr = buildExpr(r2, e.op, r1)
|
||||
} else {
|
||||
whereExpr, buildErr = buildExpr(r1, e.op, r2)
|
||||
}
|
||||
|
||||
if buildErr != nil {
|
||||
return "0=1"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"NOT EXISTS (SELECT 1 FROM (%s) {{%s}} WHERE %s)",
|
||||
e.subQuery.Build(db, params),
|
||||
alias,
|
||||
whereExpr.Build(db, params),
|
||||
)
|
||||
}
|
||||
|
||||
func multiMatchAfterBuildFunc(op fexpr.SignOp, multiMatchAliases ...string) func(dbx.Expression) dbx.Expression {
|
||||
return func(expr dbx.Expression) dbx.Expression {
|
||||
expr = dbx.Not(expr) // inverse for the not-exist expression
|
||||
|
||||
if op == fexpr.SignEq {
|
||||
return expr
|
||||
}
|
||||
|
||||
orExprs := make([]dbx.Expression, len(multiMatchAliases)+1)
|
||||
orExprs[0] = expr
|
||||
|
||||
// Add an optional "IS NULL" condition(s) to handle the empty rows result.
|
||||
//
|
||||
// For example, let's assume that some "rel" field is [nonemptyRel1, nonemptyRel2, emptyRel3],
|
||||
// The filter "rel.total > 0" will ensures that the above will return true only if all relations
|
||||
// are existing and match the condition.
|
||||
//
|
||||
// The "=" operator is excluded because it will never equal directly with NULL anyway
|
||||
// and also because we want in case "rel.id = ''" is specified to allow
|
||||
// matching the empty relations (they will match due to the applied COALESCE).
|
||||
for i, mAlias := range multiMatchAliases {
|
||||
orExprs[i+1] = dbx.NewExp("[[" + mAlias + ".multiMatchValue]] IS NULL")
|
||||
}
|
||||
|
||||
return dbx.Or(orExprs...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,12 +495,12 @@ func (t *testFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *testFieldResolver) Resolve(field string) (name string, placeholderParams dbx.Params, err error) {
|
||||
func (t *testFieldResolver) Resolve(field string) (*ResolverResult, error) {
|
||||
t.ResolveCalls++
|
||||
|
||||
if field == "unknown" {
|
||||
return "", nil, errors.New("test error")
|
||||
return nil, errors.New("test error")
|
||||
}
|
||||
|
||||
return field, nil, nil
|
||||
return &ResolverResult{Identifier: field}, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,25 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
)
|
||||
|
||||
// ResolverResult defines a single FieldResolver.Resolve() successfully parsed result.
|
||||
type ResolverResult struct {
|
||||
// Identifier is the plain SQL identifier/column that will be used
|
||||
// in the final db expression as left or right operand.
|
||||
Identifier string
|
||||
|
||||
// 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
|
||||
|
||||
// MultiMatchSubQuery is an optional sub query expression that will be added
|
||||
// in addition to the combined ResolverResult expression during build.
|
||||
MultiMatchSubQuery dbx.Expression
|
||||
|
||||
// AfterBuild is an optional function that will be called after building
|
||||
// and combining the result of both resolved operands/sides in a single expression.
|
||||
AfterBuild func(expr dbx.Expression) dbx.Expression
|
||||
}
|
||||
|
||||
// FieldResolver defines an interface for managing search fields.
|
||||
type FieldResolver interface {
|
||||
// UpdateQuery allows to updated the provided db query based on the
|
||||
@@ -18,7 +37,7 @@ type FieldResolver interface {
|
||||
|
||||
// Resolve parses the provided field and returns a properly
|
||||
// formatted db identifier (eg. NULL, quoted column, placeholder parameter, etc.).
|
||||
Resolve(field string) (name string, placeholderParams dbx.Params, err error)
|
||||
Resolve(field string) (*ResolverResult, error)
|
||||
}
|
||||
|
||||
// NewSimpleFieldResolver creates a new `SimpleFieldResolver` with the
|
||||
@@ -49,10 +68,12 @@ func (r *SimpleFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
|
||||
// Resolve implements `search.Resolve` interface.
|
||||
//
|
||||
// Returns error if `field` is not in `r.allowedFields`.
|
||||
func (r *SimpleFieldResolver) Resolve(field string) (resultName string, placeholderParams dbx.Params, err error) {
|
||||
func (r *SimpleFieldResolver) Resolve(field string) (*ResolverResult, error) {
|
||||
if !list.ExistInSliceWithRegex(field, r.allowedFields) {
|
||||
return "", nil, fmt.Errorf("Failed to resolve field %q.", field)
|
||||
return nil, fmt.Errorf("Failed to resolve field %q.", field)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[[%s]]", inflector.Columnify(field)), nil, nil
|
||||
return &ResolverResult{
|
||||
Identifier: "[[" + inflector.Columnify(field) + "]]",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func TestSimpleFieldResolverResolve(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
name, params, err := r.Resolve(s.fieldName)
|
||||
r, err := r.Resolve(s.fieldName)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
@@ -69,13 +69,17 @@ func TestSimpleFieldResolverResolve(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
if name != s.expectName {
|
||||
t.Errorf("(%d) Expected name %q, got %q", i, s.expectName, name)
|
||||
if hasErr {
|
||||
continue
|
||||
}
|
||||
|
||||
if r.Identifier != s.expectName {
|
||||
t.Errorf("(%d) Expected r.Identifier %q, got %q", i, s.expectName, r.Identifier)
|
||||
}
|
||||
|
||||
// params should be empty
|
||||
if len(params) != 0 {
|
||||
t.Errorf("(%d) Expected 0 params, got %v", i, params)
|
||||
if len(r.Params) != 0 {
|
||||
t.Errorf("(%d) Expected 0 r.Params, got %v", i, r.Params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-6
@@ -5,6 +5,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const randomSortKey string = "@random"
|
||||
|
||||
// sort field directions
|
||||
const (
|
||||
SortAsc string = "ASC"
|
||||
@@ -19,14 +21,19 @@ type SortField struct {
|
||||
|
||||
// BuildExpr resolves the sort field into a valid db sort expression.
|
||||
func (s *SortField) BuildExpr(fieldResolver FieldResolver) (string, error) {
|
||||
name, params, err := fieldResolver.Resolve(s.Name)
|
||||
|
||||
// invalidate empty fields and non-column identifiers
|
||||
if err != nil || len(params) > 0 || name == "" || strings.ToLower(name) == "null" {
|
||||
return "", fmt.Errorf("Invalid sort field %q.", s.Name)
|
||||
// special case for random sort
|
||||
if s.Name == randomSortKey {
|
||||
return "RANDOM()", nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s", name, s.Direction), nil
|
||||
result, err := fieldResolver.Resolve(s.Name)
|
||||
|
||||
// invalidate empty fields and non-column identifiers
|
||||
if err != nil || len(result.Params) > 0 || result.Identifier == "" || strings.ToLower(result.Identifier) == "null" {
|
||||
return "", fmt.Errorf("invalid sort field %q", s.Name)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s", result.Identifier, s.Direction), nil
|
||||
}
|
||||
|
||||
// ParseSortFromString parses the provided string expression
|
||||
|
||||
@@ -27,6 +27,8 @@ func TestSortFieldBuildExpr(t *testing.T) {
|
||||
{search.SortField{"test1", search.SortAsc}, false, "[[test1]] ASC"},
|
||||
// allowed field - desc
|
||||
{search.SortField{"test1", search.SortDesc}, false, "[[test1]] DESC"},
|
||||
// special @random field (ignore direction)
|
||||
{search.SortField{"@random", search.SortDesc}, false, "RANDOM()"},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
@@ -54,6 +56,7 @@ func TestParseSortFromString(t *testing.T) {
|
||||
{"+test", `[{"name":"test","direction":"ASC"}]`},
|
||||
{"-test", `[{"name":"test","direction":"DESC"}]`},
|
||||
{"test1,-test2,+test3", `[{"name":"test1","direction":"ASC"},{"name":"test2","direction":"DESC"},{"name":"test3","direction":"ASC"}]`},
|
||||
{"@random,-test", `[{"name":"@random","direction":"ASC"},{"name":"test","direction":"DESC"}]`},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
|
||||
@@ -4,7 +4,8 @@ PB_INSTALLER_PARAM = "installer"
|
||||
PB_OAUTH2_EXAMPLE = "https://pocketbase.io/docs/authentication/#web-oauth2-integration"
|
||||
PB_RULES_SYNTAX_DOCS = "https://pocketbase.io/docs/api-rules-and-filters/"
|
||||
PB_FILE_UPLOAD_DOCS = "https://pocketbase.io/docs/files-handling/"
|
||||
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.10.2"
|
||||
PB_VERSION = "v0.11.2"
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import{S as ke,i as be,s as ge,e as r,w as b,b as g,c as _e,f as k,g as h,h as n,m as me,x as G,N as re,O as we,k as ve,P as Ce,n as Pe,t as L,a as Y,o as _,d as pe,Q as Me,C as Se,p as $e,r as H,u as je,M as Ae}from"./index.2a48c906.js";import{S as Be}from"./SdkTabs.9dbb2ca3.js";function ue(a,l,o){const s=a.slice();return s[5]=l[o],s}function de(a,l,o){const s=a.slice();return s[5]=l[o],s}function fe(a,l){let o,s=l[5].code+"",m,f,i,u;function d(){return l[4](l[5])}return{key:a,first:null,c(){o=r("button"),m=b(s),f=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(v,C){h(v,o,C),n(o,m),n(o,f),i||(u=je(o,"click",d),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&G(m,s),C&6&&H(o,"active",l[1]===l[5].code)},d(v){v&&_(o),i=!1,u()}}}function he(a,l){let o,s,m,f;return s=new Ae({props:{content:l[5].body}}),{key:a,first:null,c(){o=r("div"),_e(s.$$.fragment),m=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(i,u){h(i,o,u),me(s,o,null),n(o,m),f=!0},p(i,u){l=i;const d={};u&4&&(d.content=l[5].body),s.$set(d),(!f||u&6)&&H(o,"active",l[1]===l[5].code)},i(i){f||(L(s.$$.fragment,i),f=!0)},o(i){Y(s.$$.fragment,i),f=!1},d(i){i&&_(o),pe(s)}}}function Oe(a){var ae,ne;let l,o,s=a[0].name+"",m,f,i,u,d,v,C,F=a[0].name+"",U,X,q,P,D,j,W,M,K,R,Q,A,Z,V,y=a[0].name+"",I,x,E,B,J,S,O,w=[],ee=new Map,te,T,p=[],le=new Map,$;P=new Be({props:{js:`
|
||||
import{S as ke,i as be,s as ge,e as r,w as b,b as g,c as _e,f as k,g as h,h as n,m as me,x as G,O as re,P as we,k as ve,Q as Ce,n as Pe,t as L,a as Y,o as _,d as pe,R as Me,C as Se,p as $e,r as H,u as je,N as Ae}from"./index.536fddcb.js";import{S as Be}from"./SdkTabs.3571c26b.js";function ue(a,l,o){const s=a.slice();return s[5]=l[o],s}function de(a,l,o){const s=a.slice();return s[5]=l[o],s}function fe(a,l){let o,s=l[5].code+"",m,f,i,u;function d(){return l[4](l[5])}return{key:a,first:null,c(){o=r("button"),m=b(s),f=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(v,C){h(v,o,C),n(o,m),n(o,f),i||(u=je(o,"click",d),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&G(m,s),C&6&&H(o,"active",l[1]===l[5].code)},d(v){v&&_(o),i=!1,u()}}}function he(a,l){let o,s,m,f;return s=new Ae({props:{content:l[5].body}}),{key:a,first:null,c(){o=r("div"),_e(s.$$.fragment),m=g(),k(o,"class","tab-item"),H(o,"active",l[1]===l[5].code),this.first=o},m(i,u){h(i,o,u),me(s,o,null),n(o,m),f=!0},p(i,u){l=i;const d={};u&4&&(d.content=l[5].body),s.$set(d),(!f||u&6)&&H(o,"active",l[1]===l[5].code)},i(i){f||(L(s.$$.fragment,i),f=!0)},o(i){Y(s.$$.fragment,i),f=!1},d(i){i&&_(o),pe(s)}}}function Oe(a){var ae,ne;let l,o,s=a[0].name+"",m,f,i,u,d,v,C,F=a[0].name+"",U,R,q,P,D,j,W,M,K,X,Q,A,Z,V,y=a[0].name+"",I,x,E,B,J,S,O,w=[],ee=new Map,te,T,p=[],le=new Map,$;P=new Be({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[3]}');
|
||||
@@ -14,7 +14,7 @@ import{S as ke,i as be,s as ge,e as r,w as b,b as g,c as _e,f as k,g as h,h as n
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(ne=a[0])==null?void 0:ne.name}').listAuthMethods();
|
||||
`}});let z=a[2];const oe=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=de(a,z,e),c=oe(t);ee.set(c,w[e]=fe(c,t))}let N=a[2];const se=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=ue(a,N,e),c=se(t);le.set(c,p[e]=he(c,t))}return{c(){l=r("h3"),o=b("List auth methods ("),m=b(s),f=b(")"),i=g(),u=r("div"),d=r("p"),v=b("Returns a public list with all allowed "),C=r("strong"),U=b(F),X=b(" authentication methods."),q=g(),_e(P.$$.fragment),D=g(),j=r("h6"),j.textContent="API details",W=g(),M=r("div"),K=r("strong"),K.textContent="GET",R=g(),Q=r("div"),A=r("p"),Z=b("/api/collections/"),V=r("strong"),I=b(y),x=b("/auth-methods"),E=g(),B=r("div"),B.textContent="Responses",J=g(),S=r("div"),O=r("div");for(let e=0;e<w.length;e+=1)w[e].c();te=g(),T=r("div");for(let e=0;e<p.length;e+=1)p[e].c();k(l,"class","m-b-sm"),k(u,"class","content txt-lg m-b-sm"),k(j,"class","m-b-xs"),k(K,"class","label label-primary"),k(Q,"class","content"),k(M,"class","alert alert-info"),k(B,"class","section-title"),k(O,"class","tabs-header compact left"),k(T,"class","tabs-content"),k(S,"class","tabs")},m(e,t){h(e,l,t),n(l,o),n(l,m),n(l,f),h(e,i,t),h(e,u,t),n(u,d),n(d,v),n(d,C),n(C,U),n(d,X),h(e,q,t),me(P,e,t),h(e,D,t),h(e,j,t),h(e,W,t),h(e,M,t),n(M,K),n(M,R),n(M,Q),n(Q,A),n(A,Z),n(A,V),n(V,I),n(A,x),h(e,E,t),h(e,B,t),h(e,J,t),h(e,S,t),n(S,O);for(let c=0;c<w.length;c+=1)w[c].m(O,null);n(S,te),n(S,T);for(let c=0;c<p.length;c+=1)p[c].m(T,null);$=!0},p(e,[t]){var ie,ce;(!$||t&1)&&s!==(s=e[0].name+"")&&G(m,s),(!$||t&1)&&F!==(F=e[0].name+"")&&G(U,F);const c={};t&9&&(c.js=`
|
||||
`}});let z=a[2];const oe=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=de(a,z,e),c=oe(t);ee.set(c,w[e]=fe(c,t))}let N=a[2];const se=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=ue(a,N,e),c=se(t);le.set(c,p[e]=he(c,t))}return{c(){l=r("h3"),o=b("List auth methods ("),m=b(s),f=b(")"),i=g(),u=r("div"),d=r("p"),v=b("Returns a public list with all allowed "),C=r("strong"),U=b(F),R=b(" authentication methods."),q=g(),_e(P.$$.fragment),D=g(),j=r("h6"),j.textContent="API details",W=g(),M=r("div"),K=r("strong"),K.textContent="GET",X=g(),Q=r("div"),A=r("p"),Z=b("/api/collections/"),V=r("strong"),I=b(y),x=b("/auth-methods"),E=g(),B=r("div"),B.textContent="Responses",J=g(),S=r("div"),O=r("div");for(let e=0;e<w.length;e+=1)w[e].c();te=g(),T=r("div");for(let e=0;e<p.length;e+=1)p[e].c();k(l,"class","m-b-sm"),k(u,"class","content txt-lg m-b-sm"),k(j,"class","m-b-xs"),k(K,"class","label label-primary"),k(Q,"class","content"),k(M,"class","alert alert-info"),k(B,"class","section-title"),k(O,"class","tabs-header compact left"),k(T,"class","tabs-content"),k(S,"class","tabs")},m(e,t){h(e,l,t),n(l,o),n(l,m),n(l,f),h(e,i,t),h(e,u,t),n(u,d),n(d,v),n(d,C),n(C,U),n(d,R),h(e,q,t),me(P,e,t),h(e,D,t),h(e,j,t),h(e,W,t),h(e,M,t),n(M,K),n(M,X),n(M,Q),n(Q,A),n(A,Z),n(A,V),n(V,I),n(A,x),h(e,E,t),h(e,B,t),h(e,J,t),h(e,S,t),n(S,O);for(let c=0;c<w.length;c+=1)w[c].m(O,null);n(S,te),n(S,T);for(let c=0;c<p.length;c+=1)p[c].m(T,null);$=!0},p(e,[t]){var ie,ce;(!$||t&1)&&s!==(s=e[0].name+"")&&G(m,s),(!$||t&1)&&F!==(F=e[0].name+"")&&G(U,F);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
+8
-8
@@ -1,11 +1,11 @@
|
||||
import{S as ze,i as Ue,s as je,M as Ve,e as a,w as k,b as p,c as ae,f as b,g as c,h as o,m as ne,x as re,N as qe,O as xe,k as Ie,P as Je,n as Ke,t as U,a as j,o as d,d as ie,Q as Qe,C as He,p as We,r as x,u as Ge}from"./index.2a48c906.js";import{S as Xe}from"./SdkTabs.9dbb2ca3.js";function Ee(r,l,s){const n=r.slice();return n[5]=l[s],n}function Fe(r,l,s){const n=r.slice();return n[5]=l[s],n}function Le(r,l){let s,n=l[5].code+"",m,_,i,f;function v(){return l[4](l[5])}return{key:r,first:null,c(){s=a("button"),m=k(n),_=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(g,w){c(g,s,w),o(s,m),o(s,_),i||(f=Ge(s,"click",v),i=!0)},p(g,w){l=g,w&4&&n!==(n=l[5].code+"")&&re(m,n),w&6&&x(s,"active",l[1]===l[5].code)},d(g){g&&d(s),i=!1,f()}}}function Ne(r,l){let s,n,m,_;return n=new Ve({props:{content:l[5].body}}),{key:r,first:null,c(){s=a("div"),ae(n.$$.fragment),m=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(i,f){c(i,s,f),ne(n,s,null),o(s,m),_=!0},p(i,f){l=i;const v={};f&4&&(v.content=l[5].body),n.$set(v),(!_||f&6)&&x(s,"active",l[1]===l[5].code)},i(i){_||(U(n.$$.fragment,i),_=!0)},o(i){j(n.$$.fragment,i),_=!1},d(i){i&&d(s),ie(n)}}}function Ye(r){var Ae,Be;let l,s,n=r[0].name+"",m,_,i,f,v,g,w,A,I,S,F,ce,L,B,de,J,N=r[0].name+"",K,ue,pe,V,Q,D,W,T,G,fe,X,C,Y,he,Z,be,h,me,R,_e,ke,ve,ee,ge,te,ye,Se,$e,oe,we,le,O,se,P,q,$=[],Te=new Map,Ce,H,y=[],Pe=new Map,M;g=new Xe({props:{js:`
|
||||
import{S as ze,i as Ue,s as je,N as Ve,e as a,w as k,b as p,c as ae,f as b,g as c,h as o,m as ne,x as re,O as qe,P as xe,k as Ie,Q as Je,n as Ke,t as U,a as j,o as d,d as ie,R as Qe,C as He,p as We,r as x,u as Ge}from"./index.536fddcb.js";import{S as Xe}from"./SdkTabs.3571c26b.js";function Ee(r,l,s){const n=r.slice();return n[5]=l[s],n}function Fe(r,l,s){const n=r.slice();return n[5]=l[s],n}function Le(r,l){let s,n=l[5].code+"",m,_,i,f;function v(){return l[4](l[5])}return{key:r,first:null,c(){s=a("button"),m=k(n),_=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(g,w){c(g,s,w),o(s,m),o(s,_),i||(f=Ge(s,"click",v),i=!0)},p(g,w){l=g,w&4&&n!==(n=l[5].code+"")&&re(m,n),w&6&&x(s,"active",l[1]===l[5].code)},d(g){g&&d(s),i=!1,f()}}}function Ne(r,l){let s,n,m,_;return n=new Ve({props:{content:l[5].body}}),{key:r,first:null,c(){s=a("div"),ae(n.$$.fragment),m=p(),b(s,"class","tab-item"),x(s,"active",l[1]===l[5].code),this.first=s},m(i,f){c(i,s,f),ne(n,s,null),o(s,m),_=!0},p(i,f){l=i;const v={};f&4&&(v.content=l[5].body),n.$set(v),(!_||f&6)&&x(s,"active",l[1]===l[5].code)},i(i){_||(U(n.$$.fragment,i),_=!0)},o(i){j(n.$$.fragment,i),_=!1},d(i){i&&d(s),ie(n)}}}function Ye(r){var Be,Me;let l,s,n=r[0].name+"",m,_,i,f,v,g,w,B,I,S,F,ce,L,M,de,J,N=r[0].name+"",K,ue,pe,V,Q,D,W,T,G,fe,X,C,Y,he,Z,be,h,me,P,_e,ke,ve,ee,ge,te,ye,Se,$e,oe,we,le,O,se,R,q,$=[],Te=new Map,Ce,H,y=[],Re=new Map,A;g=new Xe({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(Ae=r[0])==null?void 0:Ae.name}').authRefresh();
|
||||
const authData = await pb.collection('${(Be=r[0])==null?void 0:Be.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
@@ -18,24 +18,24 @@ import{S as ze,i as Ue,s as je,M as Ve,e as a,w as k,b as p,c as ae,f as b,g as
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(Be=r[0])==null?void 0:Be.name}').authRefresh();
|
||||
final authData = await pb.collection('${(Me=r[0])==null?void 0:Me.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
`}}),R=new Ve({props:{content:"?expand=relField1,relField2.subRelField"}});let z=r[2];const Re=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=Fe(r,z,e),u=Re(t);Te.set(u,$[e]=Le(u,t))}let E=r[2];const Me=e=>e[5].code;for(let e=0;e<E.length;e+=1){let t=Ee(r,E,e),u=Me(t);Pe.set(u,y[e]=Ne(u,t))}return{c(){l=a("h3"),s=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),f=a("div"),f.innerHTML=`<p>Returns a new auth response (token and record data) for an
|
||||
`}}),P=new Ve({props:{content:"?expand=relField1,relField2.subRelField"}});let z=r[2];const Pe=e=>e[5].code;for(let e=0;e<z.length;e+=1){let t=Fe(r,z,e),u=Pe(t);Te.set(u,$[e]=Le(u,t))}let E=r[2];const Ae=e=>e[5].code;for(let e=0;e<E.length;e+=1){let t=Ee(r,E,e),u=Ae(t);Re.set(u,y[e]=Ne(u,t))}return{c(){l=a("h3"),s=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),f=a("div"),f.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>`,v=p(),ae(g.$$.fragment),w=p(),A=a("h6"),A.textContent="API details",I=p(),S=a("div"),F=a("strong"),F.textContent="POST",ce=p(),L=a("div"),B=a("p"),de=k("/api/collections/"),J=a("strong"),K=k(N),ue=k("/auth-refresh"),pe=p(),V=a("p"),V.innerHTML="Requires record <code>Authorization:TOKEN</code> header",Q=p(),D=a("div"),D.textContent="Query parameters",W=p(),T=a("table"),G=a("thead"),G.innerHTML=`<tr><th>Param</th>
|
||||
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,v=p(),ae(g.$$.fragment),w=p(),B=a("h6"),B.textContent="API details",I=p(),S=a("div"),F=a("strong"),F.textContent="POST",ce=p(),L=a("div"),M=a("p"),de=k("/api/collections/"),J=a("strong"),K=k(N),ue=k("/auth-refresh"),pe=p(),V=a("p"),V.innerHTML="Requires record <code>Authorization:TOKEN</code> header",Q=p(),D=a("div"),D.textContent="Query parameters",W=p(),T=a("table"),G=a("thead"),G.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,fe=p(),X=a("tbody"),C=a("tr"),Y=a("td"),Y.textContent="expand",he=p(),Z=a("td"),Z.innerHTML='<span class="label">String</span>',be=p(),h=a("td"),me=k(`Auto expand record relations. Ex.:
|
||||
`),ae(R.$$.fragment),_e=k(`
|
||||
`),ae(P.$$.fragment),_e=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),ke=a("br"),ve=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),ee=a("code"),ee.textContent="expand",ge=k(" property (eg. "),te=a("code"),te.textContent='"expand": {"relField1": {...}, ...}',ye=k(`).
|
||||
`),Se=a("br"),$e=k(`
|
||||
Only the relations to which the request user has permissions to `),oe=a("strong"),oe.textContent="view",we=k(" will be expanded."),le=p(),O=a("div"),O.textContent="Responses",se=p(),P=a("div"),q=a("div");for(let e=0;e<$.length;e+=1)$[e].c();Ce=p(),H=a("div");for(let e=0;e<y.length;e+=1)y[e].c();b(l,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(A,"class","m-b-xs"),b(F,"class","label label-primary"),b(L,"class","content"),b(V,"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(q,"class","tabs-header compact left"),b(H,"class","tabs-content"),b(P,"class","tabs")},m(e,t){c(e,l,t),o(l,s),o(l,m),o(l,_),c(e,i,t),c(e,f,t),c(e,v,t),ne(g,e,t),c(e,w,t),c(e,A,t),c(e,I,t),c(e,S,t),o(S,F),o(S,ce),o(S,L),o(L,B),o(B,de),o(B,J),o(J,K),o(B,ue),o(S,pe),o(S,V),c(e,Q,t),c(e,D,t),c(e,W,t),c(e,T,t),o(T,G),o(T,fe),o(T,X),o(X,C),o(C,Y),o(C,he),o(C,Z),o(C,be),o(C,h),o(h,me),ne(R,h,null),o(h,_e),o(h,ke),o(h,ve),o(h,ee),o(h,ge),o(h,te),o(h,ye),o(h,Se),o(h,$e),o(h,oe),o(h,we),c(e,le,t),c(e,O,t),c(e,se,t),c(e,P,t),o(P,q);for(let u=0;u<$.length;u+=1)$[u].m(q,null);o(P,Ce),o(P,H);for(let u=0;u<y.length;u+=1)y[u].m(H,null);M=!0},p(e,[t]){var De,Oe;(!M||t&1)&&n!==(n=e[0].name+"")&&re(m,n);const u={};t&9&&(u.js=`
|
||||
Only the relations to which the request user has permissions to `),oe=a("strong"),oe.textContent="view",we=k(" will be expanded."),le=p(),O=a("div"),O.textContent="Responses",se=p(),R=a("div"),q=a("div");for(let e=0;e<$.length;e+=1)$[e].c();Ce=p(),H=a("div");for(let e=0;e<y.length;e+=1)y[e].c();b(l,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(F,"class","label label-primary"),b(L,"class","content"),b(V,"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(q,"class","tabs-header compact left"),b(H,"class","tabs-content"),b(R,"class","tabs")},m(e,t){c(e,l,t),o(l,s),o(l,m),o(l,_),c(e,i,t),c(e,f,t),c(e,v,t),ne(g,e,t),c(e,w,t),c(e,B,t),c(e,I,t),c(e,S,t),o(S,F),o(S,ce),o(S,L),o(L,M),o(M,de),o(M,J),o(J,K),o(M,ue),o(S,pe),o(S,V),c(e,Q,t),c(e,D,t),c(e,W,t),c(e,T,t),o(T,G),o(T,fe),o(T,X),o(X,C),o(C,Y),o(C,he),o(C,Z),o(C,be),o(C,h),o(h,me),ne(P,h,null),o(h,_e),o(h,ke),o(h,ve),o(h,ee),o(h,ge),o(h,te),o(h,ye),o(h,Se),o(h,$e),o(h,oe),o(h,we),c(e,le,t),c(e,O,t),c(e,se,t),c(e,R,t),o(R,q);for(let u=0;u<$.length;u+=1)$[u].m(q,null);o(R,Ce),o(R,H);for(let u=0;u<y.length;u+=1)y[u].m(H,null);A=!0},p(e,[t]){var De,Oe;(!A||t&1)&&n!==(n=e[0].name+"")&&re(m,n);const u={};t&9&&(u.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -61,7 +61,7 @@ import{S as ze,i as Ue,s as je,M as Ve,e as a,w as k,b as p,c as ae,f as b,g as
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
`),g.$set(u),(!M||t&1)&&N!==(N=e[0].name+"")&&re(K,N),t&6&&(z=e[2],$=qe($,t,Re,1,e,z,Te,q,xe,Le,null,Fe)),t&6&&(E=e[2],Ie(),y=qe(y,t,Me,1,e,E,Pe,H,Je,Ne,null,Ee),Ke())},i(e){if(!M){U(g.$$.fragment,e),U(R.$$.fragment,e);for(let t=0;t<E.length;t+=1)U(y[t]);M=!0}},o(e){j(g.$$.fragment,e),j(R.$$.fragment,e);for(let t=0;t<y.length;t+=1)j(y[t]);M=!1},d(e){e&&d(l),e&&d(i),e&&d(f),e&&d(v),ie(g,e),e&&d(w),e&&d(A),e&&d(I),e&&d(S),e&&d(Q),e&&d(D),e&&d(W),e&&d(T),ie(R),e&&d(le),e&&d(O),e&&d(se),e&&d(P);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<y.length;t+=1)y[t].d()}}}function Ze(r,l,s){let n,{collection:m=new Qe}=l,_=200,i=[];const f=v=>s(1,_=v.code);return r.$$set=v=>{"collection"in v&&s(0,m=v.collection)},r.$$.update=()=>{r.$$.dirty&1&&s(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:He.dummyCollectionRecord(m)},null,2)},{code:401,body:`
|
||||
`),g.$set(u),(!A||t&1)&&N!==(N=e[0].name+"")&&re(K,N),t&6&&(z=e[2],$=qe($,t,Pe,1,e,z,Te,q,xe,Le,null,Fe)),t&6&&(E=e[2],Ie(),y=qe(y,t,Ae,1,e,E,Re,H,Je,Ne,null,Ee),Ke())},i(e){if(!A){U(g.$$.fragment,e),U(P.$$.fragment,e);for(let t=0;t<E.length;t+=1)U(y[t]);A=!0}},o(e){j(g.$$.fragment,e),j(P.$$.fragment,e);for(let t=0;t<y.length;t+=1)j(y[t]);A=!1},d(e){e&&d(l),e&&d(i),e&&d(f),e&&d(v),ie(g,e),e&&d(w),e&&d(B),e&&d(I),e&&d(S),e&&d(Q),e&&d(D),e&&d(W),e&&d(T),ie(P),e&&d(le),e&&d(O),e&&d(se),e&&d(R);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<y.length;t+=1)y[t].d()}}}function Ze(r,l,s){let n,{collection:m=new Qe}=l,_=200,i=[];const f=v=>s(1,_=v.code);return r.$$set=v=>{"collection"in v&&s(0,m=v.collection)},r.$$.update=()=>{r.$$.dirty&1&&s(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:He.dummyCollectionRecord(m)},null,2)},{code:401,body:`
|
||||
{
|
||||
"code": 401,
|
||||
"message": "The request requires valid record authorization token to be set.",
|
||||
Vendored
+8
-8
@@ -1,4 +1,4 @@
|
||||
import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as r,h as a,m as ce,x as de,N as Ve,O as Ne,k as Qe,P as ze,n as Ke,t as j,a as H,o as c,d as ue,Q as Ye,C as Be,p as Ge,r as J,u as Xe}from"./index.2a48c906.js";import{S as Ze}from"./SdkTabs.9dbb2ca3.js";function Fe(i,l,o){const n=i.slice();return n[5]=l[o],n}function Le(i,l,o){const n=i.slice();return n[5]=l[o],n}function Me(i,l){let o,n=l[5].code+"",m,_,d,b;function g(){return l[4](l[5])}return{key:i,first:null,c(){o=s("button"),m=v(n),_=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(k,y){r(k,o,y),a(o,m),a(o,_),d||(b=Xe(o,"click",g),d=!0)},p(k,y){l=k,y&4&&n!==(n=l[5].code+"")&&de(m,n),y&6&&J(o,"active",l[1]===l[5].code)},d(k){k&&c(o),d=!1,b()}}}function xe(i,l){let o,n,m,_;return n=new We({props:{content:l[5].body}}),{key:i,first:null,c(){o=s("div"),re(n.$$.fragment),m=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(d,b){r(d,o,b),ce(n,o,null),a(o,m),_=!0},p(d,b){l=d;const g={};b&4&&(g.content=l[5].body),n.$set(g),(!_||b&6)&&J(o,"active",l[1]===l[5].code)},i(d){_||(j(n.$$.fragment,d),_=!0)},o(d){H(n.$$.fragment,d),_=!1},d(d){d&&c(o),ue(n)}}}function et(i){var qe,Ie;let l,o,n=i[0].name+"",m,_,d,b,g,k,y,C,N,O,L,pe,M,D,he,Q,x=i[0].name+"",z,be,K,q,Y,I,G,P,X,R,Z,fe,ee,$,te,me,ae,_e,f,ve,E,ge,ke,we,le,Se,oe,ye,Oe,Re,se,$e,ne,U,ie,A,V,S=[],Ae=new Map,Ee,B,w=[],Te=new Map,T;k=new Ze({props:{js:`
|
||||
import{S as je,i as He,s as Je,N as We,e as s,w as v,b as p,c as re,f as h,g as r,h as a,m as ce,x as de,O as Ve,P as Ne,k as Qe,Q as ze,n as Ke,t as j,a as H,o as c,d as ue,R as Ye,C as Be,p as Ge,r as J,u as Xe}from"./index.536fddcb.js";import{S as Ze}from"./SdkTabs.3571c26b.js";function Fe(i,l,o){const n=i.slice();return n[5]=l[o],n}function Le(i,l,o){const n=i.slice();return n[5]=l[o],n}function xe(i,l){let o,n=l[5].code+"",m,_,d,b;function g(){return l[4](l[5])}return{key:i,first:null,c(){o=s("button"),m=v(n),_=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(k,R){r(k,o,R),a(o,m),a(o,_),d||(b=Xe(o,"click",g),d=!0)},p(k,R){l=k,R&4&&n!==(n=l[5].code+"")&&de(m,n),R&6&&J(o,"active",l[1]===l[5].code)},d(k){k&&c(o),d=!1,b()}}}function Me(i,l){let o,n,m,_;return n=new We({props:{content:l[5].body}}),{key:i,first:null,c(){o=s("div"),re(n.$$.fragment),m=p(),h(o,"class","tab-item"),J(o,"active",l[1]===l[5].code),this.first=o},m(d,b){r(d,o,b),ce(n,o,null),a(o,m),_=!0},p(d,b){l=d;const g={};b&4&&(g.content=l[5].body),n.$set(g),(!_||b&6)&&J(o,"active",l[1]===l[5].code)},i(d){_||(j(n.$$.fragment,d),_=!0)},o(d){H(n.$$.fragment,d),_=!1},d(d){d&&c(o),ue(n)}}}function et(i){var qe,Ie;let l,o,n=i[0].name+"",m,_,d,b,g,k,R,C,N,y,L,pe,x,D,he,Q,M=i[0].name+"",z,be,K,q,Y,I,G,P,X,O,Z,fe,ee,$,te,me,ae,_e,f,ve,E,ge,ke,we,le,Se,oe,Re,ye,Oe,se,$e,ne,U,ie,A,V,S=[],Ae=new Map,Ee,B,w=[],Te=new Map,T;k=new Ze({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i[3]}');
|
||||
@@ -48,11 +48,11 @@ import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`}}),E=new We({props:{content:"?expand=relField1,relField2.subRelField"}});let W=i[2];const Ce=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=Le(i,W,e),u=Ce(t);Ae.set(u,S[e]=Me(u,t))}let F=i[2];const De=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=Fe(i,F,e),u=De(t);Te.set(u,w[e]=xe(u,t))}return{c(){l=s("h3"),o=v("Auth with OAuth2 ("),m=v(n),_=v(")"),d=p(),b=s("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p>
|
||||
`}}),E=new We({props:{content:"?expand=relField1,relField2.subRelField"}});let W=i[2];const Ce=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=Le(i,W,e),u=Ce(t);Ae.set(u,S[e]=xe(u,t))}let F=i[2];const De=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=Fe(i,F,e),u=De(t);Te.set(u,w[e]=Me(u,t))}return{c(){l=s("h3"),o=v("Auth with OAuth2 ("),m=v(n),_=v(")"),d=p(),b=s("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p>
|
||||
<p>This action usually should be called right after the provider login page redirect.</p>
|
||||
<p>You could also check the
|
||||
<a href="https://pocketbase.io/docs/authentication/#web-oauth2-integration" target="_blank" rel="noopener noreferrer">OAuth2 web integration example
|
||||
</a>.</p>`,g=p(),re(k.$$.fragment),y=p(),C=s("h6"),C.textContent="API details",N=p(),O=s("div"),L=s("strong"),L.textContent="POST",pe=p(),M=s("div"),D=s("p"),he=v("/api/collections/"),Q=s("strong"),z=v(x),be=v("/auth-with-oauth2"),K=p(),q=s("div"),q.textContent="Body Parameters",Y=p(),I=s("table"),I.innerHTML=`<thead><tr><th>Param</th>
|
||||
</a>.</p>`,g=p(),re(k.$$.fragment),R=p(),C=s("h6"),C.textContent="API details",N=p(),y=s("div"),L=s("strong"),L.textContent="POST",pe=p(),x=s("div"),D=s("p"),he=v("/api/collections/"),Q=s("strong"),z=v(M),be=v("/auth-with-oauth2"),K=p(),q=s("div"),q.textContent="Body Parameters",Y=p(),I=s("table"),I.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>
|
||||
@@ -79,15 +79,15 @@ import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as
|
||||
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>`,G=p(),P=s("div"),P.textContent="Query parameters",X=p(),R=s("table"),Z=s("thead"),Z.innerHTML=`<tr><th>Param</th>
|
||||
upload currently are not supported during OAuth2 sign-ups.</em></p></td></tr></tbody>`,G=p(),P=s("div"),P.textContent="Query parameters",X=p(),O=s("table"),Z=s("thead"),Z.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,fe=p(),ee=s("tbody"),$=s("tr"),te=s("td"),te.textContent="expand",me=p(),ae=s("td"),ae.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ve=v(`Auto expand record relations. Ex.:
|
||||
`),re(E.$$.fragment),ge=v(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),ke=s("br"),we=v(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),le=s("code"),le.textContent="expand",Se=v(" property (eg. "),oe=s("code"),oe.textContent='"expand": {"relField1": {...}, ...}',ye=v(`).
|
||||
`),Oe=s("br"),Re=v(`
|
||||
Only the relations to which the request user has permissions to `),se=s("strong"),se.textContent="view",$e=v(" will be expanded."),ne=p(),U=s("div"),U.textContent="Responses",ie=p(),A=s("div"),V=s("div");for(let e=0;e<S.length;e+=1)S[e].c();Ee=p(),B=s("div");for(let e=0;e<w.length;e+=1)w[e].c();h(l,"class","m-b-sm"),h(b,"class","content txt-lg m-b-sm"),h(C,"class","m-b-xs"),h(L,"class","label label-primary"),h(M,"class","content"),h(O,"class","alert alert-success"),h(q,"class","section-title"),h(I,"class","table-compact table-border m-b-base"),h(P,"class","section-title"),h(R,"class","table-compact table-border m-b-base"),h(U,"class","section-title"),h(V,"class","tabs-header compact left"),h(B,"class","tabs-content"),h(A,"class","tabs")},m(e,t){r(e,l,t),a(l,o),a(l,m),a(l,_),r(e,d,t),r(e,b,t),r(e,g,t),ce(k,e,t),r(e,y,t),r(e,C,t),r(e,N,t),r(e,O,t),a(O,L),a(O,pe),a(O,M),a(M,D),a(D,he),a(D,Q),a(Q,z),a(D,be),r(e,K,t),r(e,q,t),r(e,Y,t),r(e,I,t),r(e,G,t),r(e,P,t),r(e,X,t),r(e,R,t),a(R,Z),a(R,fe),a(R,ee),a(ee,$),a($,te),a($,me),a($,ae),a($,_e),a($,f),a(f,ve),ce(E,f,null),a(f,ge),a(f,ke),a(f,we),a(f,le),a(f,Se),a(f,oe),a(f,ye),a(f,Oe),a(f,Re),a(f,se),a(f,$e),r(e,ne,t),r(e,U,t),r(e,ie,t),r(e,A,t),a(A,V);for(let u=0;u<S.length;u+=1)S[u].m(V,null);a(A,Ee),a(A,B);for(let u=0;u<w.length;u+=1)w[u].m(B,null);T=!0},p(e,[t]){var Pe,Ue;(!T||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const u={};t&9&&(u.js=`
|
||||
`),le=s("code"),le.textContent="expand",Se=v(" property (eg. "),oe=s("code"),oe.textContent='"expand": {"relField1": {...}, ...}',Re=v(`).
|
||||
`),ye=s("br"),Oe=v(`
|
||||
Only the relations to which the request user has permissions to `),se=s("strong"),se.textContent="view",$e=v(" will be expanded."),ne=p(),U=s("div"),U.textContent="Responses",ie=p(),A=s("div"),V=s("div");for(let e=0;e<S.length;e+=1)S[e].c();Ee=p(),B=s("div");for(let e=0;e<w.length;e+=1)w[e].c();h(l,"class","m-b-sm"),h(b,"class","content txt-lg m-b-sm"),h(C,"class","m-b-xs"),h(L,"class","label label-primary"),h(x,"class","content"),h(y,"class","alert alert-success"),h(q,"class","section-title"),h(I,"class","table-compact table-border m-b-base"),h(P,"class","section-title"),h(O,"class","table-compact table-border m-b-base"),h(U,"class","section-title"),h(V,"class","tabs-header compact left"),h(B,"class","tabs-content"),h(A,"class","tabs")},m(e,t){r(e,l,t),a(l,o),a(l,m),a(l,_),r(e,d,t),r(e,b,t),r(e,g,t),ce(k,e,t),r(e,R,t),r(e,C,t),r(e,N,t),r(e,y,t),a(y,L),a(y,pe),a(y,x),a(x,D),a(D,he),a(D,Q),a(Q,z),a(D,be),r(e,K,t),r(e,q,t),r(e,Y,t),r(e,I,t),r(e,G,t),r(e,P,t),r(e,X,t),r(e,O,t),a(O,Z),a(O,fe),a(O,ee),a(ee,$),a($,te),a($,me),a($,ae),a($,_e),a($,f),a(f,ve),ce(E,f,null),a(f,ge),a(f,ke),a(f,we),a(f,le),a(f,Se),a(f,oe),a(f,Re),a(f,ye),a(f,Oe),a(f,se),a(f,$e),r(e,ne,t),r(e,U,t),r(e,ie,t),r(e,A,t),a(A,V);for(let u=0;u<S.length;u+=1)S[u].m(V,null);a(A,Ee),a(A,B);for(let u=0;u<w.length;u+=1)w[u].m(B,null);T=!0},p(e,[t]){var Pe,Ue;(!T||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const u={};t&9&&(u.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -137,7 +137,7 @@ import{S as je,i as He,s as Je,M as We,e as s,w as v,b as p,c as re,f as h,g as
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`),k.$set(u),(!T||t&1)&&x!==(x=e[0].name+"")&&de(z,x),t&6&&(W=e[2],S=Ve(S,t,Ce,1,e,W,Ae,V,Ne,Me,null,Le)),t&6&&(F=e[2],Qe(),w=Ve(w,t,De,1,e,F,Te,B,ze,xe,null,Fe),Ke())},i(e){if(!T){j(k.$$.fragment,e),j(E.$$.fragment,e);for(let t=0;t<F.length;t+=1)j(w[t]);T=!0}},o(e){H(k.$$.fragment,e),H(E.$$.fragment,e);for(let t=0;t<w.length;t+=1)H(w[t]);T=!1},d(e){e&&c(l),e&&c(d),e&&c(b),e&&c(g),ue(k,e),e&&c(y),e&&c(C),e&&c(N),e&&c(O),e&&c(K),e&&c(q),e&&c(Y),e&&c(I),e&&c(G),e&&c(P),e&&c(X),e&&c(R),ue(E),e&&c(ne),e&&c(U),e&&c(ie),e&&c(A);for(let t=0;t<S.length;t+=1)S[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function tt(i,l,o){let n,{collection:m=new Ye}=l,_=200,d=[];const b=g=>o(1,_=g.code);return i.$$set=g=>{"collection"in g&&o(0,m=g.collection)},i.$$.update=()=>{i.$$.dirty&1&&o(2,d=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Be.dummyCollectionRecord(m),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png"}},null,2)},{code:400,body:`
|
||||
`),k.$set(u),(!T||t&1)&&M!==(M=e[0].name+"")&&de(z,M),t&6&&(W=e[2],S=Ve(S,t,Ce,1,e,W,Ae,V,Ne,xe,null,Le)),t&6&&(F=e[2],Qe(),w=Ve(w,t,De,1,e,F,Te,B,ze,Me,null,Fe),Ke())},i(e){if(!T){j(k.$$.fragment,e),j(E.$$.fragment,e);for(let t=0;t<F.length;t+=1)j(w[t]);T=!0}},o(e){H(k.$$.fragment,e),H(E.$$.fragment,e);for(let t=0;t<w.length;t+=1)H(w[t]);T=!1},d(e){e&&c(l),e&&c(d),e&&c(b),e&&c(g),ue(k,e),e&&c(R),e&&c(C),e&&c(N),e&&c(y),e&&c(K),e&&c(q),e&&c(Y),e&&c(I),e&&c(G),e&&c(P),e&&c(X),e&&c(O),ue(E),e&&c(ne),e&&c(U),e&&c(ie),e&&c(A);for(let t=0;t<S.length;t+=1)S[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function tt(i,l,o){let n,{collection:m=new Ye}=l,_=200,d=[];const b=g=>o(1,_=g.code);return i.$$set=g=>{"collection"in g&&o(0,m=g.collection)},i.$$.update=()=>{i.$$.dirty&1&&o(2,d=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Be.dummyCollectionRecord(m),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png"}},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while submitting the form.",
|
||||
Vendored
+7
-7
@@ -1,4 +1,4 @@
|
||||
import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as r,h as o,m as At,x as Tt,N as ce,O as ye,k as ge,P as Pe,n as $e,t as tt,a as et,o as c,d as Mt,Q as Re,C as de,p as Ce,r as lt,u as Oe}from"./index.2a48c906.js";import{S as Ae}from"./SdkTabs.9dbb2ca3.js";function ue(n,e,l){const i=n.slice();return i[8]=e[l],i}function fe(n,e,l){const i=n.slice();return i[8]=e[l],i}function Te(n){let e;return{c(){e=f("email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Me(n){let e;return{c(){e=f("username")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Ue(n){let e;return{c(){e=f("username/email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function pe(n){let e;return{c(){e=s("strong"),e.textContent="username"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function be(n){let e;return{c(){e=f("or")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function me(n){let e;return{c(){e=s("strong"),e.textContent="email"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function he(n,e){let l,i=e[8].code+"",S,m,p,d;function _(){return e[7](e[8])}return{key:n,first:null,c(){l=s("button"),S=f(i),m=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m(R,C){r(R,l,C),o(l,S),o(l,m),p||(d=Oe(l,"click",_),p=!0)},p(R,C){e=R,C&16&&i!==(i=e[8].code+"")&&Tt(S,i),C&24&<(l,"active",e[3]===e[8].code)},d(R){R&&c(l),p=!1,d()}}}function _e(n,e){let l,i,S,m;return i=new ke({props:{content:e[8].body}}),{key:n,first:null,c(){l=s("div"),Ot(i.$$.fragment),S=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m(p,d){r(p,l,d),At(i,l,null),o(l,S),m=!0},p(p,d){e=p;const _={};d&16&&(_.content=e[8].body),i.$set(_),(!m||d&24)&<(l,"active",e[3]===e[8].code)},i(p){m||(tt(i.$$.fragment,p),m=!0)},o(p){et(i.$$.fragment,p),m=!1},d(p){p&&c(l),Mt(i)}}}function De(n){var se,ne;let e,l,i=n[0].name+"",S,m,p,d,_,R,C,O,B,Ut,ot,T,at,F,st,M,G,Dt,X,I,Et,nt,Z=n[0].name+"",it,Wt,rt,N,ct,U,dt,Lt,V,D,ut,Bt,ft,Ht,g,Yt,pt,bt,mt,qt,ht,_t,j,kt,E,St,Ft,vt,W,wt,It,yt,Nt,k,Vt,H,jt,Jt,Qt,gt,Kt,Pt,zt,Gt,Xt,$t,Zt,Rt,J,Ct,L,Q,A=[],xt=new Map,te,K,P=[],ee=new Map,Y;function le(t,a){if(t[1]&&t[2])return Ue;if(t[1])return Me;if(t[2])return Te}let q=le(n),$=q&&q(n);T=new Ae({props:{js:`
|
||||
import{S as Se,i as ve,s as we,N as ke,e as s,w as f,b as u,c as Ot,f as h,g as r,h as o,m as At,x as Tt,O as ce,P as ye,k as ge,Q as Pe,n as Re,t as tt,a as et,o as c,d as Ut,R as $e,C as de,p as Ce,r as lt,u as Oe}from"./index.536fddcb.js";import{S as Ae}from"./SdkTabs.3571c26b.js";function ue(n,e,l){const i=n.slice();return i[8]=e[l],i}function fe(n,e,l){const i=n.slice();return i[8]=e[l],i}function Te(n){let e;return{c(){e=f("email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Ue(n){let e;return{c(){e=f("username")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function Me(n){let e;return{c(){e=f("username/email")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function pe(n){let e;return{c(){e=s("strong"),e.textContent="username"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function be(n){let e;return{c(){e=f("or")},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function me(n){let e;return{c(){e=s("strong"),e.textContent="email"},m(l,i){r(l,e,i)},d(l){l&&c(e)}}}function he(n,e){let l,i=e[8].code+"",S,m,p,d;function _(){return e[7](e[8])}return{key:n,first:null,c(){l=s("button"),S=f(i),m=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m($,C){r($,l,C),o(l,S),o(l,m),p||(d=Oe(l,"click",_),p=!0)},p($,C){e=$,C&16&&i!==(i=e[8].code+"")&&Tt(S,i),C&24&<(l,"active",e[3]===e[8].code)},d($){$&&c(l),p=!1,d()}}}function _e(n,e){let l,i,S,m;return i=new ke({props:{content:e[8].body}}),{key:n,first:null,c(){l=s("div"),Ot(i.$$.fragment),S=u(),h(l,"class","tab-item"),lt(l,"active",e[3]===e[8].code),this.first=l},m(p,d){r(p,l,d),At(i,l,null),o(l,S),m=!0},p(p,d){e=p;const _={};d&16&&(_.content=e[8].body),i.$set(_),(!m||d&24)&<(l,"active",e[3]===e[8].code)},i(p){m||(tt(i.$$.fragment,p),m=!0)},o(p){et(i.$$.fragment,p),m=!1},d(p){p&&c(l),Ut(i)}}}function De(n){var se,ne;let e,l,i=n[0].name+"",S,m,p,d,_,$,C,O,B,Mt,ot,T,at,F,st,U,G,Dt,X,I,Et,nt,Z=n[0].name+"",it,Wt,rt,N,ct,M,dt,Lt,V,D,ut,Bt,ft,Ht,g,Yt,pt,bt,mt,qt,ht,_t,j,kt,E,St,Ft,vt,W,wt,It,yt,Nt,k,Vt,H,jt,Jt,Qt,gt,Kt,Pt,zt,Gt,Xt,Rt,Zt,$t,J,Ct,L,Q,A=[],xt=new Map,te,K,P=[],ee=new Map,Y;function le(t,a){if(t[1]&&t[2])return Me;if(t[1])return Ue;if(t[2])return Te}let q=le(n),R=q&&q(n);T=new Ae({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[6]}');
|
||||
@@ -36,9 +36,9 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`}});let v=n[1]&&pe(),w=n[1]&&n[2]&&be(),y=n[2]&&me();H=new ke({props:{content:"?expand=relField1,relField2.subRelField"}});let x=n[4];const oe=t=>t[8].code;for(let t=0;t<x.length;t+=1){let a=fe(n,x,t),b=oe(a);xt.set(b,A[t]=he(b,a))}let z=n[4];const ae=t=>t[8].code;for(let t=0;t<z.length;t+=1){let a=ue(n,z,t),b=ae(a);ee.set(b,P[t]=_e(b,a))}return{c(){e=s("h3"),l=f("Auth with password ("),S=f(i),m=f(")"),p=u(),d=s("div"),_=s("p"),R=f(`Returns new auth token and account data by a combination of
|
||||
`),C=s("strong"),$&&$.c(),O=f(`
|
||||
and `),B=s("strong"),B.textContent="password",Ut=f("."),ot=u(),Ot(T.$$.fragment),at=u(),F=s("h6"),F.textContent="API details",st=u(),M=s("div"),G=s("strong"),G.textContent="POST",Dt=u(),X=s("div"),I=s("p"),Et=f("/api/collections/"),nt=s("strong"),it=f(Z),Wt=f("/auth-with-password"),rt=u(),N=s("div"),N.textContent="Body Parameters",ct=u(),U=s("table"),dt=s("thead"),dt.innerHTML=`<tr><th>Param</th>
|
||||
`}});let v=n[1]&&pe(),w=n[1]&&n[2]&&be(),y=n[2]&&me();H=new ke({props:{content:"?expand=relField1,relField2.subRelField"}});let x=n[4];const oe=t=>t[8].code;for(let t=0;t<x.length;t+=1){let a=fe(n,x,t),b=oe(a);xt.set(b,A[t]=he(b,a))}let z=n[4];const ae=t=>t[8].code;for(let t=0;t<z.length;t+=1){let a=ue(n,z,t),b=ae(a);ee.set(b,P[t]=_e(b,a))}return{c(){e=s("h3"),l=f("Auth with password ("),S=f(i),m=f(")"),p=u(),d=s("div"),_=s("p"),$=f(`Returns new auth token and account data by a combination of
|
||||
`),C=s("strong"),R&&R.c(),O=f(`
|
||||
and `),B=s("strong"),B.textContent="password",Mt=f("."),ot=u(),Ot(T.$$.fragment),at=u(),F=s("h6"),F.textContent="API details",st=u(),U=s("div"),G=s("strong"),G.textContent="POST",Dt=u(),X=s("div"),I=s("p"),Et=f("/api/collections/"),nt=s("strong"),it=f(Z),Wt=f("/auth-with-password"),rt=u(),N=s("div"),N.textContent="Body Parameters",ct=u(),M=s("table"),dt=s("thead"),dt.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr>`,Lt=u(),V=s("tbody"),D=s("tr"),ut=s("td"),ut.innerHTML=`<div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>identity</span></div>`,Bt=u(),ft=s("td"),ft.innerHTML='<span class="label">String</span>',Ht=u(),g=s("td"),Yt=f(`The
|
||||
@@ -54,7 +54,7 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
|
||||
The expanded relations will be appended to the record under the
|
||||
`),gt=s("code"),gt.textContent="expand",Kt=f(" property (eg. "),Pt=s("code"),Pt.textContent='"expand": {"relField1": {...}, ...}',zt=f(`).
|
||||
`),Gt=s("br"),Xt=f(`
|
||||
Only the relations to which the request user has permissions to `),$t=s("strong"),$t.textContent="view",Zt=f(" will be expanded."),Rt=u(),J=s("div"),J.textContent="Responses",Ct=u(),L=s("div"),Q=s("div");for(let t=0;t<A.length;t+=1)A[t].c();te=u(),K=s("div");for(let t=0;t<P.length;t+=1)P[t].c();h(e,"class","m-b-sm"),h(d,"class","content txt-lg m-b-sm"),h(F,"class","m-b-xs"),h(G,"class","label label-primary"),h(X,"class","content"),h(M,"class","alert alert-success"),h(N,"class","section-title"),h(U,"class","table-compact table-border m-b-base"),h(j,"class","section-title"),h(E,"class","table-compact table-border m-b-base"),h(J,"class","section-title"),h(Q,"class","tabs-header compact left"),h(K,"class","tabs-content"),h(L,"class","tabs")},m(t,a){r(t,e,a),o(e,l),o(e,S),o(e,m),r(t,p,a),r(t,d,a),o(d,_),o(_,R),o(_,C),$&&$.m(C,null),o(_,O),o(_,B),o(_,Ut),r(t,ot,a),At(T,t,a),r(t,at,a),r(t,F,a),r(t,st,a),r(t,M,a),o(M,G),o(M,Dt),o(M,X),o(X,I),o(I,Et),o(I,nt),o(nt,it),o(I,Wt),r(t,rt,a),r(t,N,a),r(t,ct,a),r(t,U,a),o(U,dt),o(U,Lt),o(U,V),o(V,D),o(D,ut),o(D,Bt),o(D,ft),o(D,Ht),o(D,g),o(g,Yt),v&&v.m(g,null),o(g,pt),w&&w.m(g,null),o(g,bt),y&&y.m(g,null),o(g,mt),o(V,qt),o(V,ht),r(t,_t,a),r(t,j,a),r(t,kt,a),r(t,E,a),o(E,St),o(E,Ft),o(E,vt),o(vt,W),o(W,wt),o(W,It),o(W,yt),o(W,Nt),o(W,k),o(k,Vt),At(H,k,null),o(k,jt),o(k,Jt),o(k,Qt),o(k,gt),o(k,Kt),o(k,Pt),o(k,zt),o(k,Gt),o(k,Xt),o(k,$t),o(k,Zt),r(t,Rt,a),r(t,J,a),r(t,Ct,a),r(t,L,a),o(L,Q);for(let b=0;b<A.length;b+=1)A[b].m(Q,null);o(L,te),o(L,K);for(let b=0;b<P.length;b+=1)P[b].m(K,null);Y=!0},p(t,[a]){var ie,re;(!Y||a&1)&&i!==(i=t[0].name+"")&&Tt(S,i),q!==(q=le(t))&&($&&$.d(1),$=q&&q(t),$&&($.c(),$.m(C,null)));const b={};a&97&&(b.js=`
|
||||
Only the relations to which the request user has permissions to `),Rt=s("strong"),Rt.textContent="view",Zt=f(" will be expanded."),$t=u(),J=s("div"),J.textContent="Responses",Ct=u(),L=s("div"),Q=s("div");for(let t=0;t<A.length;t+=1)A[t].c();te=u(),K=s("div");for(let t=0;t<P.length;t+=1)P[t].c();h(e,"class","m-b-sm"),h(d,"class","content txt-lg m-b-sm"),h(F,"class","m-b-xs"),h(G,"class","label label-primary"),h(X,"class","content"),h(U,"class","alert alert-success"),h(N,"class","section-title"),h(M,"class","table-compact table-border m-b-base"),h(j,"class","section-title"),h(E,"class","table-compact table-border m-b-base"),h(J,"class","section-title"),h(Q,"class","tabs-header compact left"),h(K,"class","tabs-content"),h(L,"class","tabs")},m(t,a){r(t,e,a),o(e,l),o(e,S),o(e,m),r(t,p,a),r(t,d,a),o(d,_),o(_,$),o(_,C),R&&R.m(C,null),o(_,O),o(_,B),o(_,Mt),r(t,ot,a),At(T,t,a),r(t,at,a),r(t,F,a),r(t,st,a),r(t,U,a),o(U,G),o(U,Dt),o(U,X),o(X,I),o(I,Et),o(I,nt),o(nt,it),o(I,Wt),r(t,rt,a),r(t,N,a),r(t,ct,a),r(t,M,a),o(M,dt),o(M,Lt),o(M,V),o(V,D),o(D,ut),o(D,Bt),o(D,ft),o(D,Ht),o(D,g),o(g,Yt),v&&v.m(g,null),o(g,pt),w&&w.m(g,null),o(g,bt),y&&y.m(g,null),o(g,mt),o(V,qt),o(V,ht),r(t,_t,a),r(t,j,a),r(t,kt,a),r(t,E,a),o(E,St),o(E,Ft),o(E,vt),o(vt,W),o(W,wt),o(W,It),o(W,yt),o(W,Nt),o(W,k),o(k,Vt),At(H,k,null),o(k,jt),o(k,Jt),o(k,Qt),o(k,gt),o(k,Kt),o(k,Pt),o(k,zt),o(k,Gt),o(k,Xt),o(k,Rt),o(k,Zt),r(t,$t,a),r(t,J,a),r(t,Ct,a),r(t,L,a),o(L,Q);for(let b=0;b<A.length;b+=1)A[b].m(Q,null);o(L,te),o(L,K);for(let b=0;b<P.length;b+=1)P[b].m(K,null);Y=!0},p(t,[a]){var ie,re;(!Y||a&1)&&i!==(i=t[0].name+"")&&Tt(S,i),q!==(q=le(t))&&(R&&R.d(1),R=q&&q(t),R&&(R.c(),R.m(C,null)));const b={};a&97&&(b.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${t[6]}');
|
||||
@@ -92,7 +92,7 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`),T.$set(b),(!Y||a&1)&&Z!==(Z=t[0].name+"")&&Tt(it,Z),t[1]?v||(v=pe(),v.c(),v.m(g,pt)):v&&(v.d(1),v=null),t[1]&&t[2]?w||(w=be(),w.c(),w.m(g,bt)):w&&(w.d(1),w=null),t[2]?y||(y=me(),y.c(),y.m(g,mt)):y&&(y.d(1),y=null),a&24&&(x=t[4],A=ce(A,a,oe,1,t,x,xt,Q,ye,he,null,fe)),a&24&&(z=t[4],ge(),P=ce(P,a,ae,1,t,z,ee,K,Pe,_e,null,ue),$e())},i(t){if(!Y){tt(T.$$.fragment,t),tt(H.$$.fragment,t);for(let a=0;a<z.length;a+=1)tt(P[a]);Y=!0}},o(t){et(T.$$.fragment,t),et(H.$$.fragment,t);for(let a=0;a<P.length;a+=1)et(P[a]);Y=!1},d(t){t&&c(e),t&&c(p),t&&c(d),$&&$.d(),t&&c(ot),Mt(T,t),t&&c(at),t&&c(F),t&&c(st),t&&c(M),t&&c(rt),t&&c(N),t&&c(ct),t&&c(U),v&&v.d(),w&&w.d(),y&&y.d(),t&&c(_t),t&&c(j),t&&c(kt),t&&c(E),Mt(H),t&&c(Rt),t&&c(J),t&&c(Ct),t&&c(L);for(let a=0;a<A.length;a+=1)A[a].d();for(let a=0;a<P.length;a+=1)P[a].d()}}}function Ee(n,e,l){let i,S,m,p,{collection:d=new Re}=e,_=200,R=[];const C=O=>l(3,_=O.code);return n.$$set=O=>{"collection"in O&&l(0,d=O.collection)},n.$$.update=()=>{var O,B;n.$$.dirty&1&&l(2,S=(O=d==null?void 0:d.options)==null?void 0:O.allowEmailAuth),n.$$.dirty&1&&l(1,m=(B=d==null?void 0:d.options)==null?void 0:B.allowUsernameAuth),n.$$.dirty&6&&l(5,p=m&&S?"YOUR_USERNAME_OR_EMAIL":m?"YOUR_USERNAME":"YOUR_EMAIL"),n.$$.dirty&1&&l(4,R=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:de.dummyCollectionRecord(d)},null,2)},{code:400,body:`
|
||||
`),T.$set(b),(!Y||a&1)&&Z!==(Z=t[0].name+"")&&Tt(it,Z),t[1]?v||(v=pe(),v.c(),v.m(g,pt)):v&&(v.d(1),v=null),t[1]&&t[2]?w||(w=be(),w.c(),w.m(g,bt)):w&&(w.d(1),w=null),t[2]?y||(y=me(),y.c(),y.m(g,mt)):y&&(y.d(1),y=null),a&24&&(x=t[4],A=ce(A,a,oe,1,t,x,xt,Q,ye,he,null,fe)),a&24&&(z=t[4],ge(),P=ce(P,a,ae,1,t,z,ee,K,Pe,_e,null,ue),Re())},i(t){if(!Y){tt(T.$$.fragment,t),tt(H.$$.fragment,t);for(let a=0;a<z.length;a+=1)tt(P[a]);Y=!0}},o(t){et(T.$$.fragment,t),et(H.$$.fragment,t);for(let a=0;a<P.length;a+=1)et(P[a]);Y=!1},d(t){t&&c(e),t&&c(p),t&&c(d),R&&R.d(),t&&c(ot),Ut(T,t),t&&c(at),t&&c(F),t&&c(st),t&&c(U),t&&c(rt),t&&c(N),t&&c(ct),t&&c(M),v&&v.d(),w&&w.d(),y&&y.d(),t&&c(_t),t&&c(j),t&&c(kt),t&&c(E),Ut(H),t&&c($t),t&&c(J),t&&c(Ct),t&&c(L);for(let a=0;a<A.length;a+=1)A[a].d();for(let a=0;a<P.length;a+=1)P[a].d()}}}function Ee(n,e,l){let i,S,m,p,{collection:d=new $e}=e,_=200,$=[];const C=O=>l(3,_=O.code);return n.$$set=O=>{"collection"in O&&l(0,d=O.collection)},n.$$.update=()=>{var O,B;n.$$.dirty&1&&l(2,S=(O=d==null?void 0:d.options)==null?void 0:O.allowEmailAuth),n.$$.dirty&1&&l(1,m=(B=d==null?void 0:d.options)==null?void 0:B.allowUsernameAuth),n.$$.dirty&6&&l(5,p=m&&S?"YOUR_USERNAME_OR_EMAIL":m?"YOUR_USERNAME":"YOUR_EMAIL"),n.$$.dirty&1&&l(4,$=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:de.dummyCollectionRecord(d)},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
@@ -103,4 +103,4 @@ import{S as Se,i as ve,s as we,M as ke,e as s,w as f,b as u,c as Ot,f as h,g as
|
||||
}
|
||||
}
|
||||
}
|
||||
`}])},l(6,i=de.getApiExampleUrl(Ce.baseUrl)),[d,m,S,_,R,p,i,C]}class Be extends Se{constructor(e){super(),ve(this,e,Ee,De,we,{collection:0})}}export{Be as default};
|
||||
`}])},l(6,i=de.getApiExampleUrl(Ce.baseUrl)),[d,m,S,_,$,p,i,C]}class Be extends Se{constructor(e){super(),ve(this,e,Ee,De,we,{collection:0})}}export{Be as default};
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+5
-5
@@ -1,4 +1,4 @@
|
||||
import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n,m as ve,x as Y,N as pe,O as Pe,k as Se,P as Oe,n as Re,t as Z,a as x,o as f,d as ge,Q as Te,C as Ee,p as ye,r as j,u as Be,M as qe}from"./index.2a48c906.js";import{S as Ae}from"./SdkTabs.9dbb2ca3.js";function ue(o,l,s){const a=o.slice();return a[5]=l[s],a}function be(o,l,s){const a=o.slice();return a[5]=l[s],a}function _e(o,l){let s,a=l[5].code+"",_,u,i,d;function p(){return l[4](l[5])}return{key:o,first:null,c(){s=c("button"),_=v(a),u=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(C,$){r(C,s,$),n(s,_),n(s,u),i||(d=Be(s,"click",p),i=!0)},p(C,$){l=C,$&4&&a!==(a=l[5].code+"")&&Y(_,a),$&6&&j(s,"active",l[1]===l[5].code)},d(C){C&&f(s),i=!1,d()}}}function ke(o,l){let s,a,_,u;return a=new qe({props:{content:l[5].body}}),{key:o,first:null,c(){s=c("div"),he(a.$$.fragment),_=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(i,d){r(i,s,d),ve(a,s,null),n(s,_),u=!0},p(i,d){l=i;const p={};d&4&&(p.content=l[5].body),a.$set(p),(!u||d&6)&&j(s,"active",l[1]===l[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&f(s),ge(a)}}}function Ue(o){var re,fe;let l,s,a=o[0].name+"",_,u,i,d,p,C,$,D=o[0].name+"",H,ee,I,w,F,R,L,P,M,te,N,T,le,Q,K=o[0].name+"",z,se,G,E,J,y,V,B,X,S,q,g=[],ae=new Map,oe,A,k=[],ne=new Map,O;w=new Ae({props:{js:`
|
||||
import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n,m as ve,x as Y,O as pe,P as Pe,k as Se,Q as Oe,n as Re,t as Z,a as x,o as f,d as ge,R as Te,C as Ee,p as ye,r as j,u as Be,N as qe}from"./index.536fddcb.js";import{S as Ae}from"./SdkTabs.3571c26b.js";function ue(o,l,s){const a=o.slice();return a[5]=l[s],a}function be(o,l,s){const a=o.slice();return a[5]=l[s],a}function _e(o,l){let s,a=l[5].code+"",_,u,i,d;function p(){return l[4](l[5])}return{key:o,first:null,c(){s=c("button"),_=v(a),u=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(C,$){r(C,s,$),n(s,_),n(s,u),i||(d=Be(s,"click",p),i=!0)},p(C,$){l=C,$&4&&a!==(a=l[5].code+"")&&Y(_,a),$&6&&j(s,"active",l[1]===l[5].code)},d(C){C&&f(s),i=!1,d()}}}function ke(o,l){let s,a,_,u;return a=new qe({props:{content:l[5].body}}),{key:o,first:null,c(){s=c("div"),he(a.$$.fragment),_=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(i,d){r(i,s,d),ve(a,s,null),n(s,_),u=!0},p(i,d){l=i;const p={};d&4&&(p.content=l[5].body),a.$set(p),(!u||d&6)&&j(s,"active",l[1]===l[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&f(s),ge(a)}}}function Ue(o){var re,fe;let l,s,a=o[0].name+"",_,u,i,d,p,C,$,D=o[0].name+"",H,ee,I,w,F,R,L,P,N,te,K,T,le,Q,M=o[0].name+"",z,se,G,E,J,y,V,B,X,S,q,g=[],ae=new Map,oe,A,k=[],ne=new Map,O;w=new Ae({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
@@ -20,7 +20,7 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`}});let W=o[2];const ie=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=be(o,W,e),m=ie(t);ae.set(m,g[e]=_e(m,t))}let U=o[2];const ce=e=>e[5].code;for(let e=0;e<U.length;e+=1){let t=ue(o,U,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=v("Confirm email change ("),_=v(a),u=v(")"),i=h(),d=c("div"),p=c("p"),C=v("Confirms "),$=c("strong"),H=v(D),ee=v(" email change request."),I=h(),he(w.$$.fragment),F=h(),R=c("h6"),R.textContent="API details",L=h(),P=c("div"),M=c("strong"),M.textContent="POST",te=h(),N=c("div"),T=c("p"),le=v("/api/collections/"),Q=c("strong"),z=v(K),se=v("/confirm-email-change"),G=h(),E=c("div"),E.textContent="Body Parameters",J=h(),y=c("table"),y.innerHTML=`<thead><tr><th>Param</th>
|
||||
`}});let W=o[2];const ie=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=be(o,W,e),m=ie(t);ae.set(m,g[e]=_e(m,t))}let U=o[2];const ce=e=>e[5].code;for(let e=0;e<U.length;e+=1){let t=ue(o,U,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=v("Confirm email change ("),_=v(a),u=v(")"),i=h(),d=c("div"),p=c("p"),C=v("Confirms "),$=c("strong"),H=v(D),ee=v(" email change request."),I=h(),he(w.$$.fragment),F=h(),R=c("h6"),R.textContent="API details",L=h(),P=c("div"),N=c("strong"),N.textContent="POST",te=h(),K=c("div"),T=c("p"),le=v("/api/collections/"),Q=c("strong"),z=v(M),se=v("/confirm-email-change"),G=h(),E=c("div"),E.textContent="Body Parameters",J=h(),y=c("table"),y.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>
|
||||
@@ -30,7 +30,7 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
|
||||
<tr><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 account password to confirm the email change.</td></tr></tbody>`,V=h(),B=c("div"),B.textContent="Responses",X=h(),S=c("div"),q=c("div");for(let e=0;e<g.length;e+=1)g[e].c();oe=h(),A=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(d,"class","content txt-lg m-b-sm"),b(R,"class","m-b-xs"),b(M,"class","label label-primary"),b(N,"class","content"),b(P,"class","alert alert-success"),b(E,"class","section-title"),b(y,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(A,"class","tabs-content"),b(S,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,i,t),r(e,d,t),n(d,p),n(p,C),n(p,$),n($,H),n(p,ee),r(e,I,t),ve(w,e,t),r(e,F,t),r(e,R,t),r(e,L,t),r(e,P,t),n(P,M),n(P,te),n(P,N),n(N,T),n(T,le),n(T,Q),n(Q,z),n(T,se),r(e,G,t),r(e,E,t),r(e,J,t),r(e,y,t),r(e,V,t),r(e,B,t),r(e,X,t),r(e,S,t),n(S,q);for(let m=0;m<g.length;m+=1)g[m].m(q,null);n(S,oe),n(S,A);for(let m=0;m<k.length;m+=1)k[m].m(A,null);O=!0},p(e,[t]){var me,de;(!O||t&1)&&a!==(a=e[0].name+"")&&Y(_,a),(!O||t&1)&&D!==(D=e[0].name+"")&&Y(H,D);const m={};t&9&&(m.js=`
|
||||
<td>The account password to confirm the email change.</td></tr></tbody>`,V=h(),B=c("div"),B.textContent="Responses",X=h(),S=c("div"),q=c("div");for(let e=0;e<g.length;e+=1)g[e].c();oe=h(),A=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(d,"class","content txt-lg m-b-sm"),b(R,"class","m-b-xs"),b(N,"class","label label-primary"),b(K,"class","content"),b(P,"class","alert alert-success"),b(E,"class","section-title"),b(y,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(A,"class","tabs-content"),b(S,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,i,t),r(e,d,t),n(d,p),n(p,C),n(p,$),n($,H),n(p,ee),r(e,I,t),ve(w,e,t),r(e,F,t),r(e,R,t),r(e,L,t),r(e,P,t),n(P,N),n(P,te),n(P,K),n(K,T),n(T,le),n(T,Q),n(Q,z),n(T,se),r(e,G,t),r(e,E,t),r(e,J,t),r(e,y,t),r(e,V,t),r(e,B,t),r(e,X,t),r(e,S,t),n(S,q);for(let m=0;m<g.length;m+=1)g[m].m(q,null);n(S,oe),n(S,A);for(let m=0;m<k.length;m+=1)k[m].m(A,null);O=!0},p(e,[t]){var me,de;(!O||t&1)&&a!==(a=e[0].name+"")&&Y(_,a),(!O||t&1)&&D!==(D=e[0].name+"")&&Y(H,D);const m={};t&9&&(m.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -52,7 +52,7 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),w.$set(m),(!O||t&1)&&K!==(K=e[0].name+"")&&Y(z,K),t&6&&(W=e[2],g=pe(g,t,ie,1,e,W,ae,q,Pe,_e,null,be)),t&6&&(U=e[2],Se(),k=pe(k,t,ce,1,e,U,ne,A,Oe,ke,null,ue),Re())},i(e){if(!O){Z(w.$$.fragment,e);for(let t=0;t<U.length;t+=1)Z(k[t]);O=!0}},o(e){x(w.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);O=!1},d(e){e&&f(l),e&&f(i),e&&f(d),e&&f(I),ge(w,e),e&&f(F),e&&f(R),e&&f(L),e&&f(P),e&&f(G),e&&f(E),e&&f(J),e&&f(y),e&&f(V),e&&f(B),e&&f(X),e&&f(S);for(let t=0;t<g.length;t+=1)g[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function De(o,l,s){let a,{collection:_=new Te}=l,u=204,i=[];const d=p=>s(1,u=p.code);return o.$$set=p=>{"collection"in p&&s(0,_=p.collection)},s(3,a=Ee.getApiExampleUrl(ye.baseUrl)),s(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
`),w.$set(m),(!O||t&1)&&M!==(M=e[0].name+"")&&Y(z,M),t&6&&(W=e[2],g=pe(g,t,ie,1,e,W,ae,q,Pe,_e,null,be)),t&6&&(U=e[2],Se(),k=pe(k,t,ce,1,e,U,ne,A,Oe,ke,null,ue),Re())},i(e){if(!O){Z(w.$$.fragment,e);for(let t=0;t<U.length;t+=1)Z(k[t]);O=!0}},o(e){x(w.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);O=!1},d(e){e&&f(l),e&&f(i),e&&f(d),e&&f(I),ge(w,e),e&&f(F),e&&f(R),e&&f(L),e&&f(P),e&&f(G),e&&f(E),e&&f(J),e&&f(y),e&&f(V),e&&f(B),e&&f(X),e&&f(S);for(let t=0;t<g.length;t+=1)g[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function De(o,l,s){let a,{collection:_=new Te}=l,u=204,i=[];const d=p=>s(1,u=p.code);return o.$$set=p=>{"collection"in p&&s(0,_=p.collection)},s(3,a=Ee.getApiExampleUrl(ye.baseUrl)),s(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
@@ -63,4 +63,4 @@ import{S as Ce,i as $e,s as we,e as c,w as v,b as h,c as he,f as b,g as r,h as n
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[_,u,i,a,d]}class Ke extends Ce{constructor(l){super(),$e(this,l,De,Ue,we,{collection:0})}}export{Ke as default};
|
||||
`}]),[_,u,i,a,d]}class Me extends Ce{constructor(l){super(),$e(this,l,De,Ue,we,{collection:0})}}export{Me as default};
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as we,x as K,N as me,O as Oe,k as Ne,P as Ce,n as We,t as Z,a as x,o as d,d as Pe,Q as $e,C as Ee,p as Te,r as U,u as ge,M as Ae}from"./index.2a48c906.js";import{S as De}from"./SdkTabs.9dbb2ca3.js";function ue(o,s,l){const a=o.slice();return a[5]=s[l],a}function be(o,s,l){const a=o.slice();return a[5]=s[l],a}function _e(o,s){let l,a=s[5].code+"",_,u,i,p;function m(){return s[4](s[5])}return{key:o,first:null,c(){l=c("button"),_=w(a),u=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(S,h){r(S,l,h),n(l,_),n(l,u),i||(p=ge(l,"click",m),i=!0)},p(S,h){s=S,h&4&&a!==(a=s[5].code+"")&&K(_,a),h&6&&U(l,"active",s[1]===s[5].code)},d(S){S&&d(l),i=!1,p()}}}function ke(o,s){let l,a,_,u;return a=new Ae({props:{content:s[5].body}}),{key:o,first:null,c(){l=c("div"),ve(a.$$.fragment),_=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(i,p){r(i,l,p),we(a,l,null),n(l,_),u=!0},p(i,p){s=i;const m={};p&4&&(m.content=s[5].body),a.$set(m),(!u||p&6)&&U(l,"active",s[1]===s[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&d(l),Pe(a)}}}function ye(o){var re,de;let s,l,a=o[0].name+"",_,u,i,p,m,S,h,M=o[0].name+"",j,ee,H,R,L,W,Q,O,q,te,B,$,se,z,I=o[0].name+"",G,le,J,E,V,T,X,g,Y,N,A,P=[],ae=new Map,oe,D,k=[],ne=new Map,C;R=new De({props:{js:`
|
||||
import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as we,x as K,O as me,P as Oe,k as Ne,Q as Ce,n as We,t as Z,a as x,o as d,d as Pe,R as $e,C as Ee,p as Te,r as U,u as ge,N as Ae}from"./index.536fddcb.js";import{S as De}from"./SdkTabs.3571c26b.js";function ue(o,s,l){const a=o.slice();return a[5]=s[l],a}function be(o,s,l){const a=o.slice();return a[5]=s[l],a}function _e(o,s){let l,a=s[5].code+"",_,u,i,p;function m(){return s[4](s[5])}return{key:o,first:null,c(){l=c("button"),_=w(a),u=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(S,h){r(S,l,h),n(l,_),n(l,u),i||(p=ge(l,"click",m),i=!0)},p(S,h){s=S,h&4&&a!==(a=s[5].code+"")&&K(_,a),h&6&&U(l,"active",s[1]===s[5].code)},d(S){S&&d(l),i=!1,p()}}}function ke(o,s){let l,a,_,u;return a=new Ae({props:{content:s[5].body}}),{key:o,first:null,c(){l=c("div"),ve(a.$$.fragment),_=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(i,p){r(i,l,p),we(a,l,null),n(l,_),u=!0},p(i,p){s=i;const m={};p&4&&(m.content=s[5].body),a.$set(m),(!u||p&6)&&U(l,"active",s[1]===s[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&d(l),Pe(a)}}}function ye(o){var re,de;let s,l,a=o[0].name+"",_,u,i,p,m,S,h,q=o[0].name+"",j,ee,H,R,L,W,Q,O,B,te,M,$,se,z,I=o[0].name+"",G,le,J,E,V,T,X,g,Y,N,A,P=[],ae=new Map,oe,D,k=[],ne=new Map,C;R=new De({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
@@ -22,7 +22,7 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`}});let F=o[2];const ie=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=be(o,F,e),f=ie(t);ae.set(f,P[e]=_e(f,t))}let y=o[2];const ce=e=>e[5].code;for(let e=0;e<y.length;e+=1){let t=ue(o,y,e),f=ce(t);ne.set(f,k[e]=ke(f,t))}return{c(){s=c("h3"),l=w("Confirm password reset ("),_=w(a),u=w(")"),i=v(),p=c("div"),m=c("p"),S=w("Confirms "),h=c("strong"),j=w(M),ee=w(" password reset request and sets a new password."),H=v(),ve(R.$$.fragment),L=v(),W=c("h6"),W.textContent="API details",Q=v(),O=c("div"),q=c("strong"),q.textContent="POST",te=v(),B=c("div"),$=c("p"),se=w("/api/collections/"),z=c("strong"),G=w(I),le=w("/confirm-password-reset"),J=v(),E=c("div"),E.textContent="Body Parameters",V=v(),T=c("table"),T.innerHTML=`<thead><tr><th>Param</th>
|
||||
`}});let F=o[2];const ie=e=>e[5].code;for(let e=0;e<F.length;e+=1){let t=be(o,F,e),f=ie(t);ae.set(f,P[e]=_e(f,t))}let y=o[2];const ce=e=>e[5].code;for(let e=0;e<y.length;e+=1){let t=ue(o,y,e),f=ce(t);ne.set(f,k[e]=ke(f,t))}return{c(){s=c("h3"),l=w("Confirm password reset ("),_=w(a),u=w(")"),i=v(),p=c("div"),m=c("p"),S=w("Confirms "),h=c("strong"),j=w(q),ee=w(" password reset request and sets a new password."),H=v(),ve(R.$$.fragment),L=v(),W=c("h6"),W.textContent="API details",Q=v(),O=c("div"),B=c("strong"),B.textContent="POST",te=v(),M=c("div"),$=c("p"),se=w("/api/collections/"),z=c("strong"),G=w(I),le=w("/confirm-password-reset"),J=v(),E=c("div"),E.textContent="Body Parameters",V=v(),T=c("table"),T.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>
|
||||
@@ -36,7 +36,7 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
|
||||
<tr><td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>passwordConfirm</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The new password confirmation.</td></tr></tbody>`,X=v(),g=c("div"),g.textContent="Responses",Y=v(),N=c("div"),A=c("div");for(let e=0;e<P.length;e+=1)P[e].c();oe=v(),D=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(s,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(W,"class","m-b-xs"),b(q,"class","label label-primary"),b(B,"class","content"),b(O,"class","alert alert-success"),b(E,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(g,"class","section-title"),b(A,"class","tabs-header compact left"),b(D,"class","tabs-content"),b(N,"class","tabs")},m(e,t){r(e,s,t),n(s,l),n(s,_),n(s,u),r(e,i,t),r(e,p,t),n(p,m),n(m,S),n(m,h),n(h,j),n(m,ee),r(e,H,t),we(R,e,t),r(e,L,t),r(e,W,t),r(e,Q,t),r(e,O,t),n(O,q),n(O,te),n(O,B),n(B,$),n($,se),n($,z),n(z,G),n($,le),r(e,J,t),r(e,E,t),r(e,V,t),r(e,T,t),r(e,X,t),r(e,g,t),r(e,Y,t),r(e,N,t),n(N,A);for(let f=0;f<P.length;f+=1)P[f].m(A,null);n(N,oe),n(N,D);for(let f=0;f<k.length;f+=1)k[f].m(D,null);C=!0},p(e,[t]){var fe,pe;(!C||t&1)&&a!==(a=e[0].name+"")&&K(_,a),(!C||t&1)&&M!==(M=e[0].name+"")&&K(j,M);const f={};t&9&&(f.js=`
|
||||
<td>The new password confirmation.</td></tr></tbody>`,X=v(),g=c("div"),g.textContent="Responses",Y=v(),N=c("div"),A=c("div");for(let e=0;e<P.length;e+=1)P[e].c();oe=v(),D=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(s,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(W,"class","m-b-xs"),b(B,"class","label label-primary"),b(M,"class","content"),b(O,"class","alert alert-success"),b(E,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(g,"class","section-title"),b(A,"class","tabs-header compact left"),b(D,"class","tabs-content"),b(N,"class","tabs")},m(e,t){r(e,s,t),n(s,l),n(s,_),n(s,u),r(e,i,t),r(e,p,t),n(p,m),n(m,S),n(m,h),n(h,j),n(m,ee),r(e,H,t),we(R,e,t),r(e,L,t),r(e,W,t),r(e,Q,t),r(e,O,t),n(O,B),n(O,te),n(O,M),n(M,$),n($,se),n($,z),n(z,G),n($,le),r(e,J,t),r(e,E,t),r(e,V,t),r(e,T,t),r(e,X,t),r(e,g,t),r(e,Y,t),r(e,N,t),n(N,A);for(let f=0;f<P.length;f+=1)P[f].m(A,null);n(N,oe),n(N,D);for(let f=0;f<k.length;f+=1)k[f].m(D,null);C=!0},p(e,[t]){var fe,pe;(!C||t&1)&&a!==(a=e[0].name+"")&&K(_,a),(!C||t&1)&&q!==(q=e[0].name+"")&&K(j,q);const f={};t&9&&(f.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -60,7 +60,7 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`),R.$set(f),(!C||t&1)&&I!==(I=e[0].name+"")&&K(G,I),t&6&&(F=e[2],P=me(P,t,ie,1,e,F,ae,A,Oe,_e,null,be)),t&6&&(y=e[2],Ne(),k=me(k,t,ce,1,e,y,ne,D,Ce,ke,null,ue),We())},i(e){if(!C){Z(R.$$.fragment,e);for(let t=0;t<y.length;t+=1)Z(k[t]);C=!0}},o(e){x(R.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);C=!1},d(e){e&&d(s),e&&d(i),e&&d(p),e&&d(H),Pe(R,e),e&&d(L),e&&d(W),e&&d(Q),e&&d(O),e&&d(J),e&&d(E),e&&d(V),e&&d(T),e&&d(X),e&&d(g),e&&d(Y),e&&d(N);for(let t=0;t<P.length;t+=1)P[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Me(o,s,l){let a,{collection:_=new $e}=s,u=204,i=[];const p=m=>l(1,u=m.code);return o.$$set=m=>{"collection"in m&&l(0,_=m.collection)},l(3,a=Ee.getApiExampleUrl(Te.baseUrl)),l(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
`),R.$set(f),(!C||t&1)&&I!==(I=e[0].name+"")&&K(G,I),t&6&&(F=e[2],P=me(P,t,ie,1,e,F,ae,A,Oe,_e,null,be)),t&6&&(y=e[2],Ne(),k=me(k,t,ce,1,e,y,ne,D,Ce,ke,null,ue),We())},i(e){if(!C){Z(R.$$.fragment,e);for(let t=0;t<y.length;t+=1)Z(k[t]);C=!0}},o(e){x(R.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);C=!1},d(e){e&&d(s),e&&d(i),e&&d(p),e&&d(H),Pe(R,e),e&&d(L),e&&d(W),e&&d(Q),e&&d(O),e&&d(J),e&&d(E),e&&d(V),e&&d(T),e&&d(X),e&&d(g),e&&d(Y),e&&d(N);for(let t=0;t<P.length;t+=1)P[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function qe(o,s,l){let a,{collection:_=new $e}=s,u=204,i=[];const p=m=>l(1,u=m.code);return o.$$set=m=>{"collection"in m&&l(0,_=m.collection)},l(3,a=Ee.getApiExampleUrl(Te.baseUrl)),l(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
@@ -71,4 +71,4 @@ import{S as Se,i as he,s as Re,e as c,w,b as v,c as ve,f as b,g as r,h as n,m as
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[_,u,i,a,p]}class Ie extends Se{constructor(s){super(),he(this,s,Me,ye,Re,{collection:0})}}export{Ie as default};
|
||||
`}]),[_,u,i,a,p]}class Ie extends Se{constructor(s){super(),he(this,s,qe,ye,Re,{collection:0})}}export{Ie as default};
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n,m as he,x as H,N as de,O as Te,k as ge,P as ye,n as Be,t as Z,a as x,o as f,d as $e,Q as qe,C as Oe,p as Se,r as I,u as Ee,M as Me}from"./index.2a48c906.js";import{S as Ne}from"./SdkTabs.9dbb2ca3.js";function ue(i,l,s){const o=i.slice();return o[5]=l[s],o}function be(i,l,s){const o=i.slice();return o[5]=l[s],o}function _e(i,l){let s,o=l[5].code+"",_,u,a,p;function d(){return l[4](l[5])}return{key:i,first:null,c(){s=c("button"),_=h(o),u=v(),b(s,"class","tab-item"),I(s,"active",l[1]===l[5].code),this.first=s},m(w,C){r(w,s,C),n(s,_),n(s,u),a||(p=Ee(s,"click",d),a=!0)},p(w,C){l=w,C&4&&o!==(o=l[5].code+"")&&H(_,o),C&6&&I(s,"active",l[1]===l[5].code)},d(w){w&&f(s),a=!1,p()}}}function ke(i,l){let s,o,_,u;return o=new Me({props:{content:l[5].body}}),{key:i,first:null,c(){s=c("div"),ve(o.$$.fragment),_=v(),b(s,"class","tab-item"),I(s,"active",l[1]===l[5].code),this.first=s},m(a,p){r(a,s,p),he(o,s,null),n(s,_),u=!0},p(a,p){l=a;const d={};p&4&&(d.content=l[5].body),o.$set(d),(!u||p&6)&&I(s,"active",l[1]===l[5].code)},i(a){u||(Z(o.$$.fragment,a),u=!0)},o(a){x(o.$$.fragment,a),u=!1},d(a){a&&f(s),$e(o)}}}function Ve(i){var re,fe;let l,s,o=i[0].name+"",_,u,a,p,d,w,C,K=i[0].name+"",R,ee,F,P,L,B,Q,T,A,te,U,q,le,z,j=i[0].name+"",G,se,J,O,W,S,X,E,Y,g,M,$=[],oe=new Map,ie,N,k=[],ne=new Map,y;P=new Ne({props:{js:`
|
||||
import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n,m as he,x as D,O as de,P as Te,k as ge,Q as ye,n as Be,t as Z,a as x,o as f,d as $e,R as qe,C as Oe,p as Se,r as H,u as Ee,N as Ne}from"./index.536fddcb.js";import{S as Ve}from"./SdkTabs.3571c26b.js";function ue(i,l,s){const o=i.slice();return o[5]=l[s],o}function be(i,l,s){const o=i.slice();return o[5]=l[s],o}function _e(i,l){let s,o=l[5].code+"",_,u,a,p;function d(){return l[4](l[5])}return{key:i,first:null,c(){s=c("button"),_=h(o),u=v(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m(w,C){r(w,s,C),n(s,_),n(s,u),a||(p=Ee(s,"click",d),a=!0)},p(w,C){l=w,C&4&&o!==(o=l[5].code+"")&&D(_,o),C&6&&H(s,"active",l[1]===l[5].code)},d(w){w&&f(s),a=!1,p()}}}function ke(i,l){let s,o,_,u;return o=new Ne({props:{content:l[5].body}}),{key:i,first:null,c(){s=c("div"),ve(o.$$.fragment),_=v(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m(a,p){r(a,s,p),he(o,s,null),n(s,_),u=!0},p(a,p){l=a;const d={};p&4&&(d.content=l[5].body),o.$set(d),(!u||p&6)&&H(s,"active",l[1]===l[5].code)},i(a){u||(Z(o.$$.fragment,a),u=!0)},o(a){x(o.$$.fragment,a),u=!1},d(a){a&&f(s),$e(o)}}}function Ke(i){var re,fe;let l,s,o=i[0].name+"",_,u,a,p,d,w,C,M=i[0].name+"",I,ee,F,P,L,B,Q,T,A,te,R,q,le,z,U=i[0].name+"",G,se,J,O,W,S,X,E,Y,g,N,$=[],oe=new Map,ie,V,k=[],ne=new Map,y;P=new Ve({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i[3]}');
|
||||
@@ -14,13 +14,13 @@ import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=i[0])==null?void 0:fe.name}').confirmVerification('TOKEN');
|
||||
`}});let D=i[2];const ae=e=>e[5].code;for(let e=0;e<D.length;e+=1){let t=be(i,D,e),m=ae(t);oe.set(m,$[e]=_e(m,t))}let V=i[2];const ce=e=>e[5].code;for(let e=0;e<V.length;e+=1){let t=ue(i,V,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=h("Confirm verification ("),_=h(o),u=h(")"),a=v(),p=c("div"),d=c("p"),w=h("Confirms "),C=c("strong"),R=h(K),ee=h(" account verification request."),F=v(),ve(P.$$.fragment),L=v(),B=c("h6"),B.textContent="API details",Q=v(),T=c("div"),A=c("strong"),A.textContent="POST",te=v(),U=c("div"),q=c("p"),le=h("/api/collections/"),z=c("strong"),G=h(j),se=h("/confirm-verification"),J=v(),O=c("div"),O.textContent="Body Parameters",W=v(),S=c("table"),S.innerHTML=`<thead><tr><th>Param</th>
|
||||
`}});let j=i[2];const ae=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=be(i,j,e),m=ae(t);oe.set(m,$[e]=_e(m,t))}let K=i[2];const ce=e=>e[5].code;for(let e=0;e<K.length;e+=1){let t=ue(i,K,e),m=ce(t);ne.set(m,k[e]=ke(m,t))}return{c(){l=c("h3"),s=h("Confirm verification ("),_=h(o),u=h(")"),a=v(),p=c("div"),d=c("p"),w=h("Confirms "),C=c("strong"),I=h(M),ee=h(" account verification request."),F=v(),ve(P.$$.fragment),L=v(),B=c("h6"),B.textContent="API details",Q=v(),T=c("div"),A=c("strong"),A.textContent="POST",te=v(),R=c("div"),q=c("p"),le=h("/api/collections/"),z=c("strong"),G=h(U),se=h("/confirm-verification"),J=v(),O=c("div"),O.textContent="Body Parameters",W=v(),S=c("table"),S.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>token</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The token from the verification request email.</td></tr></tbody>`,X=v(),E=c("div"),E.textContent="Responses",Y=v(),g=c("div"),M=c("div");for(let e=0;e<$.length;e+=1)$[e].c();ie=v(),N=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(A,"class","label label-primary"),b(U,"class","content"),b(T,"class","alert alert-success"),b(O,"class","section-title"),b(S,"class","table-compact table-border m-b-base"),b(E,"class","section-title"),b(M,"class","tabs-header compact left"),b(N,"class","tabs-content"),b(g,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,a,t),r(e,p,t),n(p,d),n(d,w),n(d,C),n(C,R),n(d,ee),r(e,F,t),he(P,e,t),r(e,L,t),r(e,B,t),r(e,Q,t),r(e,T,t),n(T,A),n(T,te),n(T,U),n(U,q),n(q,le),n(q,z),n(z,G),n(q,se),r(e,J,t),r(e,O,t),r(e,W,t),r(e,S,t),r(e,X,t),r(e,E,t),r(e,Y,t),r(e,g,t),n(g,M);for(let m=0;m<$.length;m+=1)$[m].m(M,null);n(g,ie),n(g,N);for(let m=0;m<k.length;m+=1)k[m].m(N,null);y=!0},p(e,[t]){var me,pe;(!y||t&1)&&o!==(o=e[0].name+"")&&H(_,o),(!y||t&1)&&K!==(K=e[0].name+"")&&H(R,K);const m={};t&9&&(m.js=`
|
||||
<td>The token from the verification request email.</td></tr></tbody>`,X=v(),E=c("div"),E.textContent="Responses",Y=v(),g=c("div"),N=c("div");for(let e=0;e<$.length;e+=1)$[e].c();ie=v(),V=c("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(A,"class","label label-primary"),b(R,"class","content"),b(T,"class","alert alert-success"),b(O,"class","section-title"),b(S,"class","table-compact table-border m-b-base"),b(E,"class","section-title"),b(N,"class","tabs-header compact left"),b(V,"class","tabs-content"),b(g,"class","tabs")},m(e,t){r(e,l,t),n(l,s),n(l,_),n(l,u),r(e,a,t),r(e,p,t),n(p,d),n(d,w),n(d,C),n(C,I),n(d,ee),r(e,F,t),he(P,e,t),r(e,L,t),r(e,B,t),r(e,Q,t),r(e,T,t),n(T,A),n(T,te),n(T,R),n(R,q),n(q,le),n(q,z),n(z,G),n(q,se),r(e,J,t),r(e,O,t),r(e,W,t),r(e,S,t),r(e,X,t),r(e,E,t),r(e,Y,t),r(e,g,t),n(g,N);for(let m=0;m<$.length;m+=1)$[m].m(N,null);n(g,ie),n(g,V);for(let m=0;m<k.length;m+=1)k[m].m(V,null);y=!0},p(e,[t]){var me,pe;(!y||t&1)&&o!==(o=e[0].name+"")&&D(_,o),(!y||t&1)&&M!==(M=e[0].name+"")&&D(I,M);const m={};t&9&&(m.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -36,7 +36,7 @@ import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n
|
||||
...
|
||||
|
||||
await pb.collection('${(pe=e[0])==null?void 0:pe.name}').confirmVerification('TOKEN');
|
||||
`),P.$set(m),(!y||t&1)&&j!==(j=e[0].name+"")&&H(G,j),t&6&&(D=e[2],$=de($,t,ae,1,e,D,oe,M,Te,_e,null,be)),t&6&&(V=e[2],ge(),k=de(k,t,ce,1,e,V,ne,N,ye,ke,null,ue),Be())},i(e){if(!y){Z(P.$$.fragment,e);for(let t=0;t<V.length;t+=1)Z(k[t]);y=!0}},o(e){x(P.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);y=!1},d(e){e&&f(l),e&&f(a),e&&f(p),e&&f(F),$e(P,e),e&&f(L),e&&f(B),e&&f(Q),e&&f(T),e&&f(J),e&&f(O),e&&f(W),e&&f(S),e&&f(X),e&&f(E),e&&f(Y),e&&f(g);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Ke(i,l,s){let o,{collection:_=new qe}=l,u=204,a=[];const p=d=>s(1,u=d.code);return i.$$set=d=>{"collection"in d&&s(0,_=d.collection)},s(3,o=Oe.getApiExampleUrl(Se.baseUrl)),s(2,a=[{code:204,body:"null"},{code:400,body:`
|
||||
`),P.$set(m),(!y||t&1)&&U!==(U=e[0].name+"")&&D(G,U),t&6&&(j=e[2],$=de($,t,ae,1,e,j,oe,N,Te,_e,null,be)),t&6&&(K=e[2],ge(),k=de(k,t,ce,1,e,K,ne,V,ye,ke,null,ue),Be())},i(e){if(!y){Z(P.$$.fragment,e);for(let t=0;t<K.length;t+=1)Z(k[t]);y=!0}},o(e){x(P.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);y=!1},d(e){e&&f(l),e&&f(a),e&&f(p),e&&f(F),$e(P,e),e&&f(L),e&&f(B),e&&f(Q),e&&f(T),e&&f(J),e&&f(O),e&&f(W),e&&f(S),e&&f(X),e&&f(E),e&&f(Y),e&&f(g);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Me(i,l,s){let o,{collection:_=new qe}=l,u=204,a=[];const p=d=>s(1,u=d.code);return i.$$set=d=>{"collection"in d&&s(0,_=d.collection)},s(3,o=Oe.getApiExampleUrl(Se.baseUrl)),s(2,a=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
@@ -47,4 +47,4 @@ import{S as we,i as Ce,s as Pe,e as c,w as h,b as v,c as ve,f as b,g as r,h as n
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[_,u,a,o,p]}class je extends we{constructor(l){super(),Ce(this,l,Ke,Ve,Pe,{collection:0})}}export{je as default};
|
||||
`}]),[_,u,a,o,p]}class Ue extends we{constructor(l){super(),Ce(this,l,Me,Ke,Pe,{collection:0})}}export{Ue as default};
|
||||
+11
-11
@@ -1,15 +1,15 @@
|
||||
import{S as Ht,i as Lt,s as Pt,C as Q,M as At,e as a,w as k,b as m,c as Pe,f as h,g as r,h as n,m as Be,x,N as Le,O as ht,k as Bt,P as Ft,n as Rt,t as fe,a as pe,o as d,d as Fe,Q as gt,p as jt,r as ue,u as Dt,y as le}from"./index.2a48c906.js";import{S as Nt}from"./SdkTabs.9dbb2ca3.js";function wt(o,e,l){const s=o.slice();return s[7]=e[l],s}function Ct(o,e,l){const s=o.slice();return s[7]=e[l],s}function St(o,e,l){const s=o.slice();return s[12]=e[l],s}function $t(o){let e;return{c(){e=a("p"),e.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",h(e,"class","txt-hint txt-sm txt-right")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Tt(o){let e,l,s,b,p,c,f,y,T,w,M,g,D,V,L,I,j,F,S,N,q,C,_;function O(u,$){var ee,K;return(K=(ee=u[0])==null?void 0:ee.options)!=null&&K.requireEmail?It:Vt}let z=O(o),P=z(o);return{c(){e=a("tr"),e.innerHTML='<td colspan="3" class="txt-hint">Auth fields</td>',l=m(),s=a("tr"),s.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
import{S as Ht,i as Lt,s as Pt,C as Q,N as At,e as a,w as k,b as m,c as Pe,f as h,g as r,h as n,m as Re,x,O as Le,P as ht,k as Rt,Q as Bt,n as Ft,t as fe,a as pe,o as d,d as Be,R as gt,p as jt,r as ue,u as Dt,y as le}from"./index.536fddcb.js";import{S as Nt}from"./SdkTabs.3571c26b.js";function wt(o,e,l){const s=o.slice();return s[7]=e[l],s}function Ct(o,e,l){const s=o.slice();return s[7]=e[l],s}function St(o,e,l){const s=o.slice();return s[12]=e[l],s}function $t(o){let e;return{c(){e=a("p"),e.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",h(e,"class","txt-hint txt-sm txt-right")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Tt(o){let e,l,s,b,p,c,f,y,T,w,O,g,D,V,L,I,j,B,S,N,q,C,_;function M(u,$){var ee,K;return(K=(ee=u[0])==null?void 0:ee.options)!=null&&K.requireEmail?It:Vt}let z=M(o),P=z(o);return{c(){e=a("tr"),e.innerHTML='<td colspan="3" class="txt-hint">Auth fields</td>',l=m(),s=a("tr"),s.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<span>username</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The username of the auth record.
|
||||
<br/>
|
||||
If not set, it will be auto generated.</td>`,b=m(),p=a("tr"),c=a("td"),f=a("div"),P.c(),y=m(),T=a("span"),T.textContent="email",w=m(),M=a("td"),M.innerHTML='<span class="label">String</span>',g=m(),D=a("td"),D.textContent="Auth record email address.",V=m(),L=a("tr"),L.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
If not set, it will be auto generated.</td>`,b=m(),p=a("tr"),c=a("td"),f=a("div"),P.c(),y=m(),T=a("span"),T.textContent="email",w=m(),O=a("td"),O.innerHTML='<span class="label">String</span>',g=m(),D=a("td"),D.textContent="Auth record email address.",V=m(),L=a("tr"),L.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<span>emailVisibility</span></div></td>
|
||||
<td><span class="label">Boolean</span></td>
|
||||
<td>Whether to show/hide the auth record email when fetching the record data.</td>`,I=m(),j=a("tr"),j.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>Auth record password.</td>`,F=m(),S=a("tr"),S.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<td>Auth record password.</td>`,B=m(),S=a("tr"),S.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>passwordConfirm</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>Auth record password confirmation.</td>`,N=m(),q=a("tr"),q.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
@@ -17,8 +17,8 @@ import{S as Ht,i as Lt,s as Pt,C as Q,M as At,e as a,w as k,b as m,c as Pe,f as
|
||||
<td><span class="label">Boolean</span></td>
|
||||
<td>Indicates whether the auth record is verified or not.
|
||||
<br/>
|
||||
This field can be set only by admins or auth records with "Manage" access.</td>`,C=m(),_=a("tr"),_.innerHTML='<td colspan="3" class="txt-hint">Schema fields</td>',h(f,"class","inline-flex")},m(u,$){r(u,e,$),r(u,l,$),r(u,s,$),r(u,b,$),r(u,p,$),n(p,c),n(c,f),P.m(f,null),n(f,y),n(f,T),n(p,w),n(p,M),n(p,g),n(p,D),r(u,V,$),r(u,L,$),r(u,I,$),r(u,j,$),r(u,F,$),r(u,S,$),r(u,N,$),r(u,q,$),r(u,C,$),r(u,_,$)},p(u,$){z!==(z=O(u))&&(P.d(1),P=z(u),P&&(P.c(),P.m(f,y)))},d(u){u&&d(e),u&&d(l),u&&d(s),u&&d(b),u&&d(p),P.d(),u&&d(V),u&&d(L),u&&d(I),u&&d(j),u&&d(F),u&&d(S),u&&d(N),u&&d(q),u&&d(C),u&&d(_)}}}function Vt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function It(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Jt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Et(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Ut(o){var p;let e,l=((p=o[12].options)==null?void 0:p.maxSelect)===1?"id":"ids",s,b;return{c(){e=k("Relation record "),s=k(l),b=k(".")},m(c,f){r(c,e,f),r(c,s,f),r(c,b,f)},p(c,f){var y;f&1&&l!==(l=((y=c[12].options)==null?void 0:y.maxSelect)===1?"id":"ids")&&x(s,l)},d(c){c&&d(e),c&&d(s),c&&d(b)}}}function Qt(o){let e,l,s,b,p;return{c(){e=k("File object."),l=a("br"),s=k(`
|
||||
Set to `),b=a("code"),b.textContent="null",p=k(" to delete already uploaded file(s).")},m(c,f){r(c,e,f),r(c,l,f),r(c,s,f),r(c,b,f),r(c,p,f)},p:le,d(c){c&&d(e),c&&d(l),c&&d(s),c&&d(b),c&&d(p)}}}function zt(o){let e;return{c(){e=k("URL address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Kt(o){let e;return{c(){e=k("Email address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Wt(o){let e;return{c(){e=k("JSON array or object.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Yt(o){let e;return{c(){e=k("Number value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Gt(o){let e;return{c(){e=k("Plain text value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function qt(o,e){let l,s,b,p,c,f=e[12].name+"",y,T,w,M,g=Q.getFieldValueType(e[12])+"",D,V,L,I;function j(_,O){return _[12].required?Et:Jt}let F=j(e),S=F(e);function N(_,O){if(_[12].type==="text")return Gt;if(_[12].type==="number")return Yt;if(_[12].type==="json")return Wt;if(_[12].type==="email")return Kt;if(_[12].type==="url")return zt;if(_[12].type==="file")return Qt;if(_[12].type==="relation")return Ut}let q=N(e),C=q&&q(e);return{key:o,first:null,c(){l=a("tr"),s=a("td"),b=a("div"),S.c(),p=m(),c=a("span"),y=k(f),T=m(),w=a("td"),M=a("span"),D=k(g),V=m(),L=a("td"),C&&C.c(),I=m(),h(b,"class","inline-flex"),h(M,"class","label"),this.first=l},m(_,O){r(_,l,O),n(l,s),n(s,b),S.m(b,null),n(b,p),n(b,c),n(c,y),n(l,T),n(l,w),n(w,M),n(M,D),n(l,V),n(l,L),C&&C.m(L,null),n(l,I)},p(_,O){e=_,F!==(F=j(e))&&(S.d(1),S=F(e),S&&(S.c(),S.m(b,p))),O&1&&f!==(f=e[12].name+"")&&x(y,f),O&1&&g!==(g=Q.getFieldValueType(e[12])+"")&&x(D,g),q===(q=N(e))&&C?C.p(e,O):(C&&C.d(1),C=q&&q(e),C&&(C.c(),C.m(L,null)))},d(_){_&&d(l),S.d(),C&&C.d()}}}function Mt(o,e){let l,s=e[7].code+"",b,p,c,f;function y(){return e[6](e[7])}return{key:o,first:null,c(){l=a("button"),b=k(s),p=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(T,w){r(T,l,w),n(l,b),n(l,p),c||(f=Dt(l,"click",y),c=!0)},p(T,w){e=T,w&4&&s!==(s=e[7].code+"")&&x(b,s),w&6&&ue(l,"active",e[1]===e[7].code)},d(T){T&&d(l),c=!1,f()}}}function Ot(o,e){let l,s,b,p;return s=new At({props:{content:e[7].body}}),{key:o,first:null,c(){l=a("div"),Pe(s.$$.fragment),b=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(c,f){r(c,l,f),Be(s,l,null),n(l,b),p=!0},p(c,f){e=c;const y={};f&4&&(y.content=e[7].body),s.$set(y),(!p||f&6)&&ue(l,"active",e[1]===e[7].code)},i(c){p||(fe(s.$$.fragment,c),p=!0)},o(c){pe(s.$$.fragment,c),p=!1},d(c){c&&d(l),Fe(s)}}}function Xt(o){var st,it,at,ot,rt,dt,ct,ft;let e,l,s=o[0].name+"",b,p,c,f,y,T,w,M=o[0].name+"",g,D,V,L,I,j,F,S,N,q,C,_,O,z,P,u,$,ee,K=o[0].name+"",me,Re,ge,be,ne,_e,W,ke,je,J,ye,De,ve,E=[],Ne=new Map,he,se,we,Y,Ce,Ve,Se,G,$e,Ie,Te,Je,A,Ee,te,Ue,Qe,ze,qe,Ke,Me,We,Ye,Ge,Oe,Xe,Ae,ie,He,X,ae,U=[],Ze=new Map,xe,oe,R=[],et=new Map,Z;S=new Nt({props:{js:`
|
||||
This field can be set only by admins or auth records with "Manage" access.</td>`,C=m(),_=a("tr"),_.innerHTML='<td colspan="3" class="txt-hint">Schema fields</td>',h(f,"class","inline-flex")},m(u,$){r(u,e,$),r(u,l,$),r(u,s,$),r(u,b,$),r(u,p,$),n(p,c),n(c,f),P.m(f,null),n(f,y),n(f,T),n(p,w),n(p,O),n(p,g),n(p,D),r(u,V,$),r(u,L,$),r(u,I,$),r(u,j,$),r(u,B,$),r(u,S,$),r(u,N,$),r(u,q,$),r(u,C,$),r(u,_,$)},p(u,$){z!==(z=M(u))&&(P.d(1),P=z(u),P&&(P.c(),P.m(f,y)))},d(u){u&&d(e),u&&d(l),u&&d(s),u&&d(b),u&&d(p),P.d(),u&&d(V),u&&d(L),u&&d(I),u&&d(j),u&&d(B),u&&d(S),u&&d(N),u&&d(q),u&&d(C),u&&d(_)}}}function Vt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function It(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Jt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Et(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Ut(o){var p;let e,l=((p=o[12].options)==null?void 0:p.maxSelect)===1?"id":"ids",s,b;return{c(){e=k("Relation record "),s=k(l),b=k(".")},m(c,f){r(c,e,f),r(c,s,f),r(c,b,f)},p(c,f){var y;f&1&&l!==(l=((y=c[12].options)==null?void 0:y.maxSelect)===1?"id":"ids")&&x(s,l)},d(c){c&&d(e),c&&d(s),c&&d(b)}}}function Qt(o){let e,l,s,b,p;return{c(){e=k("File object."),l=a("br"),s=k(`
|
||||
Set to `),b=a("code"),b.textContent="null",p=k(" to delete already uploaded file(s).")},m(c,f){r(c,e,f),r(c,l,f),r(c,s,f),r(c,b,f),r(c,p,f)},p:le,d(c){c&&d(e),c&&d(l),c&&d(s),c&&d(b),c&&d(p)}}}function zt(o){let e;return{c(){e=k("URL address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Kt(o){let e;return{c(){e=k("Email address.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Wt(o){let e;return{c(){e=k("JSON array or object.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Yt(o){let e;return{c(){e=k("Number value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function Gt(o){let e;return{c(){e=k("Plain text value.")},m(l,s){r(l,e,s)},p:le,d(l){l&&d(e)}}}function qt(o,e){let l,s,b,p,c,f=e[12].name+"",y,T,w,O,g=Q.getFieldValueType(e[12])+"",D,V,L,I;function j(_,M){return _[12].required?Et:Jt}let B=j(e),S=B(e);function N(_,M){if(_[12].type==="text")return Gt;if(_[12].type==="number")return Yt;if(_[12].type==="json")return Wt;if(_[12].type==="email")return Kt;if(_[12].type==="url")return zt;if(_[12].type==="file")return Qt;if(_[12].type==="relation")return Ut}let q=N(e),C=q&&q(e);return{key:o,first:null,c(){l=a("tr"),s=a("td"),b=a("div"),S.c(),p=m(),c=a("span"),y=k(f),T=m(),w=a("td"),O=a("span"),D=k(g),V=m(),L=a("td"),C&&C.c(),I=m(),h(b,"class","inline-flex"),h(O,"class","label"),this.first=l},m(_,M){r(_,l,M),n(l,s),n(s,b),S.m(b,null),n(b,p),n(b,c),n(c,y),n(l,T),n(l,w),n(w,O),n(O,D),n(l,V),n(l,L),C&&C.m(L,null),n(l,I)},p(_,M){e=_,B!==(B=j(e))&&(S.d(1),S=B(e),S&&(S.c(),S.m(b,p))),M&1&&f!==(f=e[12].name+"")&&x(y,f),M&1&&g!==(g=Q.getFieldValueType(e[12])+"")&&x(D,g),q===(q=N(e))&&C?C.p(e,M):(C&&C.d(1),C=q&&q(e),C&&(C.c(),C.m(L,null)))},d(_){_&&d(l),S.d(),C&&C.d()}}}function Ot(o,e){let l,s=e[7].code+"",b,p,c,f;function y(){return e[6](e[7])}return{key:o,first:null,c(){l=a("button"),b=k(s),p=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(T,w){r(T,l,w),n(l,b),n(l,p),c||(f=Dt(l,"click",y),c=!0)},p(T,w){e=T,w&4&&s!==(s=e[7].code+"")&&x(b,s),w&6&&ue(l,"active",e[1]===e[7].code)},d(T){T&&d(l),c=!1,f()}}}function Mt(o,e){let l,s,b,p;return s=new At({props:{content:e[7].body}}),{key:o,first:null,c(){l=a("div"),Pe(s.$$.fragment),b=m(),h(l,"class","tab-item"),ue(l,"active",e[1]===e[7].code),this.first=l},m(c,f){r(c,l,f),Re(s,l,null),n(l,b),p=!0},p(c,f){e=c;const y={};f&4&&(y.content=e[7].body),s.$set(y),(!p||f&6)&&ue(l,"active",e[1]===e[7].code)},i(c){p||(fe(s.$$.fragment,c),p=!0)},o(c){pe(s.$$.fragment,c),p=!1},d(c){c&&d(l),Be(s)}}}function Xt(o){var st,it,at,ot,rt,dt,ct,ft;let e,l,s=o[0].name+"",b,p,c,f,y,T,w,O=o[0].name+"",g,D,V,L,I,j,B,S,N,q,C,_,M,z,P,u,$,ee,K=o[0].name+"",me,Fe,ge,be,ne,_e,W,ke,je,J,ye,De,ve,E=[],Ne=new Map,he,se,we,Y,Ce,Ve,Se,G,$e,Ie,Te,Je,A,Ee,te,Ue,Qe,ze,qe,Ke,Oe,We,Ye,Ge,Me,Xe,Ae,ie,He,X,ae,U=[],Ze=new Map,xe,oe,F=[],et=new Map,Z;S=new Nt({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[4]}');
|
||||
@@ -46,12 +46,12 @@ final record = await pb.collection('${(ot=o[0])==null?void 0:ot.name}').create(b
|
||||
`+((rt=o[0])!=null&&rt.isAuth?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(dt=o[0])==null?void 0:dt.name}').requestVerification('test@example.com');
|
||||
`:"")}});let B=o[5]&&$t(),H=((ct=o[0])==null?void 0:ct.isAuth)&&Tt(o),de=(ft=o[0])==null?void 0:ft.schema;const tt=t=>t[12].name;for(let t=0;t<de.length;t+=1){let i=St(o,de,t),v=tt(i);Ne.set(v,E[t]=qt(v,i))}te=new At({props:{content:"?expand=relField1,relField2.subRelField"}});let ce=o[2];const lt=t=>t[7].code;for(let t=0;t<ce.length;t+=1){let i=Ct(o,ce,t),v=lt(i);Ze.set(v,U[t]=Mt(v,i))}let re=o[2];const nt=t=>t[7].code;for(let t=0;t<re.length;t+=1){let i=wt(o,re,t),v=nt(i);et.set(v,R[t]=Ot(v,i))}return{c(){e=a("h3"),l=k("Create ("),b=k(s),p=k(")"),c=m(),f=a("div"),y=a("p"),T=k("Create a new "),w=a("strong"),g=k(M),D=k(" record."),V=m(),L=a("p"),L.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
`:"")}});let R=o[5]&&$t(),H=((ct=o[0])==null?void 0:ct.isAuth)&&Tt(o),de=(ft=o[0])==null?void 0:ft.schema;const tt=t=>t[12].name;for(let t=0;t<de.length;t+=1){let i=St(o,de,t),v=tt(i);Ne.set(v,E[t]=qt(v,i))}te=new At({props:{content:"?expand=relField1,relField2.subRelField"}});let ce=o[2];const lt=t=>t[7].code;for(let t=0;t<ce.length;t+=1){let i=Ct(o,ce,t),v=lt(i);Ze.set(v,U[t]=Ot(v,i))}let re=o[2];const nt=t=>t[7].code;for(let t=0;t<re.length;t+=1){let i=wt(o,re,t),v=nt(i);et.set(v,F[t]=Mt(v,i))}return{c(){e=a("h3"),l=k("Create ("),b=k(s),p=k(")"),c=m(),f=a("div"),y=a("p"),T=k("Create a new "),w=a("strong"),g=k(O),D=k(" record."),V=m(),L=a("p"),L.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,I=m(),j=a("p"),j.innerHTML=`File upload is supported only via <code>multipart/form-data</code>.
|
||||
<br/>
|
||||
For more info and examples you could check the detailed
|
||||
<a href="https://pocketbase.io/docs/files-handling/" target="_blank" rel="noopener noreferrer">Files upload and handling docs
|
||||
</a>.`,F=m(),Pe(S.$$.fragment),N=m(),q=a("h6"),q.textContent="API details",C=m(),_=a("div"),O=a("strong"),O.textContent="POST",z=m(),P=a("div"),u=a("p"),$=k("/api/collections/"),ee=a("strong"),me=k(K),Re=k("/records"),ge=m(),B&&B.c(),be=m(),ne=a("div"),ne.textContent="Body Parameters",_e=m(),W=a("table"),ke=a("thead"),ke.innerHTML=`<tr><th>Param</th>
|
||||
</a>.`,B=m(),Pe(S.$$.fragment),N=m(),q=a("h6"),q.textContent="API details",C=m(),_=a("div"),M=a("strong"),M.textContent="POST",z=m(),P=a("div"),u=a("p"),$=k("/api/collections/"),ee=a("strong"),me=k(K),Fe=k("/records"),ge=m(),R&&R.c(),be=m(),ne=a("div"),ne.textContent="Body Parameters",_e=m(),W=a("table"),ke=a("thead"),ke.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr>`,je=m(),J=a("tbody"),ye=a("tr"),ye.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<span>id</span></div></td>
|
||||
@@ -64,9 +64,9 @@ await pb.collection('${(dt=o[0])==null?void 0:dt.name}').requestVerification('te
|
||||
`),Pe(te.$$.fragment),Ue=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Qe=a("br"),ze=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),qe=a("code"),qe.textContent="expand",Ke=k(" property (eg. "),Me=a("code"),Me.textContent='"expand": {"relField1": {...}, ...}',We=k(`).
|
||||
`),qe=a("code"),qe.textContent="expand",Ke=k(" property (eg. "),Oe=a("code"),Oe.textContent='"expand": {"relField1": {...}, ...}',We=k(`).
|
||||
`),Ye=a("br"),Ge=k(`
|
||||
Only the relations to which the request user has permissions to `),Oe=a("strong"),Oe.textContent="view",Xe=k(" will be expanded."),Ae=m(),ie=a("div"),ie.textContent="Responses",He=m(),X=a("div"),ae=a("div");for(let t=0;t<U.length;t+=1)U[t].c();xe=m(),oe=a("div");for(let t=0;t<R.length;t+=1)R[t].c();h(e,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(q,"class","m-b-xs"),h(O,"class","label label-primary"),h(P,"class","content"),h(_,"class","alert alert-success"),h(ne,"class","section-title"),h(W,"class","table-compact table-border m-b-base"),h(se,"class","section-title"),h(Y,"class","table-compact table-border m-b-base"),h(ie,"class","section-title"),h(ae,"class","tabs-header compact left"),h(oe,"class","tabs-content"),h(X,"class","tabs")},m(t,i){r(t,e,i),n(e,l),n(e,b),n(e,p),r(t,c,i),r(t,f,i),n(f,y),n(y,T),n(y,w),n(w,g),n(y,D),n(f,V),n(f,L),n(f,I),n(f,j),r(t,F,i),Be(S,t,i),r(t,N,i),r(t,q,i),r(t,C,i),r(t,_,i),n(_,O),n(_,z),n(_,P),n(P,u),n(u,$),n(u,ee),n(ee,me),n(u,Re),n(_,ge),B&&B.m(_,null),r(t,be,i),r(t,ne,i),r(t,_e,i),r(t,W,i),n(W,ke),n(W,je),n(W,J),n(J,ye),n(J,De),H&&H.m(J,null),n(J,ve);for(let v=0;v<E.length;v+=1)E[v].m(J,null);r(t,he,i),r(t,se,i),r(t,we,i),r(t,Y,i),n(Y,Ce),n(Y,Ve),n(Y,Se),n(Se,G),n(G,$e),n(G,Ie),n(G,Te),n(G,Je),n(G,A),n(A,Ee),Be(te,A,null),n(A,Ue),n(A,Qe),n(A,ze),n(A,qe),n(A,Ke),n(A,Me),n(A,We),n(A,Ye),n(A,Ge),n(A,Oe),n(A,Xe),r(t,Ae,i),r(t,ie,i),r(t,He,i),r(t,X,i),n(X,ae);for(let v=0;v<U.length;v+=1)U[v].m(ae,null);n(X,xe),n(X,oe);for(let v=0;v<R.length;v+=1)R[v].m(oe,null);Z=!0},p(t,[i]){var pt,ut,mt,bt,_t,kt,yt,vt;(!Z||i&1)&&s!==(s=t[0].name+"")&&x(b,s),(!Z||i&1)&&M!==(M=t[0].name+"")&&x(g,M);const v={};i&25&&(v.js=`
|
||||
Only the relations to which the request user has permissions to `),Me=a("strong"),Me.textContent="view",Xe=k(" will be expanded."),Ae=m(),ie=a("div"),ie.textContent="Responses",He=m(),X=a("div"),ae=a("div");for(let t=0;t<U.length;t+=1)U[t].c();xe=m(),oe=a("div");for(let t=0;t<F.length;t+=1)F[t].c();h(e,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(q,"class","m-b-xs"),h(M,"class","label label-primary"),h(P,"class","content"),h(_,"class","alert alert-success"),h(ne,"class","section-title"),h(W,"class","table-compact table-border m-b-base"),h(se,"class","section-title"),h(Y,"class","table-compact table-border m-b-base"),h(ie,"class","section-title"),h(ae,"class","tabs-header compact left"),h(oe,"class","tabs-content"),h(X,"class","tabs")},m(t,i){r(t,e,i),n(e,l),n(e,b),n(e,p),r(t,c,i),r(t,f,i),n(f,y),n(y,T),n(y,w),n(w,g),n(y,D),n(f,V),n(f,L),n(f,I),n(f,j),r(t,B,i),Re(S,t,i),r(t,N,i),r(t,q,i),r(t,C,i),r(t,_,i),n(_,M),n(_,z),n(_,P),n(P,u),n(u,$),n(u,ee),n(ee,me),n(u,Fe),n(_,ge),R&&R.m(_,null),r(t,be,i),r(t,ne,i),r(t,_e,i),r(t,W,i),n(W,ke),n(W,je),n(W,J),n(J,ye),n(J,De),H&&H.m(J,null),n(J,ve);for(let v=0;v<E.length;v+=1)E[v].m(J,null);r(t,he,i),r(t,se,i),r(t,we,i),r(t,Y,i),n(Y,Ce),n(Y,Ve),n(Y,Se),n(Se,G),n(G,$e),n(G,Ie),n(G,Te),n(G,Je),n(G,A),n(A,Ee),Re(te,A,null),n(A,Ue),n(A,Qe),n(A,ze),n(A,qe),n(A,Ke),n(A,Oe),n(A,We),n(A,Ye),n(A,Ge),n(A,Me),n(A,Xe),r(t,Ae,i),r(t,ie,i),r(t,He,i),r(t,X,i),n(X,ae);for(let v=0;v<U.length;v+=1)U[v].m(ae,null);n(X,xe),n(X,oe);for(let v=0;v<F.length;v+=1)F[v].m(oe,null);Z=!0},p(t,[i]){var pt,ut,mt,bt,_t,kt,yt,vt;(!Z||i&1)&&s!==(s=t[0].name+"")&&x(b,s),(!Z||i&1)&&O!==(O=t[0].name+"")&&x(g,O);const v={};i&25&&(v.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${t[4]}');
|
||||
@@ -94,12 +94,12 @@ final record = await pb.collection('${(bt=t[0])==null?void 0:bt.name}').create(b
|
||||
`+((_t=t[0])!=null&&_t.isAuth?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(kt=t[0])==null?void 0:kt.name}').requestVerification('test@example.com');
|
||||
`:"")),S.$set(v),(!Z||i&1)&&K!==(K=t[0].name+"")&&x(me,K),t[5]?B||(B=$t(),B.c(),B.m(_,null)):B&&(B.d(1),B=null),(yt=t[0])!=null&&yt.isAuth?H?H.p(t,i):(H=Tt(t),H.c(),H.m(J,ve)):H&&(H.d(1),H=null),i&1&&(de=(vt=t[0])==null?void 0:vt.schema,E=Le(E,i,tt,1,t,de,Ne,J,ht,qt,null,St)),i&6&&(ce=t[2],U=Le(U,i,lt,1,t,ce,Ze,ae,ht,Mt,null,Ct)),i&6&&(re=t[2],Bt(),R=Le(R,i,nt,1,t,re,et,oe,Ft,Ot,null,wt),Rt())},i(t){if(!Z){fe(S.$$.fragment,t),fe(te.$$.fragment,t);for(let i=0;i<re.length;i+=1)fe(R[i]);Z=!0}},o(t){pe(S.$$.fragment,t),pe(te.$$.fragment,t);for(let i=0;i<R.length;i+=1)pe(R[i]);Z=!1},d(t){t&&d(e),t&&d(c),t&&d(f),t&&d(F),Fe(S,t),t&&d(N),t&&d(q),t&&d(C),t&&d(_),B&&B.d(),t&&d(be),t&&d(ne),t&&d(_e),t&&d(W),H&&H.d();for(let i=0;i<E.length;i+=1)E[i].d();t&&d(he),t&&d(se),t&&d(we),t&&d(Y),Fe(te),t&&d(Ae),t&&d(ie),t&&d(He),t&&d(X);for(let i=0;i<U.length;i+=1)U[i].d();for(let i=0;i<R.length;i+=1)R[i].d()}}}function Zt(o,e,l){let s,b,{collection:p=new gt}=e,c=200,f=[],y={};const T=w=>l(1,c=w.code);return o.$$set=w=>{"collection"in w&&l(0,p=w.collection)},o.$$.update=()=>{var w,M;o.$$.dirty&1&&l(5,s=(p==null?void 0:p.createRule)===null),o.$$.dirty&1&&l(2,f=[{code:200,body:JSON.stringify(Q.dummyCollectionRecord(p),null,2)},{code:400,body:`
|
||||
`:"")),S.$set(v),(!Z||i&1)&&K!==(K=t[0].name+"")&&x(me,K),t[5]?R||(R=$t(),R.c(),R.m(_,null)):R&&(R.d(1),R=null),(yt=t[0])!=null&&yt.isAuth?H?H.p(t,i):(H=Tt(t),H.c(),H.m(J,ve)):H&&(H.d(1),H=null),i&1&&(de=(vt=t[0])==null?void 0:vt.schema,E=Le(E,i,tt,1,t,de,Ne,J,ht,qt,null,St)),i&6&&(ce=t[2],U=Le(U,i,lt,1,t,ce,Ze,ae,ht,Ot,null,Ct)),i&6&&(re=t[2],Rt(),F=Le(F,i,nt,1,t,re,et,oe,Bt,Mt,null,wt),Ft())},i(t){if(!Z){fe(S.$$.fragment,t),fe(te.$$.fragment,t);for(let i=0;i<re.length;i+=1)fe(F[i]);Z=!0}},o(t){pe(S.$$.fragment,t),pe(te.$$.fragment,t);for(let i=0;i<F.length;i+=1)pe(F[i]);Z=!1},d(t){t&&d(e),t&&d(c),t&&d(f),t&&d(B),Be(S,t),t&&d(N),t&&d(q),t&&d(C),t&&d(_),R&&R.d(),t&&d(be),t&&d(ne),t&&d(_e),t&&d(W),H&&H.d();for(let i=0;i<E.length;i+=1)E[i].d();t&&d(he),t&&d(se),t&&d(we),t&&d(Y),Be(te),t&&d(Ae),t&&d(ie),t&&d(He),t&&d(X);for(let i=0;i<U.length;i+=1)U[i].d();for(let i=0;i<F.length;i+=1)F[i].d()}}}function Zt(o,e,l){let s,b,{collection:p=new gt}=e,c=200,f=[],y={};const T=w=>l(1,c=w.code);return o.$$set=w=>{"collection"in w&&l(0,p=w.collection)},o.$$.update=()=>{var w,O;o.$$.dirty&1&&l(5,s=(p==null?void 0:p.createRule)===null),o.$$.dirty&1&&l(2,f=[{code:200,body:JSON.stringify(Q.dummyCollectionRecord(p),null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to create record.",
|
||||
"data": {
|
||||
"${(M=(w=p==null?void 0:p.schema)==null?void 0:w[0])==null?void 0:M.name}": {
|
||||
"${(O=(w=p==null?void 0:p.schema)==null?void 0:w[0])==null?void 0:O.name}": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n,m as we,x,N as _e,O as Ee,k as Oe,P as Te,n as Be,t as ee,a as te,o as f,d as ge,Q as Ie,C as Me,p as Ae,r as N,u as Se,M as qe}from"./index.2a48c906.js";import{S as He}from"./SdkTabs.9dbb2ca3.js";function ke(o,l,s){const a=o.slice();return a[6]=l[s],a}function he(o,l,s){const a=o.slice();return a[6]=l[s],a}function ve(o){let l;return{c(){l=c("p"),l.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,a){d(s,l,a)},d(s){s&&f(l)}}}function ye(o,l){let s,a=l[6].code+"",h,i,r,u;function $(){return l[5](l[6])}return{key:o,first:null,c(){s=c("button"),h=D(a),i=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(b,g){d(b,s,g),n(s,h),n(s,i),r||(u=Se(s,"click",$),r=!0)},p(b,g){l=b,g&20&&N(s,"active",l[2]===l[6].code)},d(b){b&&f(s),r=!1,u()}}}function De(o,l){let s,a,h,i;return a=new qe({props:{content:l[6].body}}),{key:o,first:null,c(){s=c("div"),$e(a.$$.fragment),h=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(r,u){d(r,s,u),we(a,s,null),n(s,h),i=!0},p(r,u){l=r,(!i||u&20)&&N(s,"active",l[2]===l[6].code)},i(r){i||(ee(a.$$.fragment,r),i=!0)},o(r){te(a.$$.fragment,r),i=!1},d(r){r&&f(s),ge(a)}}}function Le(o){var ue,pe;let l,s,a=o[0].name+"",h,i,r,u,$,b,g,q=o[0].name+"",z,le,F,C,K,O,Q,y,H,se,L,E,oe,G,U=o[0].name+"",J,ae,V,ne,W,T,X,B,Y,I,Z,R,M,w=[],ie=new Map,re,A,v=[],ce=new Map,P;C=new He({props:{js:`
|
||||
import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n,m as we,x,O as _e,P as Ee,k as Oe,Q as Te,n as Be,t as ee,a as te,o as f,d as ge,R as Ie,C as Ae,p as Me,r as N,u as Se,N as qe}from"./index.536fddcb.js";import{S as He}from"./SdkTabs.3571c26b.js";function ke(o,l,s){const a=o.slice();return a[6]=l[s],a}function he(o,l,s){const a=o.slice();return a[6]=l[s],a}function ve(o){let l;return{c(){l=c("p"),l.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,a){d(s,l,a)},d(s){s&&f(l)}}}function ye(o,l){let s,a=l[6].code+"",h,i,r,u;function $(){return l[5](l[6])}return{key:o,first:null,c(){s=c("button"),h=D(a),i=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(b,g){d(b,s,g),n(s,h),n(s,i),r||(u=Se(s,"click",$),r=!0)},p(b,g){l=b,g&20&&N(s,"active",l[2]===l[6].code)},d(b){b&&f(s),r=!1,u()}}}function De(o,l){let s,a,h,i;return a=new qe({props:{content:l[6].body}}),{key:o,first:null,c(){s=c("div"),$e(a.$$.fragment),h=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(r,u){d(r,s,u),we(a,s,null),n(s,h),i=!0},p(r,u){l=r,(!i||u&20)&&N(s,"active",l[2]===l[6].code)},i(r){i||(ee(a.$$.fragment,r),i=!0)},o(r){te(a.$$.fragment,r),i=!1},d(r){r&&f(s),ge(a)}}}function Le(o){var ue,pe;let l,s,a=o[0].name+"",h,i,r,u,$,b,g,q=o[0].name+"",z,le,F,C,K,O,Q,y,H,se,L,E,oe,G,U=o[0].name+"",J,ae,V,ne,W,T,X,B,Y,I,Z,R,A,w=[],ie=new Map,re,M,v=[],ce=new Map,P;C=new He({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
@@ -19,7 +19,7 @@ import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n
|
||||
<th width="60%">Description</th></tr></thead>
|
||||
<tbody><tr><td>id</td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>ID of the record to delete.</td></tr></tbody>`,Y=k(),I=c("div"),I.textContent="Responses",Z=k(),R=c("div"),M=c("div");for(let e=0;e<w.length;e+=1)w[e].c();re=k(),A=c("div");for(let e=0;e<v.length;e+=1)v[e].c();m(l,"class","m-b-sm"),m(u,"class","content txt-lg m-b-sm"),m(O,"class","m-b-xs"),m(H,"class","label label-primary"),m(L,"class","content"),m(y,"class","alert alert-danger"),m(T,"class","section-title"),m(B,"class","table-compact table-border m-b-base"),m(I,"class","section-title"),m(M,"class","tabs-header compact left"),m(A,"class","tabs-content"),m(R,"class","tabs")},m(e,t){d(e,l,t),n(l,s),n(l,h),n(l,i),d(e,r,t),d(e,u,t),n(u,$),n($,b),n($,g),n(g,z),n($,le),d(e,F,t),we(C,e,t),d(e,K,t),d(e,O,t),d(e,Q,t),d(e,y,t),n(y,H),n(y,se),n(y,L),n(L,E),n(E,oe),n(E,G),n(G,J),n(E,ae),n(E,V),n(y,ne),_&&_.m(y,null),d(e,W,t),d(e,T,t),d(e,X,t),d(e,B,t),d(e,Y,t),d(e,I,t),d(e,Z,t),d(e,R,t),n(R,M);for(let p=0;p<w.length;p+=1)w[p].m(M,null);n(R,re),n(R,A);for(let p=0;p<v.length;p+=1)v[p].m(A,null);P=!0},p(e,[t]){var me,be;(!P||t&1)&&a!==(a=e[0].name+"")&&x(h,a),(!P||t&1)&&q!==(q=e[0].name+"")&&x(z,q);const p={};t&9&&(p.js=`
|
||||
<td>ID of the record to delete.</td></tr></tbody>`,Y=k(),I=c("div"),I.textContent="Responses",Z=k(),R=c("div"),A=c("div");for(let e=0;e<w.length;e+=1)w[e].c();re=k(),M=c("div");for(let e=0;e<v.length;e+=1)v[e].c();m(l,"class","m-b-sm"),m(u,"class","content txt-lg m-b-sm"),m(O,"class","m-b-xs"),m(H,"class","label label-primary"),m(L,"class","content"),m(y,"class","alert alert-danger"),m(T,"class","section-title"),m(B,"class","table-compact table-border m-b-base"),m(I,"class","section-title"),m(A,"class","tabs-header compact left"),m(M,"class","tabs-content"),m(R,"class","tabs")},m(e,t){d(e,l,t),n(l,s),n(l,h),n(l,i),d(e,r,t),d(e,u,t),n(u,$),n($,b),n($,g),n(g,z),n($,le),d(e,F,t),we(C,e,t),d(e,K,t),d(e,O,t),d(e,Q,t),d(e,y,t),n(y,H),n(y,se),n(y,L),n(L,E),n(E,oe),n(E,G),n(G,J),n(E,ae),n(E,V),n(y,ne),_&&_.m(y,null),d(e,W,t),d(e,T,t),d(e,X,t),d(e,B,t),d(e,Y,t),d(e,I,t),d(e,Z,t),d(e,R,t),n(R,A);for(let p=0;p<w.length;p+=1)w[p].m(A,null);n(R,re),n(R,M);for(let p=0;p<v.length;p+=1)v[p].m(M,null);P=!0},p(e,[t]){var me,be;(!P||t&1)&&a!==(a=e[0].name+"")&&x(h,a),(!P||t&1)&&q!==(q=e[0].name+"")&&x(z,q);const p={};t&9&&(p.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -35,7 +35,7 @@ import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n
|
||||
...
|
||||
|
||||
await pb.collection('${(be=e[0])==null?void 0:be.name}').delete('RECORD_ID');
|
||||
`),C.$set(p),(!P||t&1)&&U!==(U=e[0].name+"")&&x(J,U),e[1]?_||(_=ve(),_.c(),_.m(y,null)):_&&(_.d(1),_=null),t&20&&(j=e[4],w=_e(w,t,de,1,e,j,ie,M,Ee,ye,null,he)),t&20&&(S=e[4],Oe(),v=_e(v,t,fe,1,e,S,ce,A,Te,De,null,ke),Be())},i(e){if(!P){ee(C.$$.fragment,e);for(let t=0;t<S.length;t+=1)ee(v[t]);P=!0}},o(e){te(C.$$.fragment,e);for(let t=0;t<v.length;t+=1)te(v[t]);P=!1},d(e){e&&f(l),e&&f(r),e&&f(u),e&&f(F),ge(C,e),e&&f(K),e&&f(O),e&&f(Q),e&&f(y),_&&_.d(),e&&f(W),e&&f(T),e&&f(X),e&&f(B),e&&f(Y),e&&f(I),e&&f(Z),e&&f(R);for(let t=0;t<w.length;t+=1)w[t].d();for(let t=0;t<v.length;t+=1)v[t].d()}}}function Ue(o,l,s){let a,h,{collection:i=new Ie}=l,r=204,u=[];const $=b=>s(2,r=b.code);return o.$$set=b=>{"collection"in b&&s(0,i=b.collection)},o.$$.update=()=>{o.$$.dirty&1&&s(1,a=(i==null?void 0:i.deleteRule)===null),o.$$.dirty&3&&i!=null&&i.id&&(u.push({code:204,body:`
|
||||
`),C.$set(p),(!P||t&1)&&U!==(U=e[0].name+"")&&x(J,U),e[1]?_||(_=ve(),_.c(),_.m(y,null)):_&&(_.d(1),_=null),t&20&&(j=e[4],w=_e(w,t,de,1,e,j,ie,A,Ee,ye,null,he)),t&20&&(S=e[4],Oe(),v=_e(v,t,fe,1,e,S,ce,M,Te,De,null,ke),Be())},i(e){if(!P){ee(C.$$.fragment,e);for(let t=0;t<S.length;t+=1)ee(v[t]);P=!0}},o(e){te(C.$$.fragment,e);for(let t=0;t<v.length;t+=1)te(v[t]);P=!1},d(e){e&&f(l),e&&f(r),e&&f(u),e&&f(F),ge(C,e),e&&f(K),e&&f(O),e&&f(Q),e&&f(y),_&&_.d(),e&&f(W),e&&f(T),e&&f(X),e&&f(B),e&&f(Y),e&&f(I),e&&f(Z),e&&f(R);for(let t=0;t<w.length;t+=1)w[t].d();for(let t=0;t<v.length;t+=1)v[t].d()}}}function Ue(o,l,s){let a,h,{collection:i=new Ie}=l,r=204,u=[];const $=b=>s(2,r=b.code);return o.$$set=b=>{"collection"in b&&s(0,i=b.collection)},o.$$.update=()=>{o.$$.dirty&1&&s(1,a=(i==null?void 0:i.deleteRule)===null),o.$$.dirty&3&&i!=null&&i.id&&(u.push({code:204,body:`
|
||||
null
|
||||
`}),u.push({code:400,body:`
|
||||
{
|
||||
@@ -55,4 +55,4 @@ import{S as Ce,i as Re,s as Pe,e as c,w as D,b as k,c as $e,f as m,g as d,h as n
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},s(3,h=Me.getApiExampleUrl(Ae.baseUrl)),[i,a,r,h,u,$]}class ze extends Ce{constructor(l){super(),Re(this,l,Ue,Le,Pe,{collection:0})}}export{ze as default};
|
||||
`}))},s(3,h=Ae.getApiExampleUrl(Me.baseUrl)),[i,a,r,h,u,$]}class ze extends Ce{constructor(l){super(),Re(this,l,Ue,Le,Pe,{collection:0})}}export{ze as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+136
@@ -0,0 +1,136 @@
|
||||
import{S as Se,i as Ne,s as qe,e,b as s,E as He,f as o,g as u,u as De,y as Fe,o as m,w as _,h as t,N as he,c as Yt,m as Zt,x as we,O as Le,P as Me,k as Ie,Q as Be,n as Ge,t as It,a as Bt,d as te,R as ze,C as _e,p as Ue,r as xe}from"./index.536fddcb.js";import{S as je}from"./SdkTabs.3571c26b.js";function Qe(d){let n,a,r;return{c(){n=e("span"),n.textContent="Show details",a=s(),r=e("i"),o(n,"class","txt"),o(r,"class","ri-arrow-down-s-line")},m(f,p){u(f,n,p),u(f,a,p),u(f,r,p)},d(f){f&&m(n),f&&m(a),f&&m(r)}}}function Je(d){let n,a,r;return{c(){n=e("span"),n.textContent="Hide details",a=s(),r=e("i"),o(n,"class","txt"),o(r,"class","ri-arrow-up-s-line")},m(f,p){u(f,n,p),u(f,a,p),u(f,r,p)},d(f){f&&m(n),f&&m(a),f&&m(r)}}}function Ae(d){let n,a,r,f,p,b,x,$,h,w,c,V,bt,Gt,R,zt,q,it,F,W,ee,B,G,le,at,ht,X,xt,se,rt,ct,Y,O,Ut,wt,v,Z,_t,jt,$t,z,tt,Ct,Qt,kt,L,dt,gt,ne,ft,oe,D,yt,et,vt,U,pt,ie,H,Ft,lt,Lt,st,At,nt,j,E,Jt,Tt,Kt,Pt,C,Q,M,ae,Rt,re,ut,ce,I,Ot,de,Et,Vt,St,Wt,A,mt,J,K,S,Nt,fe,T,k,pe,N,y,ot,ue,P,qt,me,Dt,be,Ht,Xt,Mt;return{c(){n=e("p"),n.innerHTML=`The syntax basically follows the format
|
||||
<code><span class="txt-success">OPERAND</span>
|
||||
<span class="txt-danger">OPERATOR</span>
|
||||
<span class="txt-success">OPERAND</span></code>, where:`,a=s(),r=e("ul"),f=e("li"),f.innerHTML=`<code class="txt-success">OPERAND</code> - could be any of the above field literal, string (single
|
||||
or double quoted), number, null, true, false`,p=s(),b=e("li"),x=e("code"),x.textContent="OPERATOR",$=_(` - is one of:
|
||||
`),h=e("br"),w=s(),c=e("ul"),V=e("li"),bt=e("code"),bt.textContent="=",Gt=s(),R=e("span"),R.textContent="Equal",zt=s(),q=e("li"),it=e("code"),it.textContent="!=",F=s(),W=e("span"),W.textContent="NOT equal",ee=s(),B=e("li"),G=e("code"),G.textContent=">",le=s(),at=e("span"),at.textContent="Greater than",ht=s(),X=e("li"),xt=e("code"),xt.textContent=">=",se=s(),rt=e("span"),rt.textContent="Greater than or equal",ct=s(),Y=e("li"),O=e("code"),O.textContent="<",Ut=s(),wt=e("span"),wt.textContent="Less than or equal",v=s(),Z=e("li"),_t=e("code"),_t.textContent="<=",jt=s(),$t=e("span"),$t.textContent="Less than or equal",z=s(),tt=e("li"),Ct=e("code"),Ct.textContent="~",Qt=s(),kt=e("span"),kt.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,L=s(),dt=e("li"),gt=e("code"),gt.textContent="!~",ne=s(),ft=e("span"),ft.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,oe=s(),D=e("li"),yt=e("code"),yt.textContent="?=",et=s(),vt=e("em"),vt.textContent="Any/At least one of",U=s(),pt=e("span"),pt.textContent="Equal",ie=s(),H=e("li"),Ft=e("code"),Ft.textContent="?!=",lt=s(),Lt=e("em"),Lt.textContent="Any/At least one of",st=s(),At=e("span"),At.textContent="NOT equal",nt=s(),j=e("li"),E=e("code"),E.textContent="?>",Jt=s(),Tt=e("em"),Tt.textContent="Any/At least one of",Kt=s(),Pt=e("span"),Pt.textContent="Greater than",C=s(),Q=e("li"),M=e("code"),M.textContent="?>=",ae=s(),Rt=e("em"),Rt.textContent="Any/At least one of",re=s(),ut=e("span"),ut.textContent="Greater than or equal",ce=s(),I=e("li"),Ot=e("code"),Ot.textContent="?<",de=s(),Et=e("em"),Et.textContent="Any/At least one of",Vt=s(),St=e("span"),St.textContent="Less than or equal",Wt=s(),A=e("li"),mt=e("code"),mt.textContent="?<=",J=s(),K=e("em"),K.textContent="Any/At least one of",S=s(),Nt=e("span"),Nt.textContent="Less than or equal",fe=s(),T=e("li"),k=e("code"),k.textContent="?~",pe=s(),N=e("em"),N.textContent="Any/At least one of",y=s(),ot=e("span"),ot.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,ue=s(),P=e("li"),qt=e("code"),qt.textContent="?!~",me=s(),Dt=e("em"),Dt.textContent="Any/At least one of",be=s(),Ht=e("span"),Ht.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,Xt=s(),Mt=e("p"),Mt.innerHTML=`To group and combine several expressions you could use brackets
|
||||
<code>(...)</code>, <code>&&</code> (AND) and <code>||</code> (OR) tokens.`,o(x,"class","txt-danger"),o(bt,"class","filter-op svelte-1w7s5nw"),o(R,"class","txt"),o(it,"class","filter-op svelte-1w7s5nw"),o(W,"class","txt"),o(G,"class","filter-op svelte-1w7s5nw"),o(at,"class","txt"),o(xt,"class","filter-op svelte-1w7s5nw"),o(rt,"class","txt"),o(O,"class","filter-op svelte-1w7s5nw"),o(wt,"class","txt"),o(_t,"class","filter-op svelte-1w7s5nw"),o($t,"class","txt"),o(Ct,"class","filter-op svelte-1w7s5nw"),o(kt,"class","txt"),o(gt,"class","filter-op svelte-1w7s5nw"),o(ft,"class","txt"),o(yt,"class","filter-op svelte-1w7s5nw"),o(vt,"class","txt-hint"),o(pt,"class","txt"),o(Ft,"class","filter-op svelte-1w7s5nw"),o(Lt,"class","txt-hint"),o(At,"class","txt"),o(E,"class","filter-op svelte-1w7s5nw"),o(Tt,"class","txt-hint"),o(Pt,"class","txt"),o(M,"class","filter-op svelte-1w7s5nw"),o(Rt,"class","txt-hint"),o(ut,"class","txt"),o(Ot,"class","filter-op svelte-1w7s5nw"),o(Et,"class","txt-hint"),o(St,"class","txt"),o(mt,"class","filter-op svelte-1w7s5nw"),o(K,"class","txt-hint"),o(Nt,"class","txt"),o(k,"class","filter-op svelte-1w7s5nw"),o(N,"class","txt-hint"),o(ot,"class","txt"),o(qt,"class","filter-op svelte-1w7s5nw"),o(Dt,"class","txt-hint"),o(Ht,"class","txt")},m(l,i){u(l,n,i),u(l,a,i),u(l,r,i),t(r,f),t(r,p),t(r,b),t(b,x),t(b,$),t(b,h),t(b,w),t(b,c),t(c,V),t(V,bt),t(V,Gt),t(V,R),t(c,zt),t(c,q),t(q,it),t(q,F),t(q,W),t(c,ee),t(c,B),t(B,G),t(B,le),t(B,at),t(c,ht),t(c,X),t(X,xt),t(X,se),t(X,rt),t(c,ct),t(c,Y),t(Y,O),t(Y,Ut),t(Y,wt),t(c,v),t(c,Z),t(Z,_t),t(Z,jt),t(Z,$t),t(c,z),t(c,tt),t(tt,Ct),t(tt,Qt),t(tt,kt),t(c,L),t(c,dt),t(dt,gt),t(dt,ne),t(dt,ft),t(c,oe),t(c,D),t(D,yt),t(D,et),t(D,vt),t(D,U),t(D,pt),t(c,ie),t(c,H),t(H,Ft),t(H,lt),t(H,Lt),t(H,st),t(H,At),t(c,nt),t(c,j),t(j,E),t(j,Jt),t(j,Tt),t(j,Kt),t(j,Pt),t(c,C),t(c,Q),t(Q,M),t(Q,ae),t(Q,Rt),t(Q,re),t(Q,ut),t(c,ce),t(c,I),t(I,Ot),t(I,de),t(I,Et),t(I,Vt),t(I,St),t(c,Wt),t(c,A),t(A,mt),t(A,J),t(A,K),t(A,S),t(A,Nt),t(c,fe),t(c,T),t(T,k),t(T,pe),t(T,N),t(T,y),t(T,ot),t(c,ue),t(c,P),t(P,qt),t(P,me),t(P,Dt),t(P,be),t(P,Ht),u(l,Xt,i),u(l,Mt,i)},d(l){l&&m(n),l&&m(a),l&&m(r),l&&m(Xt),l&&m(Mt)}}}function Ke(d){let n,a,r,f,p;function b(w,c){return w[0]?Je:Qe}let x=b(d),$=x(d),h=d[0]&&Ae();return{c(){n=e("button"),$.c(),a=s(),h&&h.c(),r=He(),o(n,"class","btn btn-sm btn-secondary m-t-5")},m(w,c){u(w,n,c),$.m(n,null),u(w,a,c),h&&h.m(w,c),u(w,r,c),f||(p=De(n,"click",d[1]),f=!0)},p(w,[c]){x!==(x=b(w))&&($.d(1),$=x(w),$&&($.c(),$.m(n,null))),w[0]?h||(h=Ae(),h.c(),h.m(r.parentNode,r)):h&&(h.d(1),h=null)},i:Fe,o:Fe,d(w){w&&m(n),$.d(),w&&m(a),h&&h.d(w),w&&m(r),f=!1,p()}}}function Ve(d,n,a){let r=!1;function f(){a(0,r=!r)}return[r,f]}class We extends Se{constructor(n){super(),Ne(this,n,Ve,Ke,qe,{})}}function Te(d,n,a){const r=d.slice();return r[6]=n[a],r}function Pe(d,n,a){const r=d.slice();return r[6]=n[a],r}function Re(d){let n;return{c(){n=e("p"),n.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",o(n,"class","txt-hint txt-sm txt-right")},m(a,r){u(a,n,r)},d(a){a&&m(n)}}}function Oe(d,n){let a,r=n[6].code+"",f,p,b,x;function $(){return n[5](n[6])}return{key:d,first:null,c(){a=e("div"),f=_(r),p=s(),o(a,"class","tab-item"),xe(a,"active",n[2]===n[6].code),this.first=a},m(h,w){u(h,a,w),t(a,f),t(a,p),b||(x=De(a,"click",$),b=!0)},p(h,w){n=h,w&20&&xe(a,"active",n[2]===n[6].code)},d(h){h&&m(a),b=!1,x()}}}function Ee(d,n){let a,r,f,p;return r=new he({props:{content:n[6].body}}),{key:d,first:null,c(){a=e("div"),Yt(r.$$.fragment),f=s(),o(a,"class","tab-item"),xe(a,"active",n[2]===n[6].code),this.first=a},m(b,x){u(b,a,x),Zt(r,a,null),t(a,f),p=!0},p(b,x){n=b,(!p||x&20)&&xe(a,"active",n[2]===n[6].code)},i(b){p||(It(r.$$.fragment,b),p=!0)},o(b){Bt(r.$$.fragment,b),p=!1},d(b){b&&m(a),te(r)}}}function Xe(d){var me,Dt,be,Ht,Xt,Mt;let n,a,r=d[0].name+"",f,p,b,x,$,h,w,c=d[0].name+"",V,bt,Gt,R,zt,q,it,F,W,ee,B,G,le,at,ht=d[0].name+"",X,xt,se,rt,ct,Y,O,Ut,wt,v,Z,_t,jt,$t,z,tt,Ct,Qt,kt,L,dt,gt,ne,ft,oe,D,yt,et,vt,U,pt,ie,H,Ft,lt,Lt,st,At,nt,j,E,Jt,Tt,Kt,Pt,C,Q,M,ae,Rt,re,ut,ce,I,Ot,de,Et,Vt,St,Wt,A,mt,J,K,S=[],Nt=new Map,fe,T,k=[],pe=new Map,N;R=new je({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${d[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
const resultList = await pb.collection('${(me=d[0])==null?void 0:me.name}').getList(1, 50, {
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
});
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
const records = await pb.collection('${(Dt=d[0])==null?void 0:Dt.name}').getFullList(200 /* batch size */, {
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
const record = await pb.collection('${(be=d[0])==null?void 0:be.name}').getFirstListItem('someField="test"', {
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
});
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${d[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
final resultList = await pb.collection('${(Ht=d[0])==null?void 0:Ht.name}').getList(
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
);
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
final records = await pb.collection('${(Xt=d[0])==null?void 0:Xt.name}').getFullList(
|
||||
batch: 200,
|
||||
sort: '-created',
|
||||
);
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
final record = await pb.collection('${(Mt=d[0])==null?void 0:Mt.name}').getFirstListItem(
|
||||
'someField="test"',
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
);
|
||||
`}});let y=d[1]&&Re();et=new he({props:{content:`
|
||||
// DESC by created and ASC by id
|
||||
?sort=-created,id
|
||||
`}}),st=new he({props:{content:`
|
||||
?filter=(id='abc' && created>'2022-01-01')
|
||||
`}}),nt=new We({}),M=new he({props:{content:"?expand=relField1,relField2.subRelField"}});let ot=d[4];const ue=l=>l[6].code;for(let l=0;l<ot.length;l+=1){let i=Pe(d,ot,l),g=ue(i);Nt.set(g,S[l]=Oe(g,i))}let P=d[4];const qt=l=>l[6].code;for(let l=0;l<P.length;l+=1){let i=Te(d,P,l),g=qt(i);pe.set(g,k[l]=Ee(g,i))}return{c(){n=e("h3"),a=_("List/Search ("),f=_(r),p=_(")"),b=s(),x=e("div"),$=e("p"),h=_("Fetch a paginated "),w=e("strong"),V=_(c),bt=_(" records list, supporting sorting and filtering."),Gt=s(),Yt(R.$$.fragment),zt=s(),q=e("h6"),q.textContent="API details",it=s(),F=e("div"),W=e("strong"),W.textContent="GET",ee=s(),B=e("div"),G=e("p"),le=_("/api/collections/"),at=e("strong"),X=_(ht),xt=_("/records"),se=s(),y&&y.c(),rt=s(),ct=e("div"),ct.textContent="Query parameters",Y=s(),O=e("table"),Ut=e("thead"),Ut.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,wt=s(),v=e("tbody"),Z=e("tr"),Z.innerHTML=`<td>page</td>
|
||||
<td><span class="label">Number</span></td>
|
||||
<td>The page (aka. offset) of the paginated list (default to 1).</td>`,_t=s(),jt=e("tr"),jt.innerHTML=`<td>perPage</td>
|
||||
<td><span class="label">Number</span></td>
|
||||
<td>Specify the max returned records per page (default to 30).</td>`,$t=s(),z=e("tr"),tt=e("td"),tt.textContent="sort",Ct=s(),Qt=e("td"),Qt.innerHTML='<span class="label">String</span>',kt=s(),L=e("td"),dt=_("Specify the records order attribute(s). "),gt=e("br"),ne=_(`
|
||||
Add `),ft=e("code"),ft.textContent="-",oe=_(" / "),D=e("code"),D.textContent="+",yt=_(` (default) in front of the attribute for DESC / ASC order.
|
||||
Ex.:
|
||||
`),Yt(et.$$.fragment),vt=s(),U=e("tr"),pt=e("td"),pt.textContent="filter",ie=s(),H=e("td"),H.innerHTML='<span class="label">String</span>',Ft=s(),lt=e("td"),Lt=_(`Filter the returned records. Ex.:
|
||||
`),Yt(st.$$.fragment),At=s(),Yt(nt.$$.fragment),j=s(),E=e("tr"),Jt=e("td"),Jt.textContent="expand",Tt=s(),Kt=e("td"),Kt.innerHTML='<span class="label">String</span>',Pt=s(),C=e("td"),Q=_(`Auto expand record relations. Ex.:
|
||||
`),Yt(M.$$.fragment),ae=_(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Rt=e("br"),re=_(`
|
||||
The expanded relations will be appended to each individual record under the
|
||||
`),ut=e("code"),ut.textContent="expand",ce=_(" property (eg. "),I=e("code"),I.textContent='"expand": {"relField1": {...}, ...}',Ot=_(`).
|
||||
`),de=e("br"),Et=_(`
|
||||
Only the relations to which the request user has permissions to `),Vt=e("strong"),Vt.textContent="view",St=_(" will be expanded."),Wt=s(),A=e("div"),A.textContent="Responses",mt=s(),J=e("div"),K=e("div");for(let l=0;l<S.length;l+=1)S[l].c();fe=s(),T=e("div");for(let l=0;l<k.length;l+=1)k[l].c();o(n,"class","m-b-sm"),o(x,"class","content txt-lg m-b-sm"),o(q,"class","m-b-xs"),o(W,"class","label label-primary"),o(B,"class","content"),o(F,"class","alert alert-info"),o(ct,"class","section-title"),o(O,"class","table-compact table-border m-b-base"),o(A,"class","section-title"),o(K,"class","tabs-header compact left"),o(T,"class","tabs-content"),o(J,"class","tabs")},m(l,i){u(l,n,i),t(n,a),t(n,f),t(n,p),u(l,b,i),u(l,x,i),t(x,$),t($,h),t($,w),t(w,V),t($,bt),u(l,Gt,i),Zt(R,l,i),u(l,zt,i),u(l,q,i),u(l,it,i),u(l,F,i),t(F,W),t(F,ee),t(F,B),t(B,G),t(G,le),t(G,at),t(at,X),t(G,xt),t(F,se),y&&y.m(F,null),u(l,rt,i),u(l,ct,i),u(l,Y,i),u(l,O,i),t(O,Ut),t(O,wt),t(O,v),t(v,Z),t(v,_t),t(v,jt),t(v,$t),t(v,z),t(z,tt),t(z,Ct),t(z,Qt),t(z,kt),t(z,L),t(L,dt),t(L,gt),t(L,ne),t(L,ft),t(L,oe),t(L,D),t(L,yt),Zt(et,L,null),t(v,vt),t(v,U),t(U,pt),t(U,ie),t(U,H),t(U,Ft),t(U,lt),t(lt,Lt),Zt(st,lt,null),t(lt,At),Zt(nt,lt,null),t(v,j),t(v,E),t(E,Jt),t(E,Tt),t(E,Kt),t(E,Pt),t(E,C),t(C,Q),Zt(M,C,null),t(C,ae),t(C,Rt),t(C,re),t(C,ut),t(C,ce),t(C,I),t(C,Ot),t(C,de),t(C,Et),t(C,Vt),t(C,St),u(l,Wt,i),u(l,A,i),u(l,mt,i),u(l,J,i),t(J,K);for(let g=0;g<S.length;g+=1)S[g].m(K,null);t(J,fe),t(J,T);for(let g=0;g<k.length;g+=1)k[g].m(T,null);N=!0},p(l,[i]){var $e,Ce,ke,ge,ye,ve;(!N||i&1)&&r!==(r=l[0].name+"")&&we(f,r),(!N||i&1)&&c!==(c=l[0].name+"")&&we(V,c);const g={};i&9&&(g.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${l[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
const resultList = await pb.collection('${($e=l[0])==null?void 0:$e.name}').getList(1, 50, {
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
});
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
const records = await pb.collection('${(Ce=l[0])==null?void 0:Ce.name}').getFullList(200 /* batch size */, {
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
const record = await pb.collection('${(ke=l[0])==null?void 0:ke.name}').getFirstListItem('someField="test"', {
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
});
|
||||
`),i&9&&(g.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${l[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
final resultList = await pb.collection('${(ge=l[0])==null?void 0:ge.name}').getList(
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
);
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
final records = await pb.collection('${(ye=l[0])==null?void 0:ye.name}').getFullList(
|
||||
batch: 200,
|
||||
sort: '-created',
|
||||
);
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
final record = await pb.collection('${(ve=l[0])==null?void 0:ve.name}').getFirstListItem(
|
||||
'someField="test"',
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
);
|
||||
`),R.$set(g),(!N||i&1)&&ht!==(ht=l[0].name+"")&&we(X,ht),l[1]?y||(y=Re(),y.c(),y.m(F,null)):y&&(y.d(1),y=null),i&20&&(ot=l[4],S=Le(S,i,ue,1,l,ot,Nt,K,Me,Oe,null,Pe)),i&20&&(P=l[4],Ie(),k=Le(k,i,qt,1,l,P,pe,T,Be,Ee,null,Te),Ge())},i(l){if(!N){It(R.$$.fragment,l),It(et.$$.fragment,l),It(st.$$.fragment,l),It(nt.$$.fragment,l),It(M.$$.fragment,l);for(let i=0;i<P.length;i+=1)It(k[i]);N=!0}},o(l){Bt(R.$$.fragment,l),Bt(et.$$.fragment,l),Bt(st.$$.fragment,l),Bt(nt.$$.fragment,l),Bt(M.$$.fragment,l);for(let i=0;i<k.length;i+=1)Bt(k[i]);N=!1},d(l){l&&m(n),l&&m(b),l&&m(x),l&&m(Gt),te(R,l),l&&m(zt),l&&m(q),l&&m(it),l&&m(F),y&&y.d(),l&&m(rt),l&&m(ct),l&&m(Y),l&&m(O),te(et),te(st),te(nt),te(M),l&&m(Wt),l&&m(A),l&&m(mt),l&&m(J);for(let i=0;i<S.length;i+=1)S[i].d();for(let i=0;i<k.length;i+=1)k[i].d()}}}function Ye(d,n,a){let r,f,{collection:p=new ze}=n,b=200,x=[];const $=h=>a(2,b=h.code);return d.$$set=h=>{"collection"in h&&a(0,p=h.collection)},d.$$.update=()=>{d.$$.dirty&1&&a(1,r=(p==null?void 0:p.listRule)===null),d.$$.dirty&3&&p!=null&&p.id&&(x.push({code:200,body:JSON.stringify({page:1,perPage:30,totalPages:1,totalItems:2,items:[_e.dummyCollectionRecord(p),_e.dummyCollectionRecord(p)]},null,2)}),x.push({code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Something went wrong while processing your request. Invalid filter.",
|
||||
"data": {}
|
||||
}
|
||||
`}),r&&x.push({code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Only admins can access this action.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},a(3,f=_e.getApiExampleUrl(Ue.baseUrl)),[p,r,b,f,x,$]}class el extends Se{constructor(n){super(),Ne(this,n,Ye,Xe,qe,{collection:0})}}export{el as default};
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
import{S as Et,i as Nt,s as Mt,e as l,b as a,E as qt,f as d,g as p,u as Ht,y as xt,o as u,w as k,h as e,M as Ae,c as ge,m as ye,x as Ue,N as Lt,O as Dt,k as It,P as Bt,n as zt,t as ce,a as de,d as ve,Q as Gt,C as je,p as Ut,r as Ee}from"./index.2a48c906.js";import{S as jt}from"./SdkTabs.9dbb2ca3.js";function Qt(r){let s,n,i;return{c(){s=l("span"),s.textContent="Show details",n=a(),i=l("i"),d(s,"class","txt"),d(i,"class","ri-arrow-down-s-line")},m(c,f){p(c,s,f),p(c,n,f),p(c,i,f)},d(c){c&&u(s),c&&u(n),c&&u(i)}}}function Jt(r){let s,n,i;return{c(){s=l("span"),s.textContent="Hide details",n=a(),i=l("i"),d(s,"class","txt"),d(i,"class","ri-arrow-up-s-line")},m(c,f){p(c,s,f),p(c,n,f),p(c,i,f)},d(c){c&&u(s),c&&u(n),c&&u(i)}}}function Tt(r){let s,n,i,c,f,m,_,w,b,$,h,M,W,fe,T,pe,O,G,C,H,Fe,A,E,Ce,U,X,q,Y,xe,j,Q,D,P,ue,Z,v,I,ee,me,te,N,B,le,be,se,x,J,ne,Le,K,he,V;return{c(){s=l("p"),s.innerHTML=`The syntax basically follows the format
|
||||
<code><span class="txt-success">OPERAND</span>
|
||||
<span class="txt-danger">OPERATOR</span>
|
||||
<span class="txt-success">OPERAND</span></code>, where:`,n=a(),i=l("ul"),c=l("li"),c.innerHTML=`<code class="txt-success">OPERAND</code> - could be any of the above field literal, string (single
|
||||
or double quoted), number, null, true, false`,f=a(),m=l("li"),_=l("code"),_.textContent="OPERATOR",w=k(` - is one of:
|
||||
`),b=l("br"),$=a(),h=l("ul"),M=l("li"),W=l("code"),W.textContent="=",fe=a(),T=l("span"),T.textContent="Equal",pe=a(),O=l("li"),G=l("code"),G.textContent="!=",C=a(),H=l("span"),H.textContent="NOT equal",Fe=a(),A=l("li"),E=l("code"),E.textContent=">",Ce=a(),U=l("span"),U.textContent="Greater than",X=a(),q=l("li"),Y=l("code"),Y.textContent=">=",xe=a(),j=l("span"),j.textContent="Greater than or equal",Q=a(),D=l("li"),P=l("code"),P.textContent="<",ue=a(),Z=l("span"),Z.textContent="Less than or equal",v=a(),I=l("li"),ee=l("code"),ee.textContent="<=",me=a(),te=l("span"),te.textContent="Less than or equal",N=a(),B=l("li"),le=l("code"),le.textContent="~",be=a(),se=l("span"),se.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,x=a(),J=l("li"),ne=l("code"),ne.textContent="!~",Le=a(),K=l("span"),K.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,he=a(),V=l("p"),V.innerHTML=`To group and combine several expressions you could use brackets
|
||||
<code>(...)</code>, <code>&&</code> (AND) and <code>||</code> (OR) tokens.`,d(_,"class","txt-danger"),d(W,"class","filter-op svelte-1w7s5nw"),d(T,"class","txt-hint"),d(G,"class","filter-op svelte-1w7s5nw"),d(H,"class","txt-hint"),d(E,"class","filter-op svelte-1w7s5nw"),d(U,"class","txt-hint"),d(Y,"class","filter-op svelte-1w7s5nw"),d(j,"class","txt-hint"),d(P,"class","filter-op svelte-1w7s5nw"),d(Z,"class","txt-hint"),d(ee,"class","filter-op svelte-1w7s5nw"),d(te,"class","txt-hint"),d(le,"class","filter-op svelte-1w7s5nw"),d(se,"class","txt-hint"),d(ne,"class","filter-op svelte-1w7s5nw"),d(K,"class","txt-hint")},m(F,R){p(F,s,R),p(F,n,R),p(F,i,R),e(i,c),e(i,f),e(i,m),e(m,_),e(m,w),e(m,b),e(m,$),e(m,h),e(h,M),e(M,W),e(M,fe),e(M,T),e(h,pe),e(h,O),e(O,G),e(O,C),e(O,H),e(h,Fe),e(h,A),e(A,E),e(A,Ce),e(A,U),e(h,X),e(h,q),e(q,Y),e(q,xe),e(q,j),e(h,Q),e(h,D),e(D,P),e(D,ue),e(D,Z),e(h,v),e(h,I),e(I,ee),e(I,me),e(I,te),e(h,N),e(h,B),e(B,le),e(B,be),e(B,se),e(h,x),e(h,J),e(J,ne),e(J,Le),e(J,K),p(F,he,R),p(F,V,R)},d(F){F&&u(s),F&&u(n),F&&u(i),F&&u(he),F&&u(V)}}}function Kt(r){let s,n,i,c,f;function m($,h){return $[0]?Jt:Qt}let _=m(r),w=_(r),b=r[0]&&Tt();return{c(){s=l("button"),w.c(),n=a(),b&&b.c(),i=qt(),d(s,"class","btn btn-sm btn-secondary m-t-5")},m($,h){p($,s,h),w.m(s,null),p($,n,h),b&&b.m($,h),p($,i,h),c||(f=Ht(s,"click",r[1]),c=!0)},p($,[h]){_!==(_=m($))&&(w.d(1),w=_($),w&&(w.c(),w.m(s,null))),$[0]?b||(b=Tt(),b.c(),b.m(i.parentNode,i)):b&&(b.d(1),b=null)},i:xt,o:xt,d($){$&&u(s),w.d(),$&&u(n),b&&b.d($),$&&u(i),c=!1,f()}}}function Vt(r,s,n){let i=!1;function c(){n(0,i=!i)}return[i,c]}class Wt extends Et{constructor(s){super(),Nt(this,s,Vt,Kt,Mt,{})}}function Pt(r,s,n){const i=r.slice();return i[6]=s[n],i}function Rt(r,s,n){const i=r.slice();return i[6]=s[n],i}function St(r){let s;return{c(){s=l("p"),s.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",d(s,"class","txt-hint txt-sm txt-right")},m(n,i){p(n,s,i)},d(n){n&&u(s)}}}function Ot(r,s){let n,i=s[6].code+"",c,f,m,_;function w(){return s[5](s[6])}return{key:r,first:null,c(){n=l("div"),c=k(i),f=a(),d(n,"class","tab-item"),Ee(n,"active",s[2]===s[6].code),this.first=n},m(b,$){p(b,n,$),e(n,c),e(n,f),m||(_=Ht(n,"click",w),m=!0)},p(b,$){s=b,$&20&&Ee(n,"active",s[2]===s[6].code)},d(b){b&&u(n),m=!1,_()}}}function At(r,s){let n,i,c,f;return i=new Ae({props:{content:s[6].body}}),{key:r,first:null,c(){n=l("div"),ge(i.$$.fragment),c=a(),d(n,"class","tab-item"),Ee(n,"active",s[2]===s[6].code),this.first=n},m(m,_){p(m,n,_),ye(i,n,null),e(n,c),f=!0},p(m,_){s=m,(!f||_&20)&&Ee(n,"active",s[2]===s[6].code)},i(m){f||(ce(i.$$.fragment,m),f=!0)},o(m){de(i.$$.fragment,m),f=!1},d(m){m&&u(n),ve(i)}}}function Xt(r){var mt,bt,ht,_t,$t,kt;let s,n,i=r[0].name+"",c,f,m,_,w,b,$,h=r[0].name+"",M,W,fe,T,pe,O,G,C,H,Fe,A,E,Ce,U,X=r[0].name+"",q,Y,xe,j,Q,D,P,ue,Z,v,I,ee,me,te,N,B,le,be,se,x,J,ne,Le,K,he,V,F,R,Qe,ie,Ne,Je,Me,Ke,_e,Ve,$e,We,ke,Xe,oe,He,Ye,qe,Ze,y,et,we,tt,lt,st,De,nt,Ie,it,ot,at,Be,rt,ze,Te,Ge,ae,Pe,z=[],ct=new Map,dt,Re,S=[],ft=new Map,re;T=new jt({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
const resultList = await pb.collection('${(mt=r[0])==null?void 0:mt.name}').getList(1, 50, {
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
|
||||
});
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
const records = await pb.collection('${(bt=r[0])==null?void 0:bt.name}').getFullList(200 /* batch size */, {
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
const record = await pb.collection('${(ht=r[0])==null?void 0:ht.name}').getFirstListItem('someField="test"', {
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
});
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
final resultList = await pb.collection('${(_t=r[0])==null?void 0:_t.name}').getList(
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
|
||||
);
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
final records = await pb.collection('${($t=r[0])==null?void 0:$t.name}').getFullList(
|
||||
batch: 200,
|
||||
sort: '-created',
|
||||
);
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
final record = await pb.collection('${(kt=r[0])==null?void 0:kt.name}').getFirstListItem(
|
||||
'someField="test"',
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
);
|
||||
`}});let L=r[1]&&St();R=new Ae({props:{content:`
|
||||
// DESC by created and ASC by id
|
||||
?sort=-created,id
|
||||
`}}),$e=new Ae({props:{content:`
|
||||
?filter=(id='abc' && created>'2022-01-01')
|
||||
`}}),ke=new Wt({}),we=new Ae({props:{content:"?expand=relField1,relField2.subRelField"}});let Oe=r[4];const pt=t=>t[6].code;for(let t=0;t<Oe.length;t+=1){let o=Rt(r,Oe,t),g=pt(o);ct.set(g,z[t]=Ot(g,o))}let Se=r[4];const ut=t=>t[6].code;for(let t=0;t<Se.length;t+=1){let o=Pt(r,Se,t),g=ut(o);ft.set(g,S[t]=At(g,o))}return{c(){s=l("h3"),n=k("List/Search ("),c=k(i),f=k(")"),m=a(),_=l("div"),w=l("p"),b=k("Fetch a paginated "),$=l("strong"),M=k(h),W=k(" records list, supporting sorting and filtering."),fe=a(),ge(T.$$.fragment),pe=a(),O=l("h6"),O.textContent="API details",G=a(),C=l("div"),H=l("strong"),H.textContent="GET",Fe=a(),A=l("div"),E=l("p"),Ce=k("/api/collections/"),U=l("strong"),q=k(X),Y=k("/records"),xe=a(),L&&L.c(),j=a(),Q=l("div"),Q.textContent="Query parameters",D=a(),P=l("table"),ue=l("thead"),ue.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,Z=a(),v=l("tbody"),I=l("tr"),I.innerHTML=`<td>page</td>
|
||||
<td><span class="label">Number</span></td>
|
||||
<td>The page (aka. offset) of the paginated list (default to 1).</td>`,ee=a(),me=l("tr"),me.innerHTML=`<td>perPage</td>
|
||||
<td><span class="label">Number</span></td>
|
||||
<td>Specify the max returned records per page (default to 30).</td>`,te=a(),N=l("tr"),B=l("td"),B.textContent="sort",le=a(),be=l("td"),be.innerHTML='<span class="label">String</span>',se=a(),x=l("td"),J=k("Specify the records order attribute(s). "),ne=l("br"),Le=k(`
|
||||
Add `),K=l("code"),K.textContent="-",he=k(" / "),V=l("code"),V.textContent="+",F=k(` (default) in front of the attribute for DESC / ASC order.
|
||||
Ex.:
|
||||
`),ge(R.$$.fragment),Qe=a(),ie=l("tr"),Ne=l("td"),Ne.textContent="filter",Je=a(),Me=l("td"),Me.innerHTML='<span class="label">String</span>',Ke=a(),_e=l("td"),Ve=k(`Filter the returned records. Ex.:
|
||||
`),ge($e.$$.fragment),We=a(),ge(ke.$$.fragment),Xe=a(),oe=l("tr"),He=l("td"),He.textContent="expand",Ye=a(),qe=l("td"),qe.innerHTML='<span class="label">String</span>',Ze=a(),y=l("td"),et=k(`Auto expand record relations. Ex.:
|
||||
`),ge(we.$$.fragment),tt=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),lt=l("br"),st=k(`
|
||||
The expanded relations will be appended to each individual record under the
|
||||
`),De=l("code"),De.textContent="expand",nt=k(" property (eg. "),Ie=l("code"),Ie.textContent='"expand": {"relField1": {...}, ...}',it=k(`).
|
||||
`),ot=l("br"),at=k(`
|
||||
Only the relations to which the request user has permissions to `),Be=l("strong"),Be.textContent="view",rt=k(" will be expanded."),ze=a(),Te=l("div"),Te.textContent="Responses",Ge=a(),ae=l("div"),Pe=l("div");for(let t=0;t<z.length;t+=1)z[t].c();dt=a(),Re=l("div");for(let t=0;t<S.length;t+=1)S[t].c();d(s,"class","m-b-sm"),d(_,"class","content txt-lg m-b-sm"),d(O,"class","m-b-xs"),d(H,"class","label label-primary"),d(A,"class","content"),d(C,"class","alert alert-info"),d(Q,"class","section-title"),d(P,"class","table-compact table-border m-b-base"),d(Te,"class","section-title"),d(Pe,"class","tabs-header compact left"),d(Re,"class","tabs-content"),d(ae,"class","tabs")},m(t,o){p(t,s,o),e(s,n),e(s,c),e(s,f),p(t,m,o),p(t,_,o),e(_,w),e(w,b),e(w,$),e($,M),e(w,W),p(t,fe,o),ye(T,t,o),p(t,pe,o),p(t,O,o),p(t,G,o),p(t,C,o),e(C,H),e(C,Fe),e(C,A),e(A,E),e(E,Ce),e(E,U),e(U,q),e(E,Y),e(C,xe),L&&L.m(C,null),p(t,j,o),p(t,Q,o),p(t,D,o),p(t,P,o),e(P,ue),e(P,Z),e(P,v),e(v,I),e(v,ee),e(v,me),e(v,te),e(v,N),e(N,B),e(N,le),e(N,be),e(N,se),e(N,x),e(x,J),e(x,ne),e(x,Le),e(x,K),e(x,he),e(x,V),e(x,F),ye(R,x,null),e(v,Qe),e(v,ie),e(ie,Ne),e(ie,Je),e(ie,Me),e(ie,Ke),e(ie,_e),e(_e,Ve),ye($e,_e,null),e(_e,We),ye(ke,_e,null),e(v,Xe),e(v,oe),e(oe,He),e(oe,Ye),e(oe,qe),e(oe,Ze),e(oe,y),e(y,et),ye(we,y,null),e(y,tt),e(y,lt),e(y,st),e(y,De),e(y,nt),e(y,Ie),e(y,it),e(y,ot),e(y,at),e(y,Be),e(y,rt),p(t,ze,o),p(t,Te,o),p(t,Ge,o),p(t,ae,o),e(ae,Pe);for(let g=0;g<z.length;g+=1)z[g].m(Pe,null);e(ae,dt),e(ae,Re);for(let g=0;g<S.length;g+=1)S[g].m(Re,null);re=!0},p(t,[o]){var wt,gt,yt,vt,Ft,Ct;(!re||o&1)&&i!==(i=t[0].name+"")&&Ue(c,i),(!re||o&1)&&h!==(h=t[0].name+"")&&Ue(M,h);const g={};o&9&&(g.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${t[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
const resultList = await pb.collection('${(wt=t[0])==null?void 0:wt.name}').getList(1, 50, {
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
|
||||
});
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
const records = await pb.collection('${(gt=t[0])==null?void 0:gt.name}').getFullList(200 /* batch size */, {
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
const record = await pb.collection('${(yt=t[0])==null?void 0:yt.name}').getFirstListItem('someField="test"', {
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
});
|
||||
`),o&9&&(g.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${t[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
final resultList = await pb.collection('${(vt=t[0])==null?void 0:vt.name}').getList(
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someFiled1 != someField2',
|
||||
);
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
final records = await pb.collection('${(Ft=t[0])==null?void 0:Ft.name}').getFullList(
|
||||
batch: 200,
|
||||
sort: '-created',
|
||||
);
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
final record = await pb.collection('${(Ct=t[0])==null?void 0:Ct.name}').getFirstListItem(
|
||||
'someField="test"',
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
);
|
||||
`),T.$set(g),(!re||o&1)&&X!==(X=t[0].name+"")&&Ue(q,X),t[1]?L||(L=St(),L.c(),L.m(C,null)):L&&(L.d(1),L=null),o&20&&(Oe=t[4],z=Lt(z,o,pt,1,t,Oe,ct,Pe,Dt,Ot,null,Rt)),o&20&&(Se=t[4],It(),S=Lt(S,o,ut,1,t,Se,ft,Re,Bt,At,null,Pt),zt())},i(t){if(!re){ce(T.$$.fragment,t),ce(R.$$.fragment,t),ce($e.$$.fragment,t),ce(ke.$$.fragment,t),ce(we.$$.fragment,t);for(let o=0;o<Se.length;o+=1)ce(S[o]);re=!0}},o(t){de(T.$$.fragment,t),de(R.$$.fragment,t),de($e.$$.fragment,t),de(ke.$$.fragment,t),de(we.$$.fragment,t);for(let o=0;o<S.length;o+=1)de(S[o]);re=!1},d(t){t&&u(s),t&&u(m),t&&u(_),t&&u(fe),ve(T,t),t&&u(pe),t&&u(O),t&&u(G),t&&u(C),L&&L.d(),t&&u(j),t&&u(Q),t&&u(D),t&&u(P),ve(R),ve($e),ve(ke),ve(we),t&&u(ze),t&&u(Te),t&&u(Ge),t&&u(ae);for(let o=0;o<z.length;o+=1)z[o].d();for(let o=0;o<S.length;o+=1)S[o].d()}}}function Yt(r,s,n){let i,c,{collection:f=new Gt}=s,m=200,_=[];const w=b=>n(2,m=b.code);return r.$$set=b=>{"collection"in b&&n(0,f=b.collection)},r.$$.update=()=>{r.$$.dirty&1&&n(1,i=(f==null?void 0:f.listRule)===null),r.$$.dirty&3&&f!=null&&f.id&&(_.push({code:200,body:JSON.stringify({page:1,perPage:30,totalPages:1,totalItems:2,items:[je.dummyCollectionRecord(f),je.dummyCollectionRecord(f)]},null,2)}),_.push({code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Something went wrong while processing your request. Invalid filter.",
|
||||
"data": {}
|
||||
}
|
||||
`}),i&&_.push({code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Only admins can access this action.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},n(3,c=je.getApiExampleUrl(Ut.baseUrl)),[f,i,m,c,_,w]}class tl extends Et{constructor(s){super(),Nt(this,s,Yt,Xt,Mt,{collection:0})}}export{tl as default};
|
||||
Vendored
+5
-5
@@ -1,4 +1,4 @@
|
||||
import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s,m as Se,x as U,N as Pe,O as Oe,k as Le,P as We,n as ze,t as te,a as le,o as d,d as Ee,Q as De,C as He,p as Re,r as j,u as Ue,M as je}from"./index.2a48c906.js";import{S as Ne}from"./SdkTabs.9dbb2ca3.js";function ye(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ae(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ce(a,l){let o,n=l[5].code+"",f,h,c,u;function m(){return l[4](l[5])}return{key:a,first:null,c(){o=i("button"),f=v(n),h=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(g,P){r(g,o,P),s(o,f),s(o,h),c||(u=Ue(o,"click",m),c=!0)},p(g,P){l=g,P&4&&n!==(n=l[5].code+"")&&U(f,n),P&6&&j(o,"active",l[1]===l[5].code)},d(g){g&&d(o),c=!1,u()}}}function Te(a,l){let o,n,f,h;return n=new je({props:{content:l[5].body}}),{key:a,first:null,c(){o=i("div"),Ie(n.$$.fragment),f=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(c,u){r(c,o,u),Se(n,o,null),s(o,f),h=!0},p(c,u){l=c;const m={};u&4&&(m.content=l[5].body),n.$set(m),(!h||u&6)&&j(o,"active",l[1]===l[5].code)},i(c){h||(te(n.$$.fragment,c),h=!0)},o(c){le(n.$$.fragment,c),h=!1},d(c){c&&d(o),Ee(n)}}}function Ge(a){var be,he,_e,ke;let l,o,n=a[0].name+"",f,h,c,u,m,g,P,L=a[0].name+"",N,oe,se,G,K,y,Q,I,F,w,W,ae,z,A,ne,J,D=a[0].name+"",V,ie,X,ce,re,H,Y,S,Z,E,x,B,ee,C,q,$=[],de=new Map,ue,M,k=[],pe=new Map,T;y=new Ne({props:{js:`
|
||||
import{S as Be,i as qe,s as Oe,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s,m as Se,x as U,O as Pe,P as Le,k as Me,Q as Re,n as We,t as te,a as le,o as d,d as Ee,R as ze,C as De,p as He,r as j,u as Ue,N as je}from"./index.536fddcb.js";import{S as Ne}from"./SdkTabs.3571c26b.js";function ye(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ae(a,l,o){const n=a.slice();return n[5]=l[o],n}function Ce(a,l){let o,n=l[5].code+"",f,h,c,u;function m(){return l[4](l[5])}return{key:a,first:null,c(){o=i("button"),f=v(n),h=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(g,P){r(g,o,P),s(o,f),s(o,h),c||(u=Ue(o,"click",m),c=!0)},p(g,P){l=g,P&4&&n!==(n=l[5].code+"")&&U(f,n),P&6&&j(o,"active",l[1]===l[5].code)},d(g){g&&d(o),c=!1,u()}}}function Te(a,l){let o,n,f,h;return n=new je({props:{content:l[5].body}}),{key:a,first:null,c(){o=i("div"),Ie(n.$$.fragment),f=_(),b(o,"class","tab-item"),j(o,"active",l[1]===l[5].code),this.first=o},m(c,u){r(c,o,u),Se(n,o,null),s(o,f),h=!0},p(c,u){l=c;const m={};u&4&&(m.content=l[5].body),n.$set(m),(!h||u&6)&&j(o,"active",l[1]===l[5].code)},i(c){h||(te(n.$$.fragment,c),h=!0)},o(c){le(n.$$.fragment,c),h=!1},d(c){c&&d(o),Ee(n)}}}function Ge(a){var be,he,_e,ke;let l,o,n=a[0].name+"",f,h,c,u,m,g,P,M=a[0].name+"",N,oe,se,G,K,y,Q,I,F,w,R,ae,W,A,ne,J,z=a[0].name+"",V,ie,X,ce,re,D,Y,S,Z,E,x,B,ee,C,q,$=[],de=new Map,ue,O,k=[],pe=new Map,T;y=new Ne({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[3]}');
|
||||
@@ -22,12 +22,12 @@ import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s
|
||||
final result = await pb.collection('${(ke=a[0])==null?void 0:ke.name}').listExternalAuths(
|
||||
pb.authStore.model.id,
|
||||
);
|
||||
`}});let R=a[2];const fe=e=>e[5].code;for(let e=0;e<R.length;e+=1){let t=Ae(a,R,e),p=fe(t);de.set(p,$[e]=Ce(p,t))}let O=a[2];const me=e=>e[5].code;for(let e=0;e<O.length;e+=1){let t=ye(a,O,e),p=me(t);pe.set(p,k[e]=Te(p,t))}return{c(){l=i("h3"),o=v("List OAuth2 accounts ("),f=v(n),h=v(")"),c=_(),u=i("div"),m=i("p"),g=v("Returns a list with all OAuth2 providers linked to a single "),P=i("strong"),N=v(L),oe=v("."),se=_(),G=i("p"),G.textContent="Only admins and the account owner can access this action.",K=_(),Ie(y.$$.fragment),Q=_(),I=i("h6"),I.textContent="API details",F=_(),w=i("div"),W=i("strong"),W.textContent="GET",ae=_(),z=i("div"),A=i("p"),ne=v("/api/collections/"),J=i("strong"),V=v(D),ie=v("/records/"),X=i("strong"),X.textContent=":id",ce=v("/external-auths"),re=_(),H=i("p"),H.innerHTML="Requires <code>Authorization:TOKEN</code> header",Y=_(),S=i("div"),S.textContent="Path Parameters",Z=_(),E=i("table"),E.innerHTML=`<thead><tr><th>Param</th>
|
||||
`}});let H=a[2];const fe=e=>e[5].code;for(let e=0;e<H.length;e+=1){let t=Ae(a,H,e),p=fe(t);de.set(p,$[e]=Ce(p,t))}let L=a[2];const me=e=>e[5].code;for(let e=0;e<L.length;e+=1){let t=ye(a,L,e),p=me(t);pe.set(p,k[e]=Te(p,t))}return{c(){l=i("h3"),o=v("List OAuth2 accounts ("),f=v(n),h=v(")"),c=_(),u=i("div"),m=i("p"),g=v("Returns a list with all OAuth2 providers linked to a single "),P=i("strong"),N=v(M),oe=v("."),se=_(),G=i("p"),G.textContent="Only admins and the account owner can access this action.",K=_(),Ie(y.$$.fragment),Q=_(),I=i("h6"),I.textContent="API details",F=_(),w=i("div"),R=i("strong"),R.textContent="GET",ae=_(),W=i("div"),A=i("p"),ne=v("/api/collections/"),J=i("strong"),V=v(z),ie=v("/records/"),X=i("strong"),X.textContent=":id",ce=v("/external-auths"),re=_(),D=i("p"),D.innerHTML="Requires <code>Authorization:TOKEN</code> header",Y=_(),S=i("div"),S.textContent="Path Parameters",Z=_(),E=i("table"),E.innerHTML=`<thead><tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr></thead>
|
||||
<tbody><tr><td>id</td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>ID of the auth record.</td></tr></tbody>`,x=_(),B=i("div"),B.textContent="Responses",ee=_(),C=i("div"),q=i("div");for(let e=0;e<$.length;e+=1)$[e].c();ue=_(),M=i("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(u,"class","content txt-lg m-b-sm"),b(I,"class","m-b-xs"),b(W,"class","label label-primary"),b(z,"class","content"),b(H,"class","txt-hint txt-sm txt-right"),b(w,"class","alert alert-info"),b(S,"class","section-title"),b(E,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(M,"class","tabs-content"),b(C,"class","tabs")},m(e,t){r(e,l,t),s(l,o),s(l,f),s(l,h),r(e,c,t),r(e,u,t),s(u,m),s(m,g),s(m,P),s(P,N),s(m,oe),s(u,se),s(u,G),r(e,K,t),Se(y,e,t),r(e,Q,t),r(e,I,t),r(e,F,t),r(e,w,t),s(w,W),s(w,ae),s(w,z),s(z,A),s(A,ne),s(A,J),s(J,V),s(A,ie),s(A,X),s(A,ce),s(w,re),s(w,H),r(e,Y,t),r(e,S,t),r(e,Z,t),r(e,E,t),r(e,x,t),r(e,B,t),r(e,ee,t),r(e,C,t),s(C,q);for(let p=0;p<$.length;p+=1)$[p].m(q,null);s(C,ue),s(C,M);for(let p=0;p<k.length;p+=1)k[p].m(M,null);T=!0},p(e,[t]){var ve,we,$e,ge;(!T||t&1)&&n!==(n=e[0].name+"")&&U(f,n),(!T||t&1)&&L!==(L=e[0].name+"")&&U(N,L);const p={};t&9&&(p.js=`
|
||||
<td>ID of the auth record.</td></tr></tbody>`,x=_(),B=i("div"),B.textContent="Responses",ee=_(),C=i("div"),q=i("div");for(let e=0;e<$.length;e+=1)$[e].c();ue=_(),O=i("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(u,"class","content txt-lg m-b-sm"),b(I,"class","m-b-xs"),b(R,"class","label label-primary"),b(W,"class","content"),b(D,"class","txt-hint txt-sm txt-right"),b(w,"class","alert alert-info"),b(S,"class","section-title"),b(E,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(O,"class","tabs-content"),b(C,"class","tabs")},m(e,t){r(e,l,t),s(l,o),s(l,f),s(l,h),r(e,c,t),r(e,u,t),s(u,m),s(m,g),s(m,P),s(P,N),s(m,oe),s(u,se),s(u,G),r(e,K,t),Se(y,e,t),r(e,Q,t),r(e,I,t),r(e,F,t),r(e,w,t),s(w,R),s(w,ae),s(w,W),s(W,A),s(A,ne),s(A,J),s(J,V),s(A,ie),s(A,X),s(A,ce),s(w,re),s(w,D),r(e,Y,t),r(e,S,t),r(e,Z,t),r(e,E,t),r(e,x,t),r(e,B,t),r(e,ee,t),r(e,C,t),s(C,q);for(let p=0;p<$.length;p+=1)$[p].m(q,null);s(C,ue),s(C,O);for(let p=0;p<k.length;p+=1)k[p].m(O,null);T=!0},p(e,[t]){var ve,we,$e,ge;(!T||t&1)&&n!==(n=e[0].name+"")&&U(f,n),(!T||t&1)&&M!==(M=e[0].name+"")&&U(N,M);const p={};t&9&&(p.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -51,7 +51,7 @@ import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s
|
||||
final result = await pb.collection('${(ge=e[0])==null?void 0:ge.name}').listExternalAuths(
|
||||
pb.authStore.model.id,
|
||||
);
|
||||
`),y.$set(p),(!T||t&1)&&D!==(D=e[0].name+"")&&U(V,D),t&6&&(R=e[2],$=Pe($,t,fe,1,e,R,de,q,Oe,Ce,null,Ae)),t&6&&(O=e[2],Le(),k=Pe(k,t,me,1,e,O,pe,M,We,Te,null,ye),ze())},i(e){if(!T){te(y.$$.fragment,e);for(let t=0;t<O.length;t+=1)te(k[t]);T=!0}},o(e){le(y.$$.fragment,e);for(let t=0;t<k.length;t+=1)le(k[t]);T=!1},d(e){e&&d(l),e&&d(c),e&&d(u),e&&d(K),Ee(y,e),e&&d(Q),e&&d(I),e&&d(F),e&&d(w),e&&d(Y),e&&d(S),e&&d(Z),e&&d(E),e&&d(x),e&&d(B),e&&d(ee),e&&d(C);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Ke(a,l,o){let n,{collection:f=new De}=l,h=200,c=[];const u=m=>o(1,h=m.code);return a.$$set=m=>{"collection"in m&&o(0,f=m.collection)},a.$$.update=()=>{a.$$.dirty&1&&o(2,c=[{code:200,body:`
|
||||
`),y.$set(p),(!T||t&1)&&z!==(z=e[0].name+"")&&U(V,z),t&6&&(H=e[2],$=Pe($,t,fe,1,e,H,de,q,Le,Ce,null,Ae)),t&6&&(L=e[2],Me(),k=Pe(k,t,me,1,e,L,pe,O,Re,Te,null,ye),We())},i(e){if(!T){te(y.$$.fragment,e);for(let t=0;t<L.length;t+=1)te(k[t]);T=!0}},o(e){le(y.$$.fragment,e);for(let t=0;t<k.length;t+=1)le(k[t]);T=!1},d(e){e&&d(l),e&&d(c),e&&d(u),e&&d(K),Ee(y,e),e&&d(Q),e&&d(I),e&&d(F),e&&d(w),e&&d(Y),e&&d(S),e&&d(Z),e&&d(E),e&&d(x),e&&d(B),e&&d(ee),e&&d(C);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Ke(a,l,o){let n,{collection:f=new ze}=l,h=200,c=[];const u=m=>o(1,h=m.code);return a.$$set=m=>{"collection"in m&&o(0,f=m.collection)},a.$$.update=()=>{a.$$.dirty&1&&o(2,c=[{code:200,body:`
|
||||
[
|
||||
{
|
||||
"id": "8171022dc95a4e8",
|
||||
@@ -90,4 +90,4 @@ import{S as Be,i as qe,s as Me,e as i,w as v,b as _,c as Ie,f as b,g as r,h as s
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}])},o(3,n=He.getApiExampleUrl(Re.baseUrl)),[f,h,c,n,u]}class Je extends Be{constructor(l){super(),qe(this,l,Ke,Ge,Me,{collection:0})}}export{Je as default};
|
||||
`}])},o(3,n=De.getApiExampleUrl(He.baseUrl)),[f,h,c,n,u]}class Je extends Be{constructor(l){super(),qe(this,l,Ke,Ge,Oe,{collection:0})}}export{Je as default};
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
import{S as E,i as G,s as I,F as K,c as A,m as B,t as H,a as N,d as T,C as M,q as J,e as c,w as q,b as C,f as u,r as L,g as b,h as _,u as h,v as O,j as Q,l as U,o as w,A as V,p as W,B as X,D as Y,x as Z,z as S}from"./index.2a48c906.js";function y(f){let e,o,s;return{c(){e=q("for "),o=c("strong"),s=q(f[3]),u(o,"class","txt-nowrap")},m(l,t){b(l,e,t),b(l,o,t),_(o,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&w(e),l&&w(o)}}}function x(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0,t.autofocus=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[0]),t.focus(),p||(d=h(t,"input",f[6]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&1&&t.value!==n[0]&&S(t,n[0])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function ee(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password confirm"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[1]),p||(d=h(t,"input",f[7]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&2&&t.value!==n[1]&&S(t,n[1])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function te(f){let e,o,s,l,t,r,p,d,n,i,g,R,P,v,k,F,j,m=f[3]&&y(f);return r=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),d=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),{c(){e=c("form"),o=c("div"),s=c("h4"),l=q(`Reset your admin password
|
||||
import{S as E,i as G,s as I,F as K,c as A,m as B,t as H,a as N,d as T,C as M,q as J,e as c,w as q,b as C,f as u,r as L,g as b,h as _,u as h,v as O,j as Q,l as U,o as w,A as V,p as W,B as X,D as Y,x as Z,z as S}from"./index.536fddcb.js";function y(f){let e,o,s;return{c(){e=q("for "),o=c("strong"),s=q(f[3]),u(o,"class","txt-nowrap")},m(l,t){b(l,e,t),b(l,o,t),_(o,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&w(e),l&&w(o)}}}function x(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0,t.autofocus=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[0]),t.focus(),p||(d=h(t,"input",f[6]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&1&&t.value!==n[0]&&S(t,n[0])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function ee(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password confirm"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[1]),p||(d=h(t,"input",f[7]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&2&&t.value!==n[1]&&S(t,n[1])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function te(f){let e,o,s,l,t,r,p,d,n,i,g,R,P,v,k,F,j,m=f[3]&&y(f);return r=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),d=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),{c(){e=c("form"),o=c("div"),s=c("h4"),l=q(`Reset your admin password
|
||||
`),m&&m.c(),t=C(),A(r.$$.fragment),p=C(),A(d.$$.fragment),n=C(),i=c("button"),g=c("span"),g.textContent="Set new password",R=C(),P=c("div"),v=c("a"),v.textContent="Back to login",u(s,"class","m-b-xs"),u(o,"class","content txt-center m-b-sm"),u(g,"class","txt"),u(i,"type","submit"),u(i,"class","btn btn-lg btn-block"),i.disabled=f[2],L(i,"btn-loading",f[2]),u(e,"class","m-b-base"),u(v,"href","/login"),u(v,"class","link-hint"),u(P,"class","content txt-center")},m(a,$){b(a,e,$),_(e,o),_(o,s),_(s,l),m&&m.m(s,null),_(e,t),B(r,e,null),_(e,p),B(d,e,null),_(e,n),_(e,i),_(i,g),b(a,R,$),b(a,P,$),_(P,v),k=!0,F||(j=[h(e,"submit",O(f[4])),Q(U.call(null,v))],F=!0)},p(a,$){a[3]?m?m.p(a,$):(m=y(a),m.c(),m.m(s,null)):m&&(m.d(1),m=null);const z={};$&769&&(z.$$scope={dirty:$,ctx:a}),r.$set(z);const D={};$&770&&(D.$$scope={dirty:$,ctx:a}),d.$set(D),(!k||$&4)&&(i.disabled=a[2]),(!k||$&4)&&L(i,"btn-loading",a[2])},i(a){k||(H(r.$$.fragment,a),H(d.$$.fragment,a),k=!0)},o(a){N(r.$$.fragment,a),N(d.$$.fragment,a),k=!1},d(a){a&&w(e),m&&m.d(),T(r),T(d),a&&w(R),a&&w(P),F=!1,V(j)}}}function se(f){let e,o;return e=new K({props:{$$slots:{default:[te]},$$scope:{ctx:f}}}),{c(){A(e.$$.fragment)},m(s,l){B(e,s,l),o=!0},p(s,[l]){const t={};l&527&&(t.$$scope={dirty:l,ctx:s}),e.$set(t)},i(s){o||(H(e.$$.fragment,s),o=!0)},o(s){N(e.$$.fragment,s),o=!1},d(s){T(e,s)}}}function le(f,e,o){let s,{params:l}=e,t="",r="",p=!1;async function d(){if(!p){o(2,p=!0);try{await W.admins.confirmPasswordReset(l==null?void 0:l.token,t,r),X("Successfully set a new admin password."),Y("/")}catch(g){W.errorResponseHandler(g)}o(2,p=!1)}}function n(){t=this.value,o(0,t)}function i(){r=this.value,o(1,r)}return f.$$set=g=>{"params"in g&&o(5,l=g.params)},f.$$.update=()=>{f.$$.dirty&32&&o(3,s=M.getJWTPayload(l==null?void 0:l.token).email||"")},[t,r,p,s,d,l,n,i]}class ae extends E{constructor(e){super(),G(this,e,le,se,I,{params:5})}}export{ae as default};
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
import{S as M,i as T,s as j,F as z,c as H,m as L,t as w,a as y,d as S,b as g,e as _,f as p,g as k,h as d,j as A,l as B,k as N,n as D,o as v,p as C,q as G,r as F,u as E,v as I,w as h,x as J,y as P,z as R}from"./index.2a48c906.js";function K(c){let e,s,n,l,t,o,f,m,i,a,b,u;return l=new G({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:r})=>({5:r}),({uniqueId:r})=>r?32:0]},$$scope:{ctx:c}}}),{c(){e=_("form"),s=_("div"),s.innerHTML=`<h4 class="m-b-xs">Forgotten admin password</h4>
|
||||
import{S as M,i as T,s as j,F as z,c as H,m as L,t as w,a as y,d as S,b as g,e as _,f as p,g as k,h as d,j as A,l as B,k as N,n as D,o as v,p as C,q as G,r as F,u as E,v as I,w as h,x as J,y as P,z as R}from"./index.536fddcb.js";function K(c){let e,s,n,l,t,o,f,m,i,a,b,u;return l=new G({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:r})=>({5:r}),({uniqueId:r})=>r?32:0]},$$scope:{ctx:c}}}),{c(){e=_("form"),s=_("div"),s.innerHTML=`<h4 class="m-b-xs">Forgotten admin password</h4>
|
||||
<p>Enter the email associated with your account and we\u2019ll send you a recovery link:</p>`,n=g(),H(l.$$.fragment),t=g(),o=_("button"),f=_("i"),m=g(),i=_("span"),i.textContent="Send recovery link",p(s,"class","content txt-center m-b-sm"),p(f,"class","ri-mail-send-line"),p(i,"class","txt"),p(o,"type","submit"),p(o,"class","btn btn-lg btn-block"),o.disabled=c[1],F(o,"btn-loading",c[1]),p(e,"class","m-b-base")},m(r,$){k(r,e,$),d(e,s),d(e,n),L(l,e,null),d(e,t),d(e,o),d(o,f),d(o,m),d(o,i),a=!0,b||(u=E(e,"submit",I(c[3])),b=!0)},p(r,$){const q={};$&97&&(q.$$scope={dirty:$,ctx:r}),l.$set(q),(!a||$&2)&&(o.disabled=r[1]),(!a||$&2)&&F(o,"btn-loading",r[1])},i(r){a||(w(l.$$.fragment,r),a=!0)},o(r){y(l.$$.fragment,r),a=!1},d(r){r&&v(e),S(l),b=!1,u()}}}function O(c){let e,s,n,l,t,o,f,m,i;return{c(){e=_("div"),s=_("div"),s.innerHTML='<i class="ri-checkbox-circle-line"></i>',n=g(),l=_("div"),t=_("p"),o=h("Check "),f=_("strong"),m=h(c[0]),i=h(" for the recovery link."),p(s,"class","icon"),p(f,"class","txt-nowrap"),p(l,"class","content"),p(e,"class","alert alert-success")},m(a,b){k(a,e,b),d(e,s),d(e,n),d(e,l),d(l,t),d(t,o),d(t,f),d(f,m),d(t,i)},p(a,b){b&1&&J(m,a[0])},i:P,o:P,d(a){a&&v(e)}}}function Q(c){let e,s,n,l,t,o,f,m;return{c(){e=_("label"),s=h("Email"),l=g(),t=_("input"),p(e,"for",n=c[5]),p(t,"type","email"),p(t,"id",o=c[5]),t.required=!0,t.autofocus=!0},m(i,a){k(i,e,a),d(e,s),k(i,l,a),k(i,t,a),R(t,c[0]),t.focus(),f||(m=E(t,"input",c[4]),f=!0)},p(i,a){a&32&&n!==(n=i[5])&&p(e,"for",n),a&32&&o!==(o=i[5])&&p(t,"id",o),a&1&&t.value!==i[0]&&R(t,i[0])},d(i){i&&v(e),i&&v(l),i&&v(t),f=!1,m()}}}function U(c){let e,s,n,l,t,o,f,m;const i=[O,K],a=[];function b(u,r){return u[2]?0:1}return e=b(c),s=a[e]=i[e](c),{c(){s.c(),n=g(),l=_("div"),t=_("a"),t.textContent="Back to login",p(t,"href","/login"),p(t,"class","link-hint"),p(l,"class","content txt-center")},m(u,r){a[e].m(u,r),k(u,n,r),k(u,l,r),d(l,t),o=!0,f||(m=A(B.call(null,t)),f=!0)},p(u,r){let $=e;e=b(u),e===$?a[e].p(u,r):(N(),y(a[$],1,1,()=>{a[$]=null}),D(),s=a[e],s?s.p(u,r):(s=a[e]=i[e](u),s.c()),w(s,1),s.m(n.parentNode,n))},i(u){o||(w(s),o=!0)},o(u){y(s),o=!1},d(u){a[e].d(u),u&&v(n),u&&v(l),f=!1,m()}}}function V(c){let e,s;return e=new z({props:{$$slots:{default:[U]},$$scope:{ctx:c}}}),{c(){H(e.$$.fragment)},m(n,l){L(e,n,l),s=!0},p(n,[l]){const t={};l&71&&(t.$$scope={dirty:l,ctx:n}),e.$set(t)},i(n){s||(w(e.$$.fragment,n),s=!0)},o(n){y(e.$$.fragment,n),s=!1},d(n){S(e,n)}}}function W(c,e,s){let n="",l=!1,t=!1;async function o(){if(!l){s(1,l=!0);try{await C.admins.requestPasswordReset(n),s(2,t=!0)}catch(m){C.errorResponseHandler(m)}s(1,l=!1)}}function f(){n=this.value,s(0,n)}return[n,l,t,o,f]}class Y extends M{constructor(e){super(),T(this,e,W,V,j,{})}}export{Y as default};
|
||||
@@ -1,4 +0,0 @@
|
||||
import{S as z,i as G,s as I,F as J,c as S,m as T,t as v,a as y,d as L,C as M,E as N,g as _,k as W,n as Y,o as b,R as j,G as A,p as B,q as D,e as m,w as C,b as h,f as d,r as F,h as k,u as q,v as K,y as E,x as O,z as H}from"./index.2a48c906.js";function Q(r){let e,t,l,s,n,o,c,i,a,u,g,$,p=r[3]&&R(r);return o=new D({props:{class:"form-field required",name:"password",$$slots:{default:[V,({uniqueId:f})=>({8:f}),({uniqueId:f})=>f?256:0]},$$scope:{ctx:r}}}),{c(){e=m("form"),t=m("div"),l=m("h5"),s=C(`Type your password to confirm changing your email address
|
||||
`),p&&p.c(),n=h(),S(o.$$.fragment),c=h(),i=m("button"),a=m("span"),a.textContent="Confirm new email",d(t,"class","content txt-center m-b-base"),d(a,"class","txt"),d(i,"type","submit"),d(i,"class","btn btn-lg btn-block"),i.disabled=r[1],F(i,"btn-loading",r[1])},m(f,w){_(f,e,w),k(e,t),k(t,l),k(l,s),p&&p.m(l,null),k(e,n),T(o,e,null),k(e,c),k(e,i),k(i,a),u=!0,g||($=q(e,"submit",K(r[4])),g=!0)},p(f,w){f[3]?p?p.p(f,w):(p=R(f),p.c(),p.m(l,null)):p&&(p.d(1),p=null);const P={};w&769&&(P.$$scope={dirty:w,ctx:f}),o.$set(P),(!u||w&2)&&(i.disabled=f[1]),(!u||w&2)&&F(i,"btn-loading",f[1])},i(f){u||(v(o.$$.fragment,f),u=!0)},o(f){y(o.$$.fragment,f),u=!1},d(f){f&&b(e),p&&p.d(),L(o),g=!1,$()}}}function U(r){let e,t,l,s,n;return{c(){e=m("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
|
||||
<div class="content txt-bold"><p>Successfully changed the user email address.</p>
|
||||
<p>You can now sign in with your new email address.</p></div>`,t=h(),l=m("button"),l.textContent="Close",d(e,"class","alert alert-success"),d(l,"type","button"),d(l,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,t,c),_(o,l,c),s||(n=q(l,"click",r[6]),s=!0)},p:E,i:E,o:E,d(o){o&&b(e),o&&b(t),o&&b(l),s=!1,n()}}}function R(r){let e,t,l;return{c(){e=C("to "),t=m("strong"),l=C(r[3]),d(t,"class","txt-nowrap")},m(s,n){_(s,e,n),_(s,t,n),k(t,l)},p(s,n){n&8&&O(l,s[3])},d(s){s&&b(e),s&&b(t)}}}function V(r){let e,t,l,s,n,o,c,i;return{c(){e=m("label"),t=C("Password"),s=h(),n=m("input"),d(e,"for",l=r[8]),d(n,"type","password"),d(n,"id",o=r[8]),n.required=!0,n.autofocus=!0},m(a,u){_(a,e,u),k(e,t),_(a,s,u),_(a,n,u),H(n,r[0]),n.focus(),c||(i=q(n,"input",r[7]),c=!0)},p(a,u){u&256&&l!==(l=a[8])&&d(e,"for",l),u&256&&o!==(o=a[8])&&d(n,"id",o),u&1&&n.value!==a[0]&&H(n,a[0])},d(a){a&&b(e),a&&b(s),a&&b(n),c=!1,i()}}}function X(r){let e,t,l,s;const n=[U,Q],o=[];function c(i,a){return i[2]?0:1}return e=c(r),t=o[e]=n[e](r),{c(){t.c(),l=N()},m(i,a){o[e].m(i,a),_(i,l,a),s=!0},p(i,a){let u=e;e=c(i),e===u?o[e].p(i,a):(W(),y(o[u],1,1,()=>{o[u]=null}),Y(),t=o[e],t?t.p(i,a):(t=o[e]=n[e](i),t.c()),v(t,1),t.m(l.parentNode,l))},i(i){s||(v(t),s=!0)},o(i){y(t),s=!1},d(i){o[e].d(i),i&&b(l)}}}function Z(r){let e,t;return e=new J({props:{nobranding:!0,$$slots:{default:[X]},$$scope:{ctx:r}}}),{c(){S(e.$$.fragment)},m(l,s){T(e,l,s),t=!0},p(l,[s]){const n={};s&527&&(n.$$scope={dirty:s,ctx:l}),e.$set(n)},i(l){t||(v(e.$$.fragment,l),t=!0)},o(l){y(e.$$.fragment,l),t=!1},d(l){L(e,l)}}}function x(r,e,t){let l,{params:s}=e,n="",o=!1,c=!1;async function i(){if(o)return;t(1,o=!0);const g=new j("../");try{const $=A(s==null?void 0:s.token);await g.collection($.collectionId).confirmEmailChange(s==null?void 0:s.token,n),t(2,c=!0)}catch($){B.errorResponseHandler($)}t(1,o=!1)}const a=()=>window.close();function u(){n=this.value,t(0,n)}return r.$$set=g=>{"params"in g&&t(5,s=g.params)},r.$$.update=()=>{r.$$.dirty&32&&t(3,l=M.getJWTPayload(s==null?void 0:s.token).newEmail||"")},[n,o,c,l,i,s,a,u]}class te extends z{constructor(e){super(),G(this,e,x,Z,I,{params:5})}}export{te as default};
|
||||
@@ -0,0 +1,4 @@
|
||||
import{S as z,i as A,s as G,F as I,c as T,m as L,t as v,a as y,d as R,C as J,E as M,g as _,k as N,n as W,o as b,G as Y,H as j,p as B,q as D,e as m,w as C,b as h,f as d,r as P,h as k,u as q,v as K,y as E,x as O,z as F}from"./index.536fddcb.js";function Q(r){let e,t,s,l,n,o,c,i,a,u,g,$,p=r[3]&&S(r);return o=new D({props:{class:"form-field required",name:"password",$$slots:{default:[V,({uniqueId:f})=>({8:f}),({uniqueId:f})=>f?256:0]},$$scope:{ctx:r}}}),{c(){e=m("form"),t=m("div"),s=m("h5"),l=C(`Type your password to confirm changing your email address
|
||||
`),p&&p.c(),n=h(),T(o.$$.fragment),c=h(),i=m("button"),a=m("span"),a.textContent="Confirm new email",d(t,"class","content txt-center m-b-base"),d(a,"class","txt"),d(i,"type","submit"),d(i,"class","btn btn-lg btn-block"),i.disabled=r[1],P(i,"btn-loading",r[1])},m(f,w){_(f,e,w),k(e,t),k(t,s),k(s,l),p&&p.m(s,null),k(e,n),L(o,e,null),k(e,c),k(e,i),k(i,a),u=!0,g||($=q(e,"submit",K(r[4])),g=!0)},p(f,w){f[3]?p?p.p(f,w):(p=S(f),p.c(),p.m(s,null)):p&&(p.d(1),p=null);const H={};w&769&&(H.$$scope={dirty:w,ctx:f}),o.$set(H),(!u||w&2)&&(i.disabled=f[1]),(!u||w&2)&&P(i,"btn-loading",f[1])},i(f){u||(v(o.$$.fragment,f),u=!0)},o(f){y(o.$$.fragment,f),u=!1},d(f){f&&b(e),p&&p.d(),R(o),g=!1,$()}}}function U(r){let e,t,s,l,n;return{c(){e=m("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
|
||||
<div class="content txt-bold"><p>Successfully changed the user email address.</p>
|
||||
<p>You can now sign in with your new email address.</p></div>`,t=h(),s=m("button"),s.textContent="Close",d(e,"class","alert alert-success"),d(s,"type","button"),d(s,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,t,c),_(o,s,c),l||(n=q(s,"click",r[6]),l=!0)},p:E,i:E,o:E,d(o){o&&b(e),o&&b(t),o&&b(s),l=!1,n()}}}function S(r){let e,t,s;return{c(){e=C("to "),t=m("strong"),s=C(r[3]),d(t,"class","txt-nowrap")},m(l,n){_(l,e,n),_(l,t,n),k(t,s)},p(l,n){n&8&&O(s,l[3])},d(l){l&&b(e),l&&b(t)}}}function V(r){let e,t,s,l,n,o,c,i;return{c(){e=m("label"),t=C("Password"),l=h(),n=m("input"),d(e,"for",s=r[8]),d(n,"type","password"),d(n,"id",o=r[8]),n.required=!0,n.autofocus=!0},m(a,u){_(a,e,u),k(e,t),_(a,l,u),_(a,n,u),F(n,r[0]),n.focus(),c||(i=q(n,"input",r[7]),c=!0)},p(a,u){u&256&&s!==(s=a[8])&&d(e,"for",s),u&256&&o!==(o=a[8])&&d(n,"id",o),u&1&&n.value!==a[0]&&F(n,a[0])},d(a){a&&b(e),a&&b(l),a&&b(n),c=!1,i()}}}function X(r){let e,t,s,l;const n=[U,Q],o=[];function c(i,a){return i[2]?0:1}return e=c(r),t=o[e]=n[e](r),{c(){t.c(),s=M()},m(i,a){o[e].m(i,a),_(i,s,a),l=!0},p(i,a){let u=e;e=c(i),e===u?o[e].p(i,a):(N(),y(o[u],1,1,()=>{o[u]=null}),W(),t=o[e],t?t.p(i,a):(t=o[e]=n[e](i),t.c()),v(t,1),t.m(s.parentNode,s))},i(i){l||(v(t),l=!0)},o(i){y(t),l=!1},d(i){o[e].d(i),i&&b(s)}}}function Z(r){let e,t;return e=new I({props:{nobranding:!0,$$slots:{default:[X]},$$scope:{ctx:r}}}),{c(){T(e.$$.fragment)},m(s,l){L(e,s,l),t=!0},p(s,[l]){const n={};l&527&&(n.$$scope={dirty:l,ctx:s}),e.$set(n)},i(s){t||(v(e.$$.fragment,s),t=!0)},o(s){y(e.$$.fragment,s),t=!1},d(s){R(e,s)}}}function x(r,e,t){let s,{params:l}=e,n="",o=!1,c=!1;async function i(){if(o)return;t(1,o=!0);const g=new Y("../");try{const $=j(l==null?void 0:l.token);await g.collection($.collectionId).confirmEmailChange(l==null?void 0:l.token,n),t(2,c=!0)}catch($){B.errorResponseHandler($)}t(1,o=!1)}const a=()=>window.close();function u(){n=this.value,t(0,n)}return r.$$set=g=>{"params"in g&&t(5,l=g.params)},r.$$.update=()=>{r.$$.dirty&32&&t(3,s=J.getJWTPayload(l==null?void 0:l.token).newEmail||"")},[n,o,c,s,i,l,a,u]}class te extends z{constructor(e){super(),A(this,e,x,Z,G,{params:5})}}export{te as default};
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
import{S as J,i as M,s as W,F as Y,c as H,m as N,t as P,a as q,d as L,C as j,E as A,g as _,k as B,n as D,o as m,R as K,G as O,p as Q,q as E,e as b,w as R,b as y,f as p,r as G,h as w,u as S,v as U,y as F,x as V,z as h}from"./index.2a48c906.js";function X(r){let e,l,s,n,t,o,c,u,i,a,v,k,g,C,d=r[4]&&I(r);return o=new E({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),u=new E({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=R(`Reset your user password
|
||||
`),d&&d.c(),t=y(),H(o.$$.fragment),c=y(),H(u.$$.fragment),i=y(),a=b("button"),v=b("span"),v.textContent="Set new password",p(l,"class","content txt-center m-b-base"),p(v,"class","txt"),p(a,"type","submit"),p(a,"class","btn btn-lg btn-block"),a.disabled=r[2],G(a,"btn-loading",r[2])},m(f,$){_(f,e,$),w(e,l),w(l,s),w(s,n),d&&d.m(s,null),w(e,t),N(o,e,null),w(e,c),N(u,e,null),w(e,i),w(e,a),w(a,v),k=!0,g||(C=S(e,"submit",U(r[5])),g=!0)},p(f,$){f[4]?d?d.p(f,$):(d=I(f),d.c(),d.m(s,null)):d&&(d.d(1),d=null);const T={};$&3073&&(T.$$scope={dirty:$,ctx:f}),o.$set(T);const z={};$&3074&&(z.$$scope={dirty:$,ctx:f}),u.$set(z),(!k||$&4)&&(a.disabled=f[2]),(!k||$&4)&&G(a,"btn-loading",f[2])},i(f){k||(P(o.$$.fragment,f),P(u.$$.fragment,f),k=!0)},o(f){q(o.$$.fragment,f),q(u.$$.fragment,f),k=!1},d(f){f&&m(e),d&&d.d(),L(o),L(u),g=!1,C()}}}function Z(r){let e,l,s,n,t;return{c(){e=b("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
|
||||
import{S as I,i as J,s as M,F as W,c as F,m as N,t as P,a as q,d as L,C as Y,E as j,g as _,k as B,n as D,o as m,G as K,H as O,p as Q,q as A,e as b,w as h,b as y,f as p,r as E,h as w,u as H,v as U,y as S,x as V,z as R}from"./index.536fddcb.js";function X(r){let e,l,s,n,t,o,c,u,i,a,v,k,g,C,d=r[4]&&G(r);return o=new A({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),u=new A({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:r}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=h(`Reset your user password
|
||||
`),d&&d.c(),t=y(),F(o.$$.fragment),c=y(),F(u.$$.fragment),i=y(),a=b("button"),v=b("span"),v.textContent="Set new password",p(l,"class","content txt-center m-b-base"),p(v,"class","txt"),p(a,"type","submit"),p(a,"class","btn btn-lg btn-block"),a.disabled=r[2],E(a,"btn-loading",r[2])},m(f,$){_(f,e,$),w(e,l),w(l,s),w(s,n),d&&d.m(s,null),w(e,t),N(o,e,null),w(e,c),N(u,e,null),w(e,i),w(e,a),w(a,v),k=!0,g||(C=H(e,"submit",U(r[5])),g=!0)},p(f,$){f[4]?d?d.p(f,$):(d=G(f),d.c(),d.m(s,null)):d&&(d.d(1),d=null);const T={};$&3073&&(T.$$scope={dirty:$,ctx:f}),o.$set(T);const z={};$&3074&&(z.$$scope={dirty:$,ctx:f}),u.$set(z),(!k||$&4)&&(a.disabled=f[2]),(!k||$&4)&&E(a,"btn-loading",f[2])},i(f){k||(P(o.$$.fragment,f),P(u.$$.fragment,f),k=!0)},o(f){q(o.$$.fragment,f),q(u.$$.fragment,f),k=!1},d(f){f&&m(e),d&&d.d(),L(o),L(u),g=!1,C()}}}function Z(r){let e,l,s,n,t;return{c(){e=b("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
|
||||
<div class="content txt-bold"><p>Successfully changed the user password.</p>
|
||||
<p>You can now sign in with your new password.</p></div>`,l=y(),s=b("button"),s.textContent="Close",p(e,"class","alert alert-success"),p(s,"type","button"),p(s,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,l,c),_(o,s,c),n||(t=S(s,"click",r[7]),n=!0)},p:F,i:F,o:F,d(o){o&&m(e),o&&m(l),o&&m(s),n=!1,t()}}}function I(r){let e,l,s;return{c(){e=R("for "),l=b("strong"),s=R(r[4])},m(n,t){_(n,e,t),_(n,l,t),w(l,s)},p(n,t){t&16&&V(s,n[4])},d(n){n&&m(e),n&&m(l)}}}function x(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=R("New password"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0,t.autofocus=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),h(t,r[0]),t.focus(),c||(u=S(t,"input",r[8]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&1&&t.value!==i[0]&&h(t,i[0])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function ee(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=R("New password confirm"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),h(t,r[1]),c||(u=S(t,"input",r[9]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&2&&t.value!==i[1]&&h(t,i[1])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function te(r){let e,l,s,n;const t=[Z,X],o=[];function c(u,i){return u[3]?0:1}return e=c(r),l=o[e]=t[e](r),{c(){l.c(),s=A()},m(u,i){o[e].m(u,i),_(u,s,i),n=!0},p(u,i){let a=e;e=c(u),e===a?o[e].p(u,i):(B(),q(o[a],1,1,()=>{o[a]=null}),D(),l=o[e],l?l.p(u,i):(l=o[e]=t[e](u),l.c()),P(l,1),l.m(s.parentNode,s))},i(u){n||(P(l),n=!0)},o(u){q(l),n=!1},d(u){o[e].d(u),u&&m(s)}}}function se(r){let e,l;return e=new Y({props:{nobranding:!0,$$slots:{default:[te]},$$scope:{ctx:r}}}),{c(){H(e.$$.fragment)},m(s,n){N(e,s,n),l=!0},p(s,[n]){const t={};n&2079&&(t.$$scope={dirty:n,ctx:s}),e.$set(t)},i(s){l||(P(e.$$.fragment,s),l=!0)},o(s){q(e.$$.fragment,s),l=!1},d(s){L(e,s)}}}function le(r,e,l){let s,{params:n}=e,t="",o="",c=!1,u=!1;async function i(){if(c)return;l(2,c=!0);const g=new K("../");try{const C=O(n==null?void 0:n.token);await g.collection(C.collectionId).confirmPasswordReset(n==null?void 0:n.token,t,o),l(3,u=!0)}catch(C){Q.errorResponseHandler(C)}l(2,c=!1)}const a=()=>window.close();function v(){t=this.value,l(0,t)}function k(){o=this.value,l(1,o)}return r.$$set=g=>{"params"in g&&l(6,n=g.params)},r.$$.update=()=>{r.$$.dirty&64&&l(4,s=j.getJWTPayload(n==null?void 0:n.token).email||"")},[t,o,c,u,s,i,n,a,v,k]}class oe extends J{constructor(e){super(),M(this,e,le,se,W,{params:6})}}export{oe as default};
|
||||
<p>You can now sign in with your new password.</p></div>`,l=y(),s=b("button"),s.textContent="Close",p(e,"class","alert alert-success"),p(s,"type","button"),p(s,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,l,c),_(o,s,c),n||(t=H(s,"click",r[7]),n=!0)},p:S,i:S,o:S,d(o){o&&m(e),o&&m(l),o&&m(s),n=!1,t()}}}function G(r){let e,l,s;return{c(){e=h("for "),l=b("strong"),s=h(r[4])},m(n,t){_(n,e,t),_(n,l,t),w(l,s)},p(n,t){t&16&&V(s,n[4])},d(n){n&&m(e),n&&m(l)}}}function x(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=h("New password"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0,t.autofocus=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),R(t,r[0]),t.focus(),c||(u=H(t,"input",r[8]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&1&&t.value!==i[0]&&R(t,i[0])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function ee(r){let e,l,s,n,t,o,c,u;return{c(){e=b("label"),l=h("New password confirm"),n=y(),t=b("input"),p(e,"for",s=r[10]),p(t,"type","password"),p(t,"id",o=r[10]),t.required=!0},m(i,a){_(i,e,a),w(e,l),_(i,n,a),_(i,t,a),R(t,r[1]),c||(u=H(t,"input",r[9]),c=!0)},p(i,a){a&1024&&s!==(s=i[10])&&p(e,"for",s),a&1024&&o!==(o=i[10])&&p(t,"id",o),a&2&&t.value!==i[1]&&R(t,i[1])},d(i){i&&m(e),i&&m(n),i&&m(t),c=!1,u()}}}function te(r){let e,l,s,n;const t=[Z,X],o=[];function c(u,i){return u[3]?0:1}return e=c(r),l=o[e]=t[e](r),{c(){l.c(),s=j()},m(u,i){o[e].m(u,i),_(u,s,i),n=!0},p(u,i){let a=e;e=c(u),e===a?o[e].p(u,i):(B(),q(o[a],1,1,()=>{o[a]=null}),D(),l=o[e],l?l.p(u,i):(l=o[e]=t[e](u),l.c()),P(l,1),l.m(s.parentNode,s))},i(u){n||(P(l),n=!0)},o(u){q(l),n=!1},d(u){o[e].d(u),u&&m(s)}}}function se(r){let e,l;return e=new W({props:{nobranding:!0,$$slots:{default:[te]},$$scope:{ctx:r}}}),{c(){F(e.$$.fragment)},m(s,n){N(e,s,n),l=!0},p(s,[n]){const t={};n&2079&&(t.$$scope={dirty:n,ctx:s}),e.$set(t)},i(s){l||(P(e.$$.fragment,s),l=!0)},o(s){q(e.$$.fragment,s),l=!1},d(s){L(e,s)}}}function le(r,e,l){let s,{params:n}=e,t="",o="",c=!1,u=!1;async function i(){if(c)return;l(2,c=!0);const g=new K("../");try{const C=O(n==null?void 0:n.token);await g.collection(C.collectionId).confirmPasswordReset(n==null?void 0:n.token,t,o),l(3,u=!0)}catch(C){Q.errorResponseHandler(C)}l(2,c=!1)}const a=()=>window.close();function v(){t=this.value,l(0,t)}function k(){o=this.value,l(1,o)}return r.$$set=g=>{"params"in g&&l(6,n=g.params)},r.$$.update=()=>{r.$$.dirty&64&&l(4,s=Y.getJWTPayload(n==null?void 0:n.token).email||"")},[t,o,c,u,s,i,n,a,v,k]}class oe extends I{constructor(e){super(),J(this,e,le,se,M,{params:6})}}export{oe as default};
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
import{S as v,i as y,s as w,F as x,c as C,m as g,t as $,a as L,d as H,R as M,G as P,E as S,g as r,o as a,e as u,b as _,f,u as b,y as p}from"./index.2a48c906.js";function T(o){let t,s,e,n,l;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-error-warning-line"></i></div>
|
||||
import{S as v,i as y,s as w,F as x,c as C,m as g,t as $,a as H,d as L,G as M,H as P,E as S,g as r,o as a,e as u,b as _,f,u as b,y as p}from"./index.536fddcb.js";function T(o){let t,s,e,n,l;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-error-warning-line"></i></div>
|
||||
<div class="content txt-bold"><p>Invalid or expired verification token.</p></div>`,s=_(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-danger"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(i,c){r(i,t,c),r(i,s,c),r(i,e,c),n||(l=b(e,"click",o[4]),n=!0)},p,d(i){i&&a(t),i&&a(s),i&&a(e),n=!1,l()}}}function F(o){let t,s,e,n,l;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
|
||||
<div class="content txt-bold"><p>Successfully verified email address.</p></div>`,s=_(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-success"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(i,c){r(i,t,c),r(i,s,c),r(i,e,c),n||(l=b(e,"click",o[3]),n=!0)},p,d(i){i&&a(t),i&&a(s),i&&a(e),n=!1,l()}}}function I(o){let t;return{c(){t=u("div"),t.innerHTML='<div class="loader loader-lg"><em>Please wait...</em></div>',f(t,"class","txt-center")},m(s,e){r(s,t,e)},p,d(s){s&&a(t)}}}function R(o){let t;function s(l,i){return l[1]?I:l[0]?F:T}let e=s(o),n=e(o);return{c(){n.c(),t=S()},m(l,i){n.m(l,i),r(l,t,i)},p(l,i){e===(e=s(l))&&n?n.p(l,i):(n.d(1),n=e(l),n&&(n.c(),n.m(t.parentNode,t)))},d(l){n.d(l),l&&a(t)}}}function V(o){let t,s;return t=new x({props:{nobranding:!0,$$slots:{default:[R]},$$scope:{ctx:o}}}),{c(){C(t.$$.fragment)},m(e,n){g(t,e,n),s=!0},p(e,[n]){const l={};n&67&&(l.$$scope={dirty:n,ctx:e}),t.$set(l)},i(e){s||($(t.$$.fragment,e),s=!0)},o(e){L(t.$$.fragment,e),s=!1},d(e){H(t,e)}}}function q(o,t,s){let{params:e}=t,n=!1,l=!1;i();async function i(){s(1,l=!0);const d=new M("../");try{const m=P(e==null?void 0:e.token);await d.collection(m.collectionId).confirmVerification(e==null?void 0:e.token),s(0,n=!0)}catch{s(0,n=!1)}s(1,l=!1)}const c=()=>window.close(),k=()=>window.close();return o.$$set=d=>{"params"in d&&s(2,e=d.params)},[n,l,e,c,k]}class G extends v{constructor(t){super(),y(this,t,q,V,w,{params:2})}}export{G as default};
|
||||
<div class="content txt-bold"><p>Successfully verified email address.</p></div>`,s=_(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-success"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(i,c){r(i,t,c),r(i,s,c),r(i,e,c),n||(l=b(e,"click",o[3]),n=!0)},p,d(i){i&&a(t),i&&a(s),i&&a(e),n=!1,l()}}}function I(o){let t;return{c(){t=u("div"),t.innerHTML='<div class="loader loader-lg"><em>Please wait...</em></div>',f(t,"class","txt-center")},m(s,e){r(s,t,e)},p,d(s){s&&a(t)}}}function V(o){let t;function s(l,i){return l[1]?I:l[0]?F:T}let e=s(o),n=e(o);return{c(){n.c(),t=S()},m(l,i){n.m(l,i),r(l,t,i)},p(l,i){e===(e=s(l))&&n?n.p(l,i):(n.d(1),n=e(l),n&&(n.c(),n.m(t.parentNode,t)))},d(l){n.d(l),l&&a(t)}}}function q(o){let t,s;return t=new x({props:{nobranding:!0,$$slots:{default:[V]},$$scope:{ctx:o}}}),{c(){C(t.$$.fragment)},m(e,n){g(t,e,n),s=!0},p(e,[n]){const l={};n&67&&(l.$$scope={dirty:n,ctx:e}),t.$set(l)},i(e){s||($(t.$$.fragment,e),s=!0)},o(e){H(t.$$.fragment,e),s=!1},d(e){L(t,e)}}}function A(o,t,s){let{params:e}=t,n=!1,l=!1;i();async function i(){s(1,l=!0);const d=new M("../");try{const m=P(e==null?void 0:e.token);await d.collection(m.collectionId).confirmVerification(e==null?void 0:e.token),s(0,n=!0)}catch{s(0,n=!1)}s(1,l=!1)}const c=()=>window.close(),k=()=>window.close();return o.$$set=d=>{"params"in d&&s(2,e=d.params)},[n,l,e,c,k]}class G extends v{constructor(t){super(),y(this,t,A,q,w,{params:2})}}export{G as default};
|
||||
+15
-15
@@ -1,4 +1,4 @@
|
||||
import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as p,g as t,h as I,m as ne,x as pe,t as ie,a as le,o as n,d as ce,Q as me,p as de}from"./index.2a48c906.js";import{S as fe}from"./SdkTabs.9dbb2ca3.js";function $e(o){var B,U,W,A,H,L,M,T,q,j,J,N;let i,m,l=o[0].name+"",b,d,h,f,_,$,k,c,S,v,w,R,C,g,E,r,D;return c=new fe({props:{js:`
|
||||
import{S as re,i as ae,s as be,N as ue,C as P,e as u,w as y,b as a,c as te,f as p,g as t,h as I,m as ne,x as pe,t as ie,a as le,o as n,d as ce,R as me,p as de}from"./index.536fddcb.js";import{S as fe}from"./SdkTabs.3571c26b.js";function $e(o){var B,U,W,A,H,L,T,q,M,N,j,J;let i,m,l=o[0].name+"",b,d,h,f,_,$,k,c,S,v,w,R,C,g,E,r,D;return c=new fe({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[1]}');
|
||||
@@ -32,20 +32,20 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
|
||||
// (Optionally) authenticate
|
||||
await pb.collection('users').authWithPassword('test@example.com', '123456');
|
||||
|
||||
// Subscribe to changes in any ${(M=o[0])==null?void 0:M.name} record
|
||||
pb.collection('${(T=o[0])==null?void 0:T.name}').subscribe('*', (e) {
|
||||
// Subscribe to changes in any ${(T=o[0])==null?void 0:T.name} record
|
||||
pb.collection('${(q=o[0])==null?void 0:q.name}').subscribe('*', (e) {
|
||||
console.log(e.record);
|
||||
});
|
||||
|
||||
// Subscribe to changes only in the specified record
|
||||
pb.collection('${(q=o[0])==null?void 0:q.name}').subscribe('RECORD_ID', (e) {
|
||||
pb.collection('${(M=o[0])==null?void 0:M.name}').subscribe('RECORD_ID', (e) {
|
||||
console.log(e.record);
|
||||
});
|
||||
|
||||
// Unsubscribe
|
||||
pb.collection('${(j=o[0])==null?void 0:j.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
|
||||
pb.collection('${(J=o[0])==null?void 0:J.name}').unsubscribe('*'); // remove all '*' topic subscriptions
|
||||
pb.collection('${(N=o[0])==null?void 0:N.name}').unsubscribe(); // remove all subscriptions in the collection
|
||||
pb.collection('${(N=o[0])==null?void 0:N.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
|
||||
pb.collection('${(j=o[0])==null?void 0:j.name}').unsubscribe('*'); // remove all '*' topic subscriptions
|
||||
pb.collection('${(J=o[0])==null?void 0:J.name}').unsubscribe(); // remove all subscriptions in the collection
|
||||
`}}),r=new ue({props:{content:JSON.stringify({action:"create",record:P.dummyCollectionRecord(o[0])},null,2).replace('"action": "create"','"action": "create" // create, update or delete')}}),{c(){i=u("h3"),m=y("Realtime ("),b=y(l),d=y(")"),h=a(),f=u("div"),f.innerHTML=`<p>Subscribe to realtime changes via Server-Sent Events (SSE).</p>
|
||||
<p>Events are sent for <strong>create</strong>, <strong>update</strong>
|
||||
and <strong>delete</strong> record operations (see "Event data format" section below).</p>`,_=a(),$=u("div"),$.innerHTML=`<div class="icon"><i class="ri-information-line"></i></div>
|
||||
@@ -56,7 +56,7 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
|
||||
<p>When you subscribe to an <strong>entire collection</strong>, the collection's
|
||||
<strong>ListRule</strong> will be used to determine whether the subscriber has access to receive the
|
||||
event message.</p></div>`,k=a(),te(c.$$.fragment),S=a(),v=u("h6"),v.textContent="API details",w=a(),R=u("div"),R.innerHTML=`<strong class="label label-primary">SSE</strong>
|
||||
<div class="content"><p>/api/realtime</p></div>`,C=a(),g=u("div"),g.textContent="Event data format",E=a(),te(r.$$.fragment),p(i,"class","m-b-sm"),p(f,"class","content txt-lg m-b-sm"),p($,"class","alert alert-info m-t-10 m-b-sm"),p(v,"class","m-b-xs"),p(R,"class","alert"),p(g,"class","section-title")},m(e,s){t(e,i,s),I(i,m),I(i,b),I(i,d),t(e,h,s),t(e,f,s),t(e,_,s),t(e,$,s),t(e,k,s),ne(c,e,s),t(e,S,s),t(e,v,s),t(e,w,s),t(e,R,s),t(e,C,s),t(e,g,s),t(e,E,s),ne(r,e,s),D=!0},p(e,[s]){var V,Y,z,F,G,K,X,Z,x,ee,se,oe;(!D||s&1)&&l!==(l=e[0].name+"")&&pe(b,l);const O={};s&3&&(O.js=`
|
||||
<div class="content"><p>/api/realtime</p></div>`,C=a(),g=u("div"),g.textContent="Event data format",E=a(),te(r.$$.fragment),p(i,"class","m-b-sm"),p(f,"class","content txt-lg m-b-sm"),p($,"class","alert alert-info m-t-10 m-b-sm"),p(v,"class","m-b-xs"),p(R,"class","alert"),p(g,"class","section-title")},m(e,s){t(e,i,s),I(i,m),I(i,b),I(i,d),t(e,h,s),t(e,f,s),t(e,_,s),t(e,$,s),t(e,k,s),ne(c,e,s),t(e,S,s),t(e,v,s),t(e,w,s),t(e,R,s),t(e,C,s),t(e,g,s),t(e,E,s),ne(r,e,s),D=!0},p(e,[s]){var Y,z,F,G,K,Q,X,Z,x,ee,se,oe;(!D||s&1)&&l!==(l=e[0].name+"")&&pe(b,l);const O={};s&3&&(O.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[1]}');
|
||||
@@ -66,20 +66,20 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
|
||||
// (Optionally) authenticate
|
||||
await pb.collection('users').authWithPassword('test@example.com', '123456');
|
||||
|
||||
// Subscribe to changes in any ${(V=e[0])==null?void 0:V.name} record
|
||||
pb.collection('${(Y=e[0])==null?void 0:Y.name}').subscribe('*', function (e) {
|
||||
// Subscribe to changes in any ${(Y=e[0])==null?void 0:Y.name} record
|
||||
pb.collection('${(z=e[0])==null?void 0:z.name}').subscribe('*', function (e) {
|
||||
console.log(e.record);
|
||||
});
|
||||
|
||||
// Subscribe to changes only in the specified record
|
||||
pb.collection('${(z=e[0])==null?void 0:z.name}').subscribe('RECORD_ID', function (e) {
|
||||
pb.collection('${(F=e[0])==null?void 0:F.name}').subscribe('RECORD_ID', function (e) {
|
||||
console.log(e.record);
|
||||
});
|
||||
|
||||
// Unsubscribe
|
||||
pb.collection('${(F=e[0])==null?void 0:F.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
|
||||
pb.collection('${(G=e[0])==null?void 0:G.name}').unsubscribe('*'); // remove all '*' topic subscriptions
|
||||
pb.collection('${(K=e[0])==null?void 0:K.name}').unsubscribe(); // remove all subscriptions in the collection
|
||||
pb.collection('${(G=e[0])==null?void 0:G.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
|
||||
pb.collection('${(K=e[0])==null?void 0:K.name}').unsubscribe('*'); // remove all '*' topic subscriptions
|
||||
pb.collection('${(Q=e[0])==null?void 0:Q.name}').unsubscribe(); // remove all subscriptions in the collection
|
||||
`),s&3&&(O.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
@@ -104,4 +104,4 @@ import{S as re,i as ae,s as be,M as ue,C as P,e as u,w as y,b as a,c as te,f as
|
||||
pb.collection('${(ee=e[0])==null?void 0:ee.name}').unsubscribe('RECORD_ID'); // remove all 'RECORD_ID' subscriptions
|
||||
pb.collection('${(se=e[0])==null?void 0:se.name}').unsubscribe('*'); // remove all '*' topic subscriptions
|
||||
pb.collection('${(oe=e[0])==null?void 0:oe.name}').unsubscribe(); // remove all subscriptions in the collection
|
||||
`),c.$set(O);const Q={};s&1&&(Q.content=JSON.stringify({action:"create",record:P.dummyCollectionRecord(e[0])},null,2).replace('"action": "create"','"action": "create" // create, update or delete')),r.$set(Q)},i(e){D||(ie(c.$$.fragment,e),ie(r.$$.fragment,e),D=!0)},o(e){le(c.$$.fragment,e),le(r.$$.fragment,e),D=!1},d(e){e&&n(i),e&&n(h),e&&n(f),e&&n(_),e&&n($),e&&n(k),ce(c,e),e&&n(S),e&&n(v),e&&n(w),e&&n(R),e&&n(C),e&&n(g),e&&n(E),ce(r,e)}}}function ve(o,i,m){let l,{collection:b=new me}=i;return o.$$set=d=>{"collection"in d&&m(0,b=d.collection)},m(1,l=P.getApiExampleUrl(de.baseUrl)),[b,l]}class De extends re{constructor(i){super(),ae(this,i,ve,$e,be,{collection:0})}}export{De as default};
|
||||
`),c.$set(O);const V={};s&1&&(V.content=JSON.stringify({action:"create",record:P.dummyCollectionRecord(e[0])},null,2).replace('"action": "create"','"action": "create" // create, update or delete')),r.$set(V)},i(e){D||(ie(c.$$.fragment,e),ie(r.$$.fragment,e),D=!0)},o(e){le(c.$$.fragment,e),le(r.$$.fragment,e),D=!1},d(e){e&&n(i),e&&n(h),e&&n(f),e&&n(_),e&&n($),e&&n(k),ce(c,e),e&&n(S),e&&n(v),e&&n(w),e&&n(R),e&&n(C),e&&n(g),e&&n(E),ce(r,e)}}}function ve(o,i,m){let l,{collection:b=new me}=i;return o.$$set=d=>{"collection"in d&&m(0,b=d.collection)},m(1,l=P.getApiExampleUrl(de.baseUrl)),[b,l]}class De extends re{constructor(i){super(),ae(this,i,ve,$e,be,{collection:0})}}export{De as default};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user