mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-09-20 13:30:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb8dd80859 | ||
|
|
d97b5b1f6c | ||
|
|
3885c93d59 | ||
|
|
5713cf422b | ||
|
|
9bee3bd0fd | ||
|
|
b31a0ddcd3 | ||
|
|
2bffa0e2bb | ||
|
|
8c0ef15ec2 | ||
|
|
5d46fb054e | ||
|
|
f9b2842deb | ||
|
|
0426722e99 | ||
|
|
33abc0a802 | ||
|
|
51cbe437e5 | ||
|
|
a2b89d7344 | ||
|
|
6988bf0d7c | ||
|
|
02c975467c | ||
|
|
c7ebd68a92 | ||
|
|
0876413d87 | ||
|
|
c0fe600d14 | ||
|
|
aec4870acd | ||
|
|
bf9a7b1e3d | ||
|
|
46186f84f0 | ||
|
|
4cc797071b | ||
|
|
d9af1475ef | ||
|
|
3e3c316da1 | ||
|
|
0efbbb0d10 | ||
|
|
5d32d22ff5 | ||
|
|
e81d85ae7c | ||
|
|
b930de6ff5 | ||
|
|
628efb7a7f | ||
|
|
201e2ccb05 | ||
|
|
e49025c8e5 | ||
|
|
9f6010d38d | ||
|
|
409bcdaa96 | ||
|
|
2554192c06 | ||
|
|
d135b1e686 | ||
|
|
4c5abd5bd9 | ||
|
|
f3a836eb7c | ||
|
|
fb5070d6ab | ||
|
|
c9f39ba167 | ||
|
|
e29655aba9 | ||
|
|
32de8ed04a | ||
|
|
783a99d7cc | ||
|
|
42a008f828 | ||
|
|
d0d5f3d005 | ||
|
|
b5be7f2d3c | ||
|
|
334a81c837 | ||
|
|
d68786df9c | ||
|
|
70f0e7e141 | ||
|
|
75bba1d0f9 | ||
|
|
81cc276ab9 | ||
|
|
ffc848e92d | ||
|
|
778869c318 | ||
|
|
8e7e0aa5ff | ||
|
|
e718b88391 | ||
|
|
865408a9b4 | ||
|
|
7407bdcaf5 | ||
|
|
953feb47fd | ||
|
|
60a4e3f4d2 | ||
|
|
d61ec398a2 | ||
|
|
9f1946057f | ||
|
|
824c7db388 |
@@ -1,3 +1,96 @@
|
||||
## v0.27.2
|
||||
|
||||
- Added workers pool when cascade deleting record files to minimize _"thread exhaustion"_ errors ([#6780](https://github.com/pocketbase/pocketbase/discussions/6780)).
|
||||
|
||||
- Updated the `:excerpt` fields modifier to properly account for multibyte characters ([#6778](https://github.com/pocketbase/pocketbase/issues/6778)).
|
||||
|
||||
- Use `rowid` as count column for non-view collections to minimize the need of having the id field in a covering index ([#6739](https://github.com/pocketbase/pocketbase/discussions/6739))
|
||||
|
||||
|
||||
## v0.27.1
|
||||
|
||||
- Updated example `geoPoint` API preview body data.
|
||||
|
||||
- Added JSVM `new GeoPointField({ ... })` constructor.
|
||||
|
||||
- Added _partial_ WebP thumbs generation (_the thumbs will be stored as PNG_; [#6744](https://github.com/pocketbase/pocketbase/pull/6744)).
|
||||
|
||||
- Updated npm dev dependencies.
|
||||
|
||||
|
||||
## v0.27.0
|
||||
|
||||
- ⚠️ Moved the Create and Manage API rule checks out of the `OnRecordCreateRequest` hook finalizer, **aka. now all CRUD API rules are checked BEFORE triggering their corresponding `*Request` hook**.
|
||||
This was done to minimize the confusion regarding the firing order of the request operations, making it more predictable and consistent with the other record List/View/Update/Delete request actions.
|
||||
It could be a minor breaking change if you are relying on the old behavior and have a Go `tests.ApiScenario` that is testing a Create API rule failure and expect `OnRecordCreateRequest` to be fired. In that case for example you may have to update your test scenario like:
|
||||
```go
|
||||
tests.ApiScenario{
|
||||
Name: "Example test that checks a Create API rule failure"
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/example/records",
|
||||
...
|
||||
// old:
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordCreateRequest": 1,
|
||||
},
|
||||
// new:
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
}
|
||||
```
|
||||
If you are having difficulties adjusting your code, feel free to open a [Q&A discussion](https://github.com/pocketbase/pocketbase/discussions) with the failing/problematic code sample.
|
||||
|
||||
- Added [new `geoPoint` field](https://pocketbase.io/docs/collections/#geopoint) for storing `{"lon":x,"lat":y}` geographic coordinates.
|
||||
In addition, a new [`geoDistance(lonA, lotA, lonB, lotB)` function](htts://pocketbase.io/docs/api-rules-and-filters/#geodistancelona-lata-lonb-latb) was also implemented that could be used to apply an API rule or filter constraint based on the distance (in km) between 2 geo points.
|
||||
|
||||
- Updated the `select` field UI to accommodate better larger lists and RTL languages ([#4674](https://github.com/pocketbase/pocketbase/issues/4674)).
|
||||
|
||||
- Updated the mail attachments auto MIME type detection to use `gabriel-vasile/mimetype` for consistency and broader sniffing signatures support.
|
||||
|
||||
- Forced `text/javascript` Content-Type when serving `.js`/`.mjs` collection uploaded files with the `/api/files/...` endpoint ([#6597](https://github.com/pocketbase/pocketbase/issues/6597)).
|
||||
|
||||
- Added second optional JSVM `DateTime` constructor argument for specifying a default timezone as TZ identifier when parsing the date string as alternative to a fixed offset in order to better handle daylight saving time nuances ([#6688](https://github.com/pocketbase/pocketbase/discussions/6688)):
|
||||
```js
|
||||
// the same as with CET offset: new DateTime("2025-10-26 03:00:00 +01:00")
|
||||
new DateTime("2025-10-26 03:00:00", "Europe/Amsterdam") // 2025-10-26 02:00:00.000Z
|
||||
|
||||
// the same as with CEST offset: new DateTime("2025-10-26 01:00:00 +02:00")
|
||||
new DateTime("2025-10-26 01:00:00", "Europe/Amsterdam") // 2025-10-25 23:00:00.000Z
|
||||
```
|
||||
|
||||
- Soft-deprecated the `$http.send`'s `result.raw` field in favor of `result.body` that contains the response body as plain bytes slice to avoid the discrepancies between Go and the JSVM when casting binary data to string.
|
||||
|
||||
- Updated `modernc.org/sqlite` to 1.37.0.
|
||||
|
||||
- Other minor improvements (_removed the superuser fields from the auth record create/update body examples, allowed programmatically updating the auth record password from the create/update hooks, fixed collections import error response, etc._).
|
||||
|
||||
|
||||
## v0.26.6
|
||||
|
||||
- Allow OIDC `email_verified` to be int or boolean string since some OIDC providers like AWS Cognito has non-standard userinfo response ([#6657](https://github.com/pocketbase/pocketbase/pull/6657)).
|
||||
|
||||
- Updated `modernc.org/sqlite` to 1.36.3.
|
||||
|
||||
|
||||
## v0.26.5
|
||||
|
||||
- Fixed canonical URI parts escaping when generating the S3 request signature ([#6654](https://github.com/pocketbase/pocketbase/issues/6654)).
|
||||
|
||||
|
||||
## v0.26.4
|
||||
|
||||
- Fixed `RecordErrorEvent.Error` and `CollectionErrorEvent.Error` sync with `ModelErrorEvent.Error` ([#6639](https://github.com/pocketbase/pocketbase/issues/6639)).
|
||||
|
||||
- Fixed logs details copy to clipboard action.
|
||||
|
||||
- Updated `modernc.org/sqlite` to 1.36.2.
|
||||
|
||||
|
||||
## v0.26.3
|
||||
|
||||
- Fixed and normalized logs error serialization across common types for more consistent logs error output ([#6631](https://github.com/pocketbase/pocketbase/issues/6631)).
|
||||
|
||||
|
||||
## v0.26.2
|
||||
|
||||
- Updated `golang-jwt/jwt` dependency because it comes with a [minor security fix](https://github.com/golang-jwt/jwt/security/advisories/GHSA-mh63-6h87-95cp).
|
||||
|
||||
+11
-1
@@ -2,9 +2,19 @@
|
||||
> For the most recent versions, please refer to [CHANGELOG.md](./CHANGELOG.md)
|
||||
---
|
||||
|
||||
## v0.22.34
|
||||
|
||||
- (_Backported from v0.26.6_) Allow OIDC `email_verified` to be int or boolean string since some OIDC providers like AWS Cognito has non-standard userinfo response ([#6657](https://github.com/pocketbase/pocketbase/pull/6657)).
|
||||
|
||||
|
||||
## v0.22.33
|
||||
|
||||
- (_Backported from v0.26.3_) Fixed and normalized logs error serialization across common types for more consistent logs error output ([#6631](https://github.com/pocketbase/pocketbase/issues/6631)).
|
||||
|
||||
|
||||
## v0.22.32
|
||||
|
||||
- (_Backported from v0.26.2_) - Updated `golang-jwt/jwt` dependency because it comes with a [minor security fix](https://github.com/golang-jwt/jwt/security/advisories/GHSA-mh63-6h87-95cp).
|
||||
- (_Backported from v0.26.2_) Updated `golang-jwt/jwt` dependency because it comes with a [minor security fix](https://github.com/golang-jwt/jwt/security/advisories/GHSA-mh63-6h87-95cp).
|
||||
|
||||
|
||||
## v0.22.31
|
||||
|
||||
@@ -34,7 +34,7 @@ func collectionsImport(e *core.RequestEvent) error {
|
||||
|
||||
// validation failure
|
||||
var validationErrors validation.Errors
|
||||
if errors.As(err, &validationErrors) {
|
||||
if errors.As(importErr, &validationErrors) {
|
||||
return e.BadRequestError("Failed to import collections.", validationErrors)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package apis_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -88,6 +89,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
`import2`,
|
||||
`fields`,
|
||||
},
|
||||
NotExpectedContent: []string{"Raw error:"},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnCollectionsImportRequest": 1,
|
||||
@@ -109,6 +111,71 @@ func TestCollectionsImport(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authorized as superuser + non-validator failure",
|
||||
Method: http.MethodPut,
|
||||
URL: "/api/collections/import",
|
||||
Body: strings.NewReader(`{
|
||||
"collections":[
|
||||
{
|
||||
"name": "import1",
|
||||
"fields": [
|
||||
{
|
||||
"id": "koih1lqx",
|
||||
"name": "test",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "import2",
|
||||
"fields": [
|
||||
{
|
||||
"id": "koih1lqx",
|
||||
"name": "test",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"indexes": [
|
||||
"create index idx_test on import2 (test)"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
`"data":{`,
|
||||
`"collections":{"code":"validation_collections_import_failure"`,
|
||||
`Raw error:`,
|
||||
`custom_error`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnCollectionsImportRequest": 1,
|
||||
"OnCollectionCreate": 1,
|
||||
"OnCollectionAfterCreateError": 1,
|
||||
"OnModelCreate": 1,
|
||||
"OnModelAfterCreateError": 1,
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.OnCollectionCreate().BindFunc(func(e *core.CollectionEvent) error {
|
||||
return errors.New("custom_error")
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
collections := []*core.Collection{}
|
||||
if err := app.CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := totalCollections
|
||||
if len(collections) != expected {
|
||||
t.Fatalf("Expected %d collections, got %d", expected, len(collections))
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "authorized as superuser + successful collections create",
|
||||
Method: http.MethodPut,
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ import (
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
var imageContentTypes = []string{"image/png", "image/jpg", "image/jpeg", "image/gif"}
|
||||
var imageContentTypes = []string{"image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp"}
|
||||
var defaultThumbSizes = []string{"100x100"}
|
||||
|
||||
// bindFileApi registers the file api endpoints and the corresponding handlers.
|
||||
|
||||
+80
-76
@@ -79,6 +79,11 @@ func recordsList(e *core.RequestEvent) error {
|
||||
|
||||
searchProvider := search.NewProvider(fieldsResolver).Query(query)
|
||||
|
||||
// use rowid when available to minimize the need of a covering index with the "id" field
|
||||
if !collection.IsView() {
|
||||
searchProvider.CountCol("_rowid_")
|
||||
}
|
||||
|
||||
records := []*core.Record{}
|
||||
result, err := searchProvider.ParseAndExec(e.Request.URL.Query().Encode(), &records)
|
||||
if err != nil {
|
||||
@@ -251,6 +256,73 @@ func recordCreate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
}
|
||||
}
|
||||
|
||||
// check the request and record data against the create and manage rules
|
||||
if !hasSuperuserAuth && collection.CreateRule != nil {
|
||||
dummyRecord := record.Clone()
|
||||
|
||||
dummyRandomPart := "__pb_create__" + security.PseudorandomString(6)
|
||||
|
||||
// set an id if it doesn't have already
|
||||
// (the value doesn't matter; it is used only to minimize the breaking changes with earlier versions)
|
||||
if dummyRecord.Id == "" {
|
||||
dummyRecord.Id = "__temp_id__" + dummyRandomPart
|
||||
}
|
||||
|
||||
// unset the verified field to prevent manage API rule misuse in case the rule relies on it
|
||||
dummyRecord.SetVerified(false)
|
||||
|
||||
// export the dummy record data into db params
|
||||
dummyExport, err := dummyRecord.DBExport(e.App)
|
||||
if err != nil {
|
||||
return e.BadRequestError("Failed to create record", fmt.Errorf("dummy DBExport error: %w", err))
|
||||
}
|
||||
|
||||
dummyParams := make(dbx.Params, len(dummyExport))
|
||||
selects := make([]string, 0, len(dummyExport))
|
||||
var param string
|
||||
for k, v := range dummyExport {
|
||||
k = inflector.Columnify(k) // columnify is just as extra measure in case of custom fields
|
||||
param = "__pb_create__" + k
|
||||
dummyParams[param] = v
|
||||
selects = append(selects, "{:"+param+"} AS [["+k+"]]")
|
||||
}
|
||||
|
||||
// shallow clone the current collection
|
||||
dummyCollection := *collection
|
||||
dummyCollection.Id += dummyRandomPart
|
||||
dummyCollection.Name += inflector.Columnify(dummyRandomPart)
|
||||
|
||||
withFrom := fmt.Sprintf("WITH {{%s}} as (SELECT %s)", dummyCollection.Name, strings.Join(selects, ","))
|
||||
|
||||
// check non-empty create rule
|
||||
if *dummyCollection.CreateRule != "" {
|
||||
ruleQuery := e.App.DB().Select("(1)").PreFragment(withFrom).From(dummyCollection.Name).AndBind(dummyParams)
|
||||
|
||||
resolver := core.NewRecordFieldResolver(e.App, &dummyCollection, requestInfo, true)
|
||||
|
||||
expr, err := search.FilterData(*dummyCollection.CreateRule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return e.BadRequestError("Failed to create record", fmt.Errorf("create rule build expression failure: %w", err))
|
||||
}
|
||||
ruleQuery.AndWhere(expr)
|
||||
|
||||
resolver.UpdateQuery(ruleQuery)
|
||||
|
||||
var exists int
|
||||
err = ruleQuery.Limit(1).Row(&exists)
|
||||
if err != nil || exists == 0 {
|
||||
return e.BadRequestError("Failed to create record", fmt.Errorf("create rule failure: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// check for manage rule access
|
||||
manageRuleQuery := e.App.DB().Select("(1)").PreFragment(withFrom).From(dummyCollection.Name).AndBind(dummyParams)
|
||||
if !form.HasManageAccess() &&
|
||||
hasAuthManageAccess(e.App, requestInfo, &dummyCollection, manageRuleQuery) {
|
||||
form.GrantManagerAccess()
|
||||
}
|
||||
}
|
||||
|
||||
var isOptFinalizerCalled bool
|
||||
|
||||
event := new(core.RecordRequestEvent)
|
||||
@@ -262,73 +334,6 @@ func recordCreate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
form.SetApp(e.App)
|
||||
form.SetRecord(e.Record)
|
||||
|
||||
// temporary save the record and check it against the create and manage rules
|
||||
if !hasSuperuserAuth && e.Collection.CreateRule != nil {
|
||||
dummyRecord := e.Record.Clone()
|
||||
|
||||
dummyRandomPart := "__pb_create__" + security.PseudorandomString(6)
|
||||
|
||||
// set an id if it doesn't have already
|
||||
// (the value doesn't matter; it is used only to minimize the breaking changes with earlier versions)
|
||||
if dummyRecord.Id == "" {
|
||||
dummyRecord.Id = "__temp_id__" + dummyRandomPart
|
||||
}
|
||||
|
||||
// unset the verified field to prevent manage API rule misuse in case the rule relies on it
|
||||
dummyRecord.SetVerified(false)
|
||||
|
||||
// export the dummy record data into db params
|
||||
dummyExport, err := dummyRecord.DBExport(e.App)
|
||||
if err != nil {
|
||||
return e.BadRequestError("Failed to create record", fmt.Errorf("dummy DBExport error: %w", err))
|
||||
}
|
||||
|
||||
dummyParams := make(dbx.Params, len(dummyExport))
|
||||
selects := make([]string, 0, len(dummyExport))
|
||||
var param string
|
||||
for k, v := range dummyExport {
|
||||
k = inflector.Columnify(k) // columnify is just as extra measure in case of custom fields
|
||||
param = "__pb_create__" + k
|
||||
dummyParams[param] = v
|
||||
selects = append(selects, "{:"+param+"} AS [["+k+"]]")
|
||||
}
|
||||
|
||||
// shallow clone the current collection
|
||||
dummyCollection := *e.Collection
|
||||
dummyCollection.Id += dummyRandomPart
|
||||
dummyCollection.Name += inflector.Columnify(dummyRandomPart)
|
||||
|
||||
withFrom := fmt.Sprintf("WITH {{%s}} as (SELECT %s)", dummyCollection.Name, strings.Join(selects, ","))
|
||||
|
||||
// check non-empty create rule
|
||||
if *dummyCollection.CreateRule != "" {
|
||||
ruleQuery := e.App.DB().Select("(1)").PreFragment(withFrom).From(dummyCollection.Name).AndBind(dummyParams)
|
||||
|
||||
resolver := core.NewRecordFieldResolver(e.App, &dummyCollection, requestInfo, true)
|
||||
|
||||
expr, err := search.FilterData(*dummyCollection.CreateRule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return e.BadRequestError("Failed to create record", fmt.Errorf("create rule build expression failure: %w", err))
|
||||
}
|
||||
ruleQuery.AndWhere(expr)
|
||||
|
||||
resolver.UpdateQuery(ruleQuery)
|
||||
|
||||
var exists int
|
||||
err = ruleQuery.Limit(1).Row(&exists)
|
||||
if err != nil || exists == 0 {
|
||||
return e.BadRequestError("Failed to create record", fmt.Errorf("create rule failure: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// check for manage rule access
|
||||
manageRuleQuery := e.App.DB().Select("(1)").PreFragment(withFrom).From(dummyCollection.Name).AndBind(dummyParams)
|
||||
if !form.HasManageAccess() &&
|
||||
hasAuthManageAccess(e.App, requestInfo, &dummyCollection, manageRuleQuery) {
|
||||
form.GrantManagerAccess()
|
||||
}
|
||||
}
|
||||
|
||||
err := form.Submit()
|
||||
if err != nil {
|
||||
return firstApiError(err, e.BadRequestError("Failed to create record", err))
|
||||
@@ -441,6 +446,14 @@ func recordUpdate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
}
|
||||
form.Load(data)
|
||||
|
||||
manageRuleQuery := e.App.DB().Select("(1)").From(collection.Name).AndWhere(dbx.HashExp{
|
||||
collection.Name + ".id": record.Id,
|
||||
})
|
||||
if !form.HasManageAccess() &&
|
||||
hasAuthManageAccess(e.App, requestInfo, collection, manageRuleQuery) {
|
||||
form.GrantManagerAccess()
|
||||
}
|
||||
|
||||
var isOptFinalizerCalled bool
|
||||
|
||||
event := new(core.RecordRequestEvent)
|
||||
@@ -452,15 +465,6 @@ func recordUpdate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
form.SetApp(e.App)
|
||||
form.SetRecord(e.Record)
|
||||
|
||||
manageRuleQuery := e.App.DB().Select("(1)").From(e.Collection.Name).AndWhere(dbx.HashExp{
|
||||
// note: use the original record id and not e.Record.Id because the record validations because may get overwritten
|
||||
e.Collection.Name + ".id": e.Record.LastSavedPK(),
|
||||
})
|
||||
if !form.HasManageAccess() &&
|
||||
hasAuthManageAccess(e.App, requestInfo, e.Collection, manageRuleQuery) {
|
||||
form.GrantManagerAccess()
|
||||
}
|
||||
|
||||
err := form.Submit()
|
||||
if err != nil {
|
||||
return firstApiError(err, e.BadRequestError("Failed to update record.", err))
|
||||
|
||||
@@ -1563,10 +1563,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
Body: strings.NewReader(`{"title":"test123"}`),
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordCreateRequest": 1,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "auth record submit in restricted collection (rule failure check)",
|
||||
@@ -1579,10 +1576,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordCreateRequest": 1,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "auth record submit in restricted collection (rule pass check) + expand relations",
|
||||
@@ -1731,10 +1725,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordCreateRequest": 1,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "submit via multipart form data with @jsonPayload key and satisfied @request.body rule",
|
||||
@@ -1974,14 +1965,9 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"total+":4,
|
||||
"total-":2
|
||||
}`),
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
`"data":{}`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordCreateRequest": 1,
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "@request.body.field with compute modifers (rule pass check)",
|
||||
|
||||
+2
-1
@@ -86,7 +86,7 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
|
||||
// add a default CSP
|
||||
if e.Response.Header().Get("Content-Security-Policy") == "" {
|
||||
e.Response.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' http://127.0.0.1:* data: blob:; connect-src 'self' http://127.0.0.1:*; script-src 'self' 'sha256-GRUzBA7PzKYug7pqxv5rJaec5bwDCw1Vo6/IXwvD3Tc='")
|
||||
e.Response.Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' http://127.0.0.1:* https://tile.openstreetmap.org data: blob:; connect-src 'self' http://127.0.0.1:* https://nominatim.openstreetmap.org; script-src 'self' 'sha256-GRUzBA7PzKYug7pqxv5rJaec5bwDCw1Vo6/IXwvD3Tc='")
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
@@ -259,6 +259,7 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
}
|
||||
|
||||
if listener == nil {
|
||||
//nolint:staticcheck
|
||||
return errors.New("The OnServe finalizer wasn't invoked. Did you forget to call the ServeEvent.Next() method?")
|
||||
}
|
||||
|
||||
|
||||
+19
-19
@@ -36,16 +36,16 @@ func superuserUpsertCommand(app core.App) *cobra.Command {
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.New("Missing email and password arguments.")
|
||||
return errors.New("missing email and password arguments")
|
||||
}
|
||||
|
||||
if args[0] == "" || is.EmailFormat.Validate(args[0]) != nil {
|
||||
return errors.New("Missing or invalid email address.")
|
||||
return errors.New("missing or invalid email address")
|
||||
}
|
||||
|
||||
superusersCol, err := app.FindCachedCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to fetch %q collection: %w.", core.CollectionNameSuperusers, err)
|
||||
return fmt.Errorf("failed to fetch %q collection: %w", core.CollectionNameSuperusers, err)
|
||||
}
|
||||
|
||||
superuser, err := app.FindAuthRecordByEmail(superusersCol, args[0])
|
||||
@@ -57,7 +57,7 @@ func superuserUpsertCommand(app core.App) *cobra.Command {
|
||||
superuser.SetPassword(args[1])
|
||||
|
||||
if err := app.Save(superuser); err != nil {
|
||||
return fmt.Errorf("Failed to upsert superuser account: %w.", err)
|
||||
return fmt.Errorf("failed to upsert superuser account: %w", err)
|
||||
}
|
||||
|
||||
color.Green("Successfully saved superuser %q!", superuser.Email())
|
||||
@@ -76,16 +76,16 @@ func superuserCreateCommand(app core.App) *cobra.Command {
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.New("Missing email and password arguments.")
|
||||
return errors.New("missing email and password arguments")
|
||||
}
|
||||
|
||||
if args[0] == "" || is.EmailFormat.Validate(args[0]) != nil {
|
||||
return errors.New("Missing or invalid email address.")
|
||||
return errors.New("missing or invalid email address")
|
||||
}
|
||||
|
||||
superusersCol, err := app.FindCachedCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to fetch %q collection: %w.", core.CollectionNameSuperusers, err)
|
||||
return fmt.Errorf("failed to fetch %q collection: %w", core.CollectionNameSuperusers, err)
|
||||
}
|
||||
|
||||
superuser := core.NewRecord(superusersCol)
|
||||
@@ -93,7 +93,7 @@ func superuserCreateCommand(app core.App) *cobra.Command {
|
||||
superuser.SetPassword(args[1])
|
||||
|
||||
if err := app.Save(superuser); err != nil {
|
||||
return fmt.Errorf("Failed to create new superuser account: %w.", err)
|
||||
return fmt.Errorf("failed to create new superuser account: %w", err)
|
||||
}
|
||||
|
||||
color.Green("Successfully created new superuser %q!", superuser.Email())
|
||||
@@ -112,22 +112,22 @@ func superuserUpdateCommand(app core.App) *cobra.Command {
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.New("Missing email and password arguments.")
|
||||
return errors.New("missing email and password arguments")
|
||||
}
|
||||
|
||||
if args[0] == "" || is.EmailFormat.Validate(args[0]) != nil {
|
||||
return errors.New("Missing or invalid email address.")
|
||||
return errors.New("missing or invalid email address")
|
||||
}
|
||||
|
||||
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("Superuser with email %q doesn't exist.", args[0])
|
||||
return fmt.Errorf("superuser with email %q doesn't exist", args[0])
|
||||
}
|
||||
|
||||
superuser.SetPassword(args[1])
|
||||
|
||||
if err := app.Save(superuser); err != nil {
|
||||
return fmt.Errorf("Failed to change superuser %q password: %w.", superuser.Email(), err)
|
||||
return fmt.Errorf("failed to change superuser %q password: %w", superuser.Email(), err)
|
||||
}
|
||||
|
||||
color.Green("Successfully changed superuser %q password!", superuser.Email())
|
||||
@@ -146,17 +146,17 @@ func superuserDeleteCommand(app core.App) *cobra.Command {
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) == 0 || args[0] == "" || is.EmailFormat.Validate(args[0]) != nil {
|
||||
return errors.New("Invalid or missing email address.")
|
||||
return errors.New("invalid or missing email address")
|
||||
}
|
||||
|
||||
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, args[0])
|
||||
if err != nil {
|
||||
color.Yellow("Superuser %q is missing or already deleted.", args[0])
|
||||
color.Yellow("superuser %q is missing or already deleted", args[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := app.Delete(superuser); err != nil {
|
||||
return fmt.Errorf("Failed to delete superuser %q: %w.", superuser.Email(), err)
|
||||
return fmt.Errorf("failed to delete superuser %q: %w", superuser.Email(), err)
|
||||
}
|
||||
|
||||
color.Green("Successfully deleted superuser %q!", superuser.Email())
|
||||
@@ -175,16 +175,16 @@ func superuserOTPCommand(app core.App) *cobra.Command {
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) == 0 || args[0] == "" || is.EmailFormat.Validate(args[0]) != nil {
|
||||
return errors.New("Invalid or missing email address.")
|
||||
return errors.New("invalid or missing email address")
|
||||
}
|
||||
|
||||
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("Superuser with email %q doesn't exist.", args[0])
|
||||
return fmt.Errorf("superuser with email %q doesn't exist", args[0])
|
||||
}
|
||||
|
||||
if !superuser.Collection().OTP.Enabled {
|
||||
return errors.New("OTP is not enabled for the _superusers collection.")
|
||||
return errors.New("OTP auth is not enabled for the _superusers collection")
|
||||
}
|
||||
|
||||
pass := security.RandomStringWithAlphabet(superuser.Collection().OTP.Length, "1234567890")
|
||||
@@ -196,7 +196,7 @@ func superuserOTPCommand(app core.App) *cobra.Command {
|
||||
|
||||
err = app.Save(otp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create OTP: %w", err)
|
||||
return fmt.Errorf("failed to create OTP: %w", err)
|
||||
}
|
||||
|
||||
color.New(color.BgGreen, color.FgBlack).Printf("Successfully created OTP for superuser %q:", superuser.Email())
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ type App interface {
|
||||
// after you are done working with it.
|
||||
NewFilesystem() (*filesystem.System, error)
|
||||
|
||||
// NewFilesystem creates a new local or S3 filesystem instance
|
||||
// NewBackupsFilesystem creates a new local or S3 filesystem instance
|
||||
// for managing app backups based on the current app settings.
|
||||
//
|
||||
// NB! Make sure to call Close() on the returned result
|
||||
|
||||
+60
-13
@@ -26,6 +26,8 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/store"
|
||||
"github.com/pocketbase/pocketbase/tools/subscriptions"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/spf13/cast"
|
||||
"golang.org/x/sync/semaphore"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -659,7 +661,7 @@ func (app *BaseApp) NewFilesystem() (*filesystem.System, error) {
|
||||
return filesystem.NewLocal(filepath.Join(app.DataDir(), LocalStorageDirName))
|
||||
}
|
||||
|
||||
// NewFilesystem creates a new local or S3 filesystem instance
|
||||
// NewBackupsFilesystem creates a new local or S3 filesystem instance
|
||||
// for managing app backups based on the current app settings.
|
||||
//
|
||||
// NB! Make sure to call Close() on the returned result
|
||||
@@ -1190,6 +1192,33 @@ func (app *BaseApp) initAuxDB() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// @todo remove after refactoring the FilesManager interface
|
||||
func supportFiles(m Model) bool {
|
||||
var collection *Collection
|
||||
switch v := m.(type) {
|
||||
case *Collection:
|
||||
collection = v
|
||||
case *Record:
|
||||
collection = v.Collection()
|
||||
case RecordProxy:
|
||||
if v.ProxyRecord() != nil {
|
||||
collection = v.ProxyRecord().Collection()
|
||||
}
|
||||
}
|
||||
|
||||
if collection == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, f := range collection.Fields {
|
||||
if f.Type() == FieldTypeFile {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (app *BaseApp) registerBaseHooks() {
|
||||
deletePrefix := func(prefix string) error {
|
||||
fs, err := app.NewFilesystem()
|
||||
@@ -1206,26 +1235,44 @@ func (app *BaseApp) registerBaseHooks() {
|
||||
return nil
|
||||
}
|
||||
|
||||
maxFilesDeleteWorkers := cast.ToInt64(os.Getenv("PB_FILES_DELETE_MAX_WORKERS"))
|
||||
if maxFilesDeleteWorkers <= 0 {
|
||||
maxFilesDeleteWorkers = 2000 // the value is arbitrary chosen and may change in the future
|
||||
}
|
||||
|
||||
deleteSem := semaphore.NewWeighted(maxFilesDeleteWorkers)
|
||||
|
||||
// try to delete the storage files from deleted Collection, Records, etc. model
|
||||
app.OnModelAfterDeleteSuccess().Bind(&hook.Handler[*ModelEvent]{
|
||||
Id: "__pbFilesManagerDelete__",
|
||||
Func: func(e *ModelEvent) error {
|
||||
if m, ok := e.Model.(FilesManager); ok && m.BaseFilesPath() != "" {
|
||||
if m, ok := e.Model.(FilesManager); ok && m.BaseFilesPath() != "" && supportFiles(e.Model) {
|
||||
// ensure that there is a trailing slash so that the list iterator could start walking from the prefix dir
|
||||
// (https://github.com/pocketbase/pocketbase/discussions/5246#discussioncomment-10128955)
|
||||
prefix := strings.TrimRight(m.BaseFilesPath(), "/") + "/"
|
||||
|
||||
// run in the background for "optimistic" delete to avoid
|
||||
// blocking the delete transaction
|
||||
routine.FireAndForget(func() {
|
||||
if err := deletePrefix(prefix); err != nil {
|
||||
app.Logger().Error(
|
||||
"Failed to delete storage prefix (non critical error; usually could happen because of S3 api limits)",
|
||||
slog.String("prefix", prefix),
|
||||
slog.String("error", err.Error()),
|
||||
)
|
||||
}
|
||||
})
|
||||
// note: for now assume no context cancellation
|
||||
err := deleteSem.Acquire(context.Background(), 1)
|
||||
if err != nil {
|
||||
app.Logger().Error(
|
||||
"Failed to delete storage prefix (couldn't acquire a worker)",
|
||||
slog.String("prefix", prefix),
|
||||
slog.String("error", err.Error()),
|
||||
)
|
||||
} else {
|
||||
// run in the background for "optimistic" delete to avoid blocking the delete transaction
|
||||
routine.FireAndForget(func() {
|
||||
defer deleteSem.Release(1)
|
||||
|
||||
if err := deletePrefix(prefix); err != nil {
|
||||
app.Logger().Error(
|
||||
"Failed to delete storage prefix (non critical error; usually could happen because of S3 api limits)",
|
||||
slog.String("prefix", prefix),
|
||||
slog.String("error", err.Error()),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
|
||||
@@ -170,7 +170,7 @@ func TestImportCollections(t *testing.T) {
|
||||
expectedCollectionFields := map[string]int{
|
||||
core.CollectionNameAuthOrigins: 6,
|
||||
"nologin": 10,
|
||||
"demo1": 18,
|
||||
"demo1": 19,
|
||||
"demo2": 5,
|
||||
"demo3": 5,
|
||||
"demo4": 16,
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -11,6 +14,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/dbutils"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
@@ -976,6 +980,280 @@ func TestCollectionDelete(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollectionModelEventSync(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
testCollections := make([]*core.Collection, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
testCollections[i] = core.NewBaseCollection("sync_test_" + strconv.Itoa(i))
|
||||
if err := app.Save(testCollections[i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
createModelEvent := func() *core.ModelEvent {
|
||||
event := new(core.ModelEvent)
|
||||
event.App = app
|
||||
event.Context = context.Background()
|
||||
event.Type = "test_a"
|
||||
event.Model = testCollections[0]
|
||||
return event
|
||||
}
|
||||
|
||||
createModelErrorEvent := func() *core.ModelErrorEvent {
|
||||
event := new(core.ModelErrorEvent)
|
||||
event.ModelEvent = *createModelEvent()
|
||||
event.Error = errors.New("error_a")
|
||||
return event
|
||||
}
|
||||
|
||||
changeCollectionEventBefore := func(e *core.CollectionEvent) {
|
||||
e.Type = "test_b"
|
||||
//nolint:staticcheck
|
||||
e.Context = context.WithValue(context.Background(), "test", 123)
|
||||
e.Collection = testCollections[1]
|
||||
}
|
||||
|
||||
modelEventFinalizerChange := func(e *core.ModelEvent) {
|
||||
e.Type = "test_c"
|
||||
//nolint:staticcheck
|
||||
e.Context = context.WithValue(context.Background(), "test", 456)
|
||||
e.Model = testCollections[2]
|
||||
}
|
||||
|
||||
changeCollectionEventAfter := func(e *core.CollectionEvent) {
|
||||
e.Type = "test_d"
|
||||
//nolint:staticcheck
|
||||
e.Context = context.WithValue(context.Background(), "test", 789)
|
||||
e.Collection = testCollections[3]
|
||||
}
|
||||
|
||||
expectedBeforeModelEventHandlerChecks := func(t *testing.T, e *core.ModelEvent) {
|
||||
if e.Type != "test_a" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_a", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != nil {
|
||||
t.Fatalf("Expected context value %v, got %v", nil, v)
|
||||
}
|
||||
|
||||
if e.Model.PK() != testCollections[0].Id {
|
||||
t.Fatalf("Expected collection with id %q, got %q (%d)", testCollections[0].Id, e.Model.PK(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
expectedAfterModelEventHandlerChecks := func(t *testing.T, e *core.ModelEvent) {
|
||||
if e.Type != "test_d" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_d", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != 789 {
|
||||
t.Fatalf("Expected context value %v, got %v", 789, v)
|
||||
}
|
||||
|
||||
if e.Model.PK() != testCollections[3].Id {
|
||||
t.Fatalf("Expected collection with id %q, got %q (%d)", testCollections[3].Id, e.Model.PK(), 3)
|
||||
}
|
||||
}
|
||||
|
||||
expectedBeforeCollectionEventHandlerChecks := func(t *testing.T, e *core.CollectionEvent) {
|
||||
if e.Type != "test_a" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_a", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != nil {
|
||||
t.Fatalf("Expected context value %v, got %v", nil, v)
|
||||
}
|
||||
|
||||
if e.Collection.Id != testCollections[0].Id {
|
||||
t.Fatalf("Expected collection with id %q, got %q (%d)", testCollections[0].Id, e.Collection.Id, 0)
|
||||
}
|
||||
}
|
||||
|
||||
expectedAfterCollectionEventHandlerChecks := func(t *testing.T, e *core.CollectionEvent) {
|
||||
if e.Type != "test_c" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_c", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != 456 {
|
||||
t.Fatalf("Expected context value %v, got %v", 456, v)
|
||||
}
|
||||
|
||||
if e.Collection.Id != testCollections[2].Id {
|
||||
t.Fatalf("Expected collection with id %q, got %q (%d)", testCollections[2].Id, e.Collection.Id, 2)
|
||||
}
|
||||
}
|
||||
|
||||
modelEventFinalizer := func(e *core.ModelEvent) error {
|
||||
modelEventFinalizerChange(e)
|
||||
return nil
|
||||
}
|
||||
|
||||
modelErrorEventFinalizer := func(e *core.ModelErrorEvent) error {
|
||||
modelEventFinalizerChange(&e.ModelEvent)
|
||||
e.Error = errors.New("error_c")
|
||||
return nil
|
||||
}
|
||||
|
||||
modelEventHandler := &hook.Handler[*core.ModelEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.ModelEvent) error {
|
||||
t.Run("before model", func(t *testing.T) {
|
||||
expectedBeforeModelEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after model", func(t *testing.T) {
|
||||
expectedAfterModelEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
modelErrorEventHandler := &hook.Handler[*core.ModelErrorEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.ModelErrorEvent) error {
|
||||
t.Run("before model error", func(t *testing.T) {
|
||||
expectedBeforeModelEventHandlerChecks(t, &e.ModelEvent)
|
||||
if v := e.Error.Error(); v != "error_a" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_a", v)
|
||||
}
|
||||
})
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after model error", func(t *testing.T) {
|
||||
expectedAfterModelEventHandlerChecks(t, &e.ModelEvent)
|
||||
if v := e.Error.Error(); v != "error_d" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_d", v)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
recordEventHandler := &hook.Handler[*core.CollectionEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.CollectionEvent) error {
|
||||
t.Run("before collection", func(t *testing.T) {
|
||||
expectedBeforeCollectionEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
changeCollectionEventBefore(e)
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after collection", func(t *testing.T) {
|
||||
expectedAfterCollectionEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
changeCollectionEventAfter(e)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
collectionErrorEventHandler := &hook.Handler[*core.CollectionErrorEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.CollectionErrorEvent) error {
|
||||
t.Run("before collection error", func(t *testing.T) {
|
||||
expectedBeforeCollectionEventHandlerChecks(t, &e.CollectionEvent)
|
||||
if v := e.Error.Error(); v != "error_a" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_c", v)
|
||||
}
|
||||
})
|
||||
|
||||
changeCollectionEventBefore(&e.CollectionEvent)
|
||||
e.Error = errors.New("error_b")
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after collection error", func(t *testing.T) {
|
||||
expectedAfterCollectionEventHandlerChecks(t, &e.CollectionEvent)
|
||||
if v := e.Error.Error(); v != "error_c" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_c", v)
|
||||
}
|
||||
})
|
||||
|
||||
changeCollectionEventAfter(&e.CollectionEvent)
|
||||
e.Error = errors.New("error_d")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// OnModelValidate
|
||||
app.OnCollectionValidate().Bind(recordEventHandler)
|
||||
app.OnModelValidate().Bind(modelEventHandler)
|
||||
app.OnModelValidate().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelCreate
|
||||
app.OnCollectionCreate().Bind(recordEventHandler)
|
||||
app.OnModelCreate().Bind(modelEventHandler)
|
||||
app.OnModelCreate().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelCreateExecute
|
||||
app.OnCollectionCreateExecute().Bind(recordEventHandler)
|
||||
app.OnModelCreateExecute().Bind(modelEventHandler)
|
||||
app.OnModelCreateExecute().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterCreateSuccess
|
||||
app.OnCollectionAfterCreateSuccess().Bind(recordEventHandler)
|
||||
app.OnModelAfterCreateSuccess().Bind(modelEventHandler)
|
||||
app.OnModelAfterCreateSuccess().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterCreateError
|
||||
app.OnCollectionAfterCreateError().Bind(collectionErrorEventHandler)
|
||||
app.OnModelAfterCreateError().Bind(modelErrorEventHandler)
|
||||
app.OnModelAfterCreateError().Trigger(createModelErrorEvent(), modelErrorEventFinalizer)
|
||||
|
||||
// OnModelUpdate
|
||||
app.OnCollectionUpdate().Bind(recordEventHandler)
|
||||
app.OnModelUpdate().Bind(modelEventHandler)
|
||||
app.OnModelUpdate().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelUpdateExecute
|
||||
app.OnCollectionUpdateExecute().Bind(recordEventHandler)
|
||||
app.OnModelUpdateExecute().Bind(modelEventHandler)
|
||||
app.OnModelUpdateExecute().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterUpdateSuccess
|
||||
app.OnCollectionAfterUpdateSuccess().Bind(recordEventHandler)
|
||||
app.OnModelAfterUpdateSuccess().Bind(modelEventHandler)
|
||||
app.OnModelAfterUpdateSuccess().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterUpdateError
|
||||
app.OnCollectionAfterUpdateError().Bind(collectionErrorEventHandler)
|
||||
app.OnModelAfterUpdateError().Bind(modelErrorEventHandler)
|
||||
app.OnModelAfterUpdateError().Trigger(createModelErrorEvent(), modelErrorEventFinalizer)
|
||||
|
||||
// OnModelDelete
|
||||
app.OnCollectionDelete().Bind(recordEventHandler)
|
||||
app.OnModelDelete().Bind(modelEventHandler)
|
||||
app.OnModelDelete().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelDeleteExecute
|
||||
app.OnCollectionDeleteExecute().Bind(recordEventHandler)
|
||||
app.OnModelDeleteExecute().Bind(modelEventHandler)
|
||||
app.OnModelDeleteExecute().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterDeleteSuccess
|
||||
app.OnCollectionAfterDeleteSuccess().Bind(recordEventHandler)
|
||||
app.OnModelAfterDeleteSuccess().Bind(modelEventHandler)
|
||||
app.OnModelAfterDeleteSuccess().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterDeleteError
|
||||
app.OnCollectionAfterDeleteError().Bind(collectionErrorEventHandler)
|
||||
app.OnModelAfterDeleteError().Bind(modelErrorEventHandler)
|
||||
app.OnModelAfterDeleteError().Trigger(createModelErrorEvent(), modelErrorEventFinalizer)
|
||||
}
|
||||
|
||||
func TestCollectionSaveModel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ func (app *BaseApp) IsCollectionNameUnique(name string, excludeIds ...string) bo
|
||||
// cascade and file delete actions.
|
||||
func (app *BaseApp) TruncateCollection(collection *Collection) error {
|
||||
if collection.IsView() {
|
||||
return errors.New("view collections cannot be truncated since they don't store their own records.")
|
||||
return errors.New("view collections cannot be truncated since they don't store their own records")
|
||||
}
|
||||
|
||||
return app.RunInTransaction(func(txApp App) error {
|
||||
|
||||
@@ -34,7 +34,11 @@ func onCollectionValidate(e *CollectionEvent) error {
|
||||
original,
|
||||
)
|
||||
|
||||
return validator.run()
|
||||
if err := validator.run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
func newCollectionValidator(ctx context.Context, app App, new, original *Collection) *collectionValidator {
|
||||
|
||||
+2
-2
@@ -277,7 +277,7 @@ func syncModelErrorEventWithRecordErrorEvent(me *ModelErrorEvent, re *RecordErro
|
||||
|
||||
func syncRecordErrorEventWithModelErrorEvent(re *RecordErrorEvent, me *ModelErrorEvent) {
|
||||
syncRecordEventWithModelEvent(&re.RecordEvent, &me.ModelEvent)
|
||||
me.Error = re.Error
|
||||
re.Error = me.Error
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
@@ -354,7 +354,7 @@ func syncModelErrorEventWithCollectionErrorEvent(me *ModelErrorEvent, ce *Collec
|
||||
|
||||
func syncCollectionErrorEventWithModelErrorEvent(ce *CollectionErrorEvent, me *ModelErrorEvent) {
|
||||
syncCollectionEventWithModelEvent(&ce.CollectionEvent, &me.ModelEvent)
|
||||
me.Error = ce.Error
|
||||
ce.Error = me.Error
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/pocketbase/pocketbase/core/validators"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Fields[FieldTypeGeoPoint] = func() Field {
|
||||
return &GeoPointField{}
|
||||
}
|
||||
}
|
||||
|
||||
const FieldTypeGeoPoint = "geoPoint"
|
||||
|
||||
var (
|
||||
_ Field = (*GeoPointField)(nil)
|
||||
)
|
||||
|
||||
// GeoPointField defines "geoPoint" type field for storing latitude and longitude GPS coordinates.
|
||||
//
|
||||
// You can set the record field value as [types.GeoPoint], map or serialized json object with lat-lon props.
|
||||
// The stored value is always converted to [types.GeoPoint].
|
||||
// Nil, empty map, empty bytes slice, etc. results in zero [types.GeoPoint].
|
||||
//
|
||||
// Examples of updating a record's GeoPointField value programmatically:
|
||||
//
|
||||
// record.Set("location", types.GeoPoint{Lat: 123, Lon: 456})
|
||||
// record.Set("location", map[string]any{"lat":123, "lon":456})
|
||||
// record.Set("location", []byte(`{"lat":123, "lon":456}`)
|
||||
type GeoPointField struct {
|
||||
// Name (required) is the unique name of the field.
|
||||
Name string `form:"name" json:"name"`
|
||||
|
||||
// Id is the unique stable field identifier.
|
||||
//
|
||||
// It is automatically generated from the name when adding to a collection FieldsList.
|
||||
Id string `form:"id" json:"id"`
|
||||
|
||||
// System prevents the renaming and removal of the field.
|
||||
System bool `form:"system" json:"system"`
|
||||
|
||||
// Hidden hides the field from the API response.
|
||||
Hidden bool `form:"hidden" json:"hidden"`
|
||||
|
||||
// Presentable hints the Dashboard UI to use the underlying
|
||||
// field record value in the relation preview label.
|
||||
Presentable bool `form:"presentable" json:"presentable"`
|
||||
|
||||
// ---
|
||||
|
||||
// Required will require the field coordinates to be non-zero (aka. not "Null Island").
|
||||
Required bool `form:"required" json:"required"`
|
||||
}
|
||||
|
||||
// Type implements [Field.Type] interface method.
|
||||
func (f *GeoPointField) Type() string {
|
||||
return FieldTypeGeoPoint
|
||||
}
|
||||
|
||||
// GetId implements [Field.GetId] interface method.
|
||||
func (f *GeoPointField) GetId() string {
|
||||
return f.Id
|
||||
}
|
||||
|
||||
// SetId implements [Field.SetId] interface method.
|
||||
func (f *GeoPointField) SetId(id string) {
|
||||
f.Id = id
|
||||
}
|
||||
|
||||
// GetName implements [Field.GetName] interface method.
|
||||
func (f *GeoPointField) GetName() string {
|
||||
return f.Name
|
||||
}
|
||||
|
||||
// SetName implements [Field.SetName] interface method.
|
||||
func (f *GeoPointField) SetName(name string) {
|
||||
f.Name = name
|
||||
}
|
||||
|
||||
// GetSystem implements [Field.GetSystem] interface method.
|
||||
func (f *GeoPointField) GetSystem() bool {
|
||||
return f.System
|
||||
}
|
||||
|
||||
// SetSystem implements [Field.SetSystem] interface method.
|
||||
func (f *GeoPointField) SetSystem(system bool) {
|
||||
f.System = system
|
||||
}
|
||||
|
||||
// GetHidden implements [Field.GetHidden] interface method.
|
||||
func (f *GeoPointField) GetHidden() bool {
|
||||
return f.Hidden
|
||||
}
|
||||
|
||||
// SetHidden implements [Field.SetHidden] interface method.
|
||||
func (f *GeoPointField) SetHidden(hidden bool) {
|
||||
f.Hidden = hidden
|
||||
}
|
||||
|
||||
// ColumnType implements [Field.ColumnType] interface method.
|
||||
func (f *GeoPointField) ColumnType(app App) string {
|
||||
return `JSON DEFAULT '{"lon":0,"lat":0}' NOT NULL`
|
||||
}
|
||||
|
||||
// PrepareValue implements [Field.PrepareValue] interface method.
|
||||
func (f *GeoPointField) PrepareValue(record *Record, raw any) (any, error) {
|
||||
point := types.GeoPoint{}
|
||||
err := point.Scan(raw)
|
||||
return point, err
|
||||
}
|
||||
|
||||
// ValidateValue implements [Field.ValidateValue] interface method.
|
||||
func (f *GeoPointField) ValidateValue(ctx context.Context, app App, record *Record) error {
|
||||
val, ok := record.GetRaw(f.Name).(types.GeoPoint)
|
||||
if !ok {
|
||||
return validators.ErrUnsupportedValueType
|
||||
}
|
||||
|
||||
// zero value
|
||||
if val.Lat == 0 && val.Lon == 0 {
|
||||
if f.Required {
|
||||
return validation.ErrRequired
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if val.Lat < -90 || val.Lat > 90 {
|
||||
return validation.NewError("validation_invalid_latitude", "Latitude must be between -90 and 90 degrees.")
|
||||
}
|
||||
|
||||
if val.Lon < -180 || val.Lon > 180 {
|
||||
return validation.NewError("validation_invalid_longitude", "Longitude must be between -180 and 180 degrees.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateSettings implements [Field.ValidateSettings] interface method.
|
||||
func (f *GeoPointField) ValidateSettings(ctx context.Context, app App, collection *Collection) error {
|
||||
return validation.ValidateStruct(f,
|
||||
validation.Field(&f.Id, validation.By(DefaultFieldIdValidationRule)),
|
||||
validation.Field(&f.Name, validation.By(DefaultFieldNameValidationRule)),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
func TestGeoPointFieldBaseMethods(t *testing.T) {
|
||||
testFieldBaseMethods(t, core.FieldTypeGeoPoint)
|
||||
}
|
||||
|
||||
func TestGeoPointFieldColumnType(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
f := &core.GeoPointField{}
|
||||
|
||||
expected := `JSON DEFAULT '{"lon":0,"lat":0}' NOT NULL`
|
||||
|
||||
if v := f.ColumnType(app); v != expected {
|
||||
t.Fatalf("Expected\n%q\ngot\n%q", expected, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoPointFieldPrepareValue(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
f := &core.GeoPointField{}
|
||||
record := core.NewRecord(core.NewBaseCollection("test"))
|
||||
|
||||
scenarios := []struct {
|
||||
raw any
|
||||
expected string
|
||||
}{
|
||||
{nil, `{"lon":0,"lat":0}`},
|
||||
{"", `{"lon":0,"lat":0}`},
|
||||
{[]byte{}, `{"lon":0,"lat":0}`},
|
||||
{map[string]any{}, `{"lon":0,"lat":0}`},
|
||||
{types.GeoPoint{Lon: 10, Lat: 20}, `{"lon":10,"lat":20}`},
|
||||
{&types.GeoPoint{Lon: 10, Lat: 20}, `{"lon":10,"lat":20}`},
|
||||
{[]byte(`{"lon": 10, "lat": 20}`), `{"lon":10,"lat":20}`},
|
||||
{map[string]any{"lon": 10, "lat": 20}, `{"lon":10,"lat":20}`},
|
||||
{map[string]float64{"lon": 10, "lat": 20}, `{"lon":10,"lat":20}`},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("%d_%#v", i, s.raw), func(t *testing.T) {
|
||||
v, err := f.PrepareValue(record, s.raw)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
raw, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rawStr := string(raw)
|
||||
|
||||
if rawStr != s.expected {
|
||||
t.Fatalf("Expected\n%s\ngot\n%s", s.expected, rawStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoPointFieldValidateValue(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
collection := core.NewBaseCollection("test_collection")
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
field *core.GeoPointField
|
||||
record func() *core.Record
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"invalid raw value",
|
||||
&core.GeoPointField{Name: "test"},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", 123)
|
||||
return record
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"zero field value (non-required)",
|
||||
&core.GeoPointField{Name: "test"},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{})
|
||||
return record
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"zero field value (required)",
|
||||
&core.GeoPointField{Name: "test", Required: true},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{})
|
||||
return record
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"non-zero Lat field value (required)",
|
||||
&core.GeoPointField{Name: "test", Required: true},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{Lat: 1})
|
||||
return record
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"non-zero Lon field value (required)",
|
||||
&core.GeoPointField{Name: "test", Required: true},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{Lon: 1})
|
||||
return record
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"non-zero Lat-Lon field value (required)",
|
||||
&core.GeoPointField{Name: "test", Required: true},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{Lon: -1, Lat: -2})
|
||||
return record
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"lat < -90",
|
||||
&core.GeoPointField{Name: "test"},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{Lat: -90.1})
|
||||
return record
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"lat > 90",
|
||||
&core.GeoPointField{Name: "test"},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{Lat: 90.1})
|
||||
return record
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"lon < -180",
|
||||
&core.GeoPointField{Name: "test"},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{Lon: -180.1})
|
||||
return record
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"lon > 180",
|
||||
&core.GeoPointField{Name: "test"},
|
||||
func() *core.Record {
|
||||
record := core.NewRecord(collection)
|
||||
record.SetRaw("test", types.GeoPoint{Lon: 180.1})
|
||||
return record
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
err := s.field.ValidateValue(context.Background(), app, s.record())
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
t.Fatalf("Expected hasErr %v, got %v (%v)", s.expectError, hasErr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoPointFieldValidateSettings(t *testing.T) {
|
||||
testDefaultFieldIdValidation(t, core.FieldTypeGeoPoint)
|
||||
testDefaultFieldNameValidation(t, core.FieldTypeGeoPoint)
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func (r *MigrationsRunner) Run(args ...string) error {
|
||||
color.Green("The %s table was synced with the available migrations.", r.tableName)
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("Unsupported command: %q\n", cmd)
|
||||
return fmt.Errorf("unsupported command: %q", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,12 +151,12 @@ func (r *MigrationsRunner) Up() ([]string, error) {
|
||||
// ignore empty Up action
|
||||
if m.Up != nil {
|
||||
if err := m.Up(txApp); err != nil {
|
||||
return fmt.Errorf("Failed to apply migration %s: %w", m.File, err)
|
||||
return fmt.Errorf("failed to apply migration %s: %w", m.File, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.saveAppliedMigration(txApp, m.File); err != nil {
|
||||
return fmt.Errorf("Failed to save applied migration info for %s: %w", m.File, err)
|
||||
return fmt.Errorf("failed to save applied migration info for %s: %w", m.File, err)
|
||||
}
|
||||
|
||||
applied = append(applied, m.File)
|
||||
@@ -204,12 +204,12 @@ func (r *MigrationsRunner) Down(toRevertCount int) ([]string, error) {
|
||||
// ignore empty Down action
|
||||
if m.Down != nil {
|
||||
if err := m.Down(txApp); err != nil {
|
||||
return fmt.Errorf("Failed to revert migration %s: %w", m.File, err)
|
||||
return fmt.Errorf("failed to revert migration %s: %w", m.File, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.saveRevertedMigration(txApp, m.File); err != nil {
|
||||
return fmt.Errorf("Failed to save reverted migration info for %s: %w", m.File, err)
|
||||
return fmt.Errorf("failed to save reverted migration info for %s: %w", m.File, err)
|
||||
}
|
||||
|
||||
reverted = append(reverted, m.File)
|
||||
|
||||
@@ -419,8 +419,9 @@ func (r *runner) processActiveProps() (*search.ResolverResult, error) {
|
||||
return nil, fmt.Errorf("non-filterable field %q", prop)
|
||||
}
|
||||
|
||||
// json field -> treat the rest of the props as json path
|
||||
if field != nil && field.Type() == FieldTypeJSON {
|
||||
// json or geoPoint field -> treat the rest of the props as json path
|
||||
// @todo consider converting to "JSONExtractable" interface with optional extra validation for the remaining props?
|
||||
if field != nil && (field.Type() == FieldTypeJSON || field.Type() == FieldTypeGeoPoint) {
|
||||
var jsonPath strings.Builder
|
||||
for j, p := range r.activeProps[i+1:] {
|
||||
if _, err := strconv.Atoi(p); err == nil {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -969,6 +969,13 @@ func (m *Record) GetDateTime(key string) types.DateTime {
|
||||
return d
|
||||
}
|
||||
|
||||
// GetGeoPoint returns the data value for "key" as a GeoPoint instance.
|
||||
func (m *Record) GetGeoPoint(key string) types.GeoPoint {
|
||||
point := types.GeoPoint{}
|
||||
_ = point.Scan(m.Get(key))
|
||||
return point
|
||||
}
|
||||
|
||||
// GetStringSlice returns the data value for "key" as a slice of non-zero unique strings.
|
||||
func (m *Record) GetStringSlice(key string) []string {
|
||||
return list.ToUniqueStringSlice(m.Get(key))
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -16,6 +18,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -1013,6 +1016,43 @@ func TestRecordGetStringSlice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordGetGeoPoint(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []struct {
|
||||
value any
|
||||
expected string
|
||||
}{
|
||||
{nil, `{"lon":0,"lat":0}`},
|
||||
{"", `{"lon":0,"lat":0}`},
|
||||
{0, `{"lon":0,"lat":0}`},
|
||||
{false, `{"lon":0,"lat":0}`},
|
||||
{"{}", `{"lon":0,"lat":0}`},
|
||||
{"[]", `{"lon":0,"lat":0}`},
|
||||
{[]int{1, 2}, `{"lon":0,"lat":0}`},
|
||||
{map[string]any{"lon": 1, "lat": 2}, `{"lon":1,"lat":2}`},
|
||||
{[]byte(`{"lon":1,"lat":2}`), `{"lon":1,"lat":2}`},
|
||||
{`{"lon":1,"lat":2}`, `{"lon":1,"lat":2}`},
|
||||
{types.GeoPoint{Lon: 1, Lat: 2}, `{"lon":1,"lat":2}`},
|
||||
{&types.GeoPoint{Lon: 1, Lat: 2}, `{"lon":1,"lat":2}`},
|
||||
}
|
||||
|
||||
collection := core.NewBaseCollection("test")
|
||||
record := core.NewRecord(collection)
|
||||
|
||||
for i, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("%d_%#v", i, s.value), func(t *testing.T) {
|
||||
record.Set("test", s.value)
|
||||
|
||||
pointStr := record.GetGeoPoint("test").String()
|
||||
|
||||
if pointStr != s.expected {
|
||||
t.Fatalf("Expected %q, got %q", s.expected, pointStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordGetUnsavedFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1618,6 +1658,288 @@ func TestRecordValidate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecordModelEventSync(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
col, err := app.FindCollectionByNameOrId("demo3")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testRecords := make([]*core.Record, 4)
|
||||
for i := 0; i < 4; i++ {
|
||||
testRecords[i] = core.NewRecord(col)
|
||||
testRecords[i].Set("title", "sync_test_"+strconv.Itoa(i))
|
||||
if err := app.Save(testRecords[i]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
createModelEvent := func() *core.ModelEvent {
|
||||
event := new(core.ModelEvent)
|
||||
event.App = app
|
||||
event.Context = context.Background()
|
||||
event.Type = "test_a"
|
||||
event.Model = testRecords[0]
|
||||
return event
|
||||
}
|
||||
|
||||
createModelErrorEvent := func() *core.ModelErrorEvent {
|
||||
event := new(core.ModelErrorEvent)
|
||||
event.ModelEvent = *createModelEvent()
|
||||
event.Error = errors.New("error_a")
|
||||
return event
|
||||
}
|
||||
|
||||
changeRecordEventBefore := func(e *core.RecordEvent) {
|
||||
e.Type = "test_b"
|
||||
//nolint:staticcheck
|
||||
e.Context = context.WithValue(context.Background(), "test", 123)
|
||||
e.Record = testRecords[1]
|
||||
}
|
||||
|
||||
modelEventFinalizerChange := func(e *core.ModelEvent) {
|
||||
e.Type = "test_c"
|
||||
//nolint:staticcheck
|
||||
e.Context = context.WithValue(context.Background(), "test", 456)
|
||||
e.Model = testRecords[2]
|
||||
}
|
||||
|
||||
changeRecordEventAfter := func(e *core.RecordEvent) {
|
||||
e.Type = "test_d"
|
||||
//nolint:staticcheck
|
||||
e.Context = context.WithValue(context.Background(), "test", 789)
|
||||
e.Record = testRecords[3]
|
||||
}
|
||||
|
||||
expectedBeforeModelEventHandlerChecks := func(t *testing.T, e *core.ModelEvent) {
|
||||
if e.Type != "test_a" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_a", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != nil {
|
||||
t.Fatalf("Expected context value %v, got %v", nil, v)
|
||||
}
|
||||
|
||||
if e.Model.PK() != testRecords[0].Id {
|
||||
t.Fatalf("Expected record with id %q, got %q (%d)", testRecords[0].Id, e.Model.PK(), 0)
|
||||
}
|
||||
}
|
||||
|
||||
expectedAfterModelEventHandlerChecks := func(t *testing.T, e *core.ModelEvent) {
|
||||
if e.Type != "test_d" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_d", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != 789 {
|
||||
t.Fatalf("Expected context value %v, got %v", 789, v)
|
||||
}
|
||||
|
||||
// note: currently the Model and Record values are not synced due to performance consideration
|
||||
if e.Model.PK() != testRecords[2].Id {
|
||||
t.Fatalf("Expected record with id %q, got %q (%d)", testRecords[2].Id, e.Model.PK(), 2)
|
||||
}
|
||||
}
|
||||
|
||||
expectedBeforeRecordEventHandlerChecks := func(t *testing.T, e *core.RecordEvent) {
|
||||
if e.Type != "test_a" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_a", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != nil {
|
||||
t.Fatalf("Expected context value %v, got %v", nil, v)
|
||||
}
|
||||
|
||||
if e.Record.Id != testRecords[0].Id {
|
||||
t.Fatalf("Expected record with id %q, got %q (%d)", testRecords[0].Id, e.Record.Id, 2)
|
||||
}
|
||||
}
|
||||
|
||||
expectedAfterRecordEventHandlerChecks := func(t *testing.T, e *core.RecordEvent) {
|
||||
if e.Type != "test_c" {
|
||||
t.Fatalf("Expected type %q, got %q", "test_c", e.Type)
|
||||
}
|
||||
|
||||
if v := e.Context.Value("test"); v != 456 {
|
||||
t.Fatalf("Expected context value %v, got %v", 456, v)
|
||||
}
|
||||
|
||||
// note: currently the Model and Record values are not synced due to performance consideration
|
||||
if e.Record.Id != testRecords[1].Id {
|
||||
t.Fatalf("Expected record with id %q, got %q (%d)", testRecords[1].Id, e.Record.Id, 1)
|
||||
}
|
||||
}
|
||||
|
||||
modelEventFinalizer := func(e *core.ModelEvent) error {
|
||||
modelEventFinalizerChange(e)
|
||||
return nil
|
||||
}
|
||||
|
||||
modelErrorEventFinalizer := func(e *core.ModelErrorEvent) error {
|
||||
modelEventFinalizerChange(&e.ModelEvent)
|
||||
e.Error = errors.New("error_c")
|
||||
return nil
|
||||
}
|
||||
|
||||
modelEventHandler := &hook.Handler[*core.ModelEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.ModelEvent) error {
|
||||
t.Run("before model", func(t *testing.T) {
|
||||
expectedBeforeModelEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after model", func(t *testing.T) {
|
||||
expectedAfterModelEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
modelErrorEventHandler := &hook.Handler[*core.ModelErrorEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.ModelErrorEvent) error {
|
||||
t.Run("before model error", func(t *testing.T) {
|
||||
expectedBeforeModelEventHandlerChecks(t, &e.ModelEvent)
|
||||
if v := e.Error.Error(); v != "error_a" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_a", v)
|
||||
}
|
||||
})
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after model error", func(t *testing.T) {
|
||||
expectedAfterModelEventHandlerChecks(t, &e.ModelEvent)
|
||||
if v := e.Error.Error(); v != "error_d" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_d", v)
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
recordEventHandler := &hook.Handler[*core.RecordEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.RecordEvent) error {
|
||||
t.Run("before record", func(t *testing.T) {
|
||||
expectedBeforeRecordEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
changeRecordEventBefore(e)
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after record", func(t *testing.T) {
|
||||
expectedAfterRecordEventHandlerChecks(t, e)
|
||||
})
|
||||
|
||||
changeRecordEventAfter(e)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
recordErrorEventHandler := &hook.Handler[*core.RecordErrorEvent]{
|
||||
Priority: -999,
|
||||
Func: func(e *core.RecordErrorEvent) error {
|
||||
t.Run("before record error", func(t *testing.T) {
|
||||
expectedBeforeRecordEventHandlerChecks(t, &e.RecordEvent)
|
||||
if v := e.Error.Error(); v != "error_a" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_c", v)
|
||||
}
|
||||
})
|
||||
|
||||
changeRecordEventBefore(&e.RecordEvent)
|
||||
e.Error = errors.New("error_b")
|
||||
|
||||
_ = e.Next()
|
||||
|
||||
t.Run("after record error", func(t *testing.T) {
|
||||
expectedAfterRecordEventHandlerChecks(t, &e.RecordEvent)
|
||||
if v := e.Error.Error(); v != "error_c" {
|
||||
t.Fatalf("Expected error %q, got %q", "error_c", v)
|
||||
}
|
||||
})
|
||||
|
||||
changeRecordEventAfter(&e.RecordEvent)
|
||||
e.Error = errors.New("error_d")
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// OnModelValidate
|
||||
app.OnRecordValidate().Bind(recordEventHandler)
|
||||
app.OnModelValidate().Bind(modelEventHandler)
|
||||
app.OnModelValidate().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelCreate
|
||||
app.OnRecordCreate().Bind(recordEventHandler)
|
||||
app.OnModelCreate().Bind(modelEventHandler)
|
||||
app.OnModelCreate().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelCreateExecute
|
||||
app.OnRecordCreateExecute().Bind(recordEventHandler)
|
||||
app.OnModelCreateExecute().Bind(modelEventHandler)
|
||||
app.OnModelCreateExecute().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterCreateSuccess
|
||||
app.OnRecordAfterCreateSuccess().Bind(recordEventHandler)
|
||||
app.OnModelAfterCreateSuccess().Bind(modelEventHandler)
|
||||
app.OnModelAfterCreateSuccess().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterCreateError
|
||||
app.OnRecordAfterCreateError().Bind(recordErrorEventHandler)
|
||||
app.OnModelAfterCreateError().Bind(modelErrorEventHandler)
|
||||
app.OnModelAfterCreateError().Trigger(createModelErrorEvent(), modelErrorEventFinalizer)
|
||||
|
||||
// OnModelUpdate
|
||||
app.OnRecordUpdate().Bind(recordEventHandler)
|
||||
app.OnModelUpdate().Bind(modelEventHandler)
|
||||
app.OnModelUpdate().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelUpdateExecute
|
||||
app.OnRecordUpdateExecute().Bind(recordEventHandler)
|
||||
app.OnModelUpdateExecute().Bind(modelEventHandler)
|
||||
app.OnModelUpdateExecute().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterUpdateSuccess
|
||||
app.OnRecordAfterUpdateSuccess().Bind(recordEventHandler)
|
||||
app.OnModelAfterUpdateSuccess().Bind(modelEventHandler)
|
||||
app.OnModelAfterUpdateSuccess().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterUpdateError
|
||||
app.OnRecordAfterUpdateError().Bind(recordErrorEventHandler)
|
||||
app.OnModelAfterUpdateError().Bind(modelErrorEventHandler)
|
||||
app.OnModelAfterUpdateError().Trigger(createModelErrorEvent(), modelErrorEventFinalizer)
|
||||
|
||||
// OnModelDelete
|
||||
app.OnRecordDelete().Bind(recordEventHandler)
|
||||
app.OnModelDelete().Bind(modelEventHandler)
|
||||
app.OnModelDelete().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelDeleteExecute
|
||||
app.OnRecordDeleteExecute().Bind(recordEventHandler)
|
||||
app.OnModelDeleteExecute().Bind(modelEventHandler)
|
||||
app.OnModelDeleteExecute().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterDeleteSuccess
|
||||
app.OnRecordAfterDeleteSuccess().Bind(recordEventHandler)
|
||||
app.OnModelAfterDeleteSuccess().Bind(modelEventHandler)
|
||||
app.OnModelAfterDeleteSuccess().Trigger(createModelEvent(), modelEventFinalizer)
|
||||
|
||||
// OnModelAfterDeleteError
|
||||
app.OnRecordAfterDeleteError().Bind(recordErrorEventHandler)
|
||||
app.OnModelAfterDeleteError().Bind(modelErrorEventHandler)
|
||||
app.OnModelAfterDeleteError().Trigger(createModelErrorEvent(), modelErrorEventFinalizer)
|
||||
}
|
||||
|
||||
func TestRecordSave(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
+34
-7
@@ -27,9 +27,10 @@ type RecordUpsert struct {
|
||||
accessLevel int
|
||||
|
||||
// extra password fields
|
||||
password string
|
||||
passwordConfirm string
|
||||
oldPassword string
|
||||
disablePasswordValidations bool
|
||||
password string
|
||||
passwordConfirm string
|
||||
oldPassword string
|
||||
}
|
||||
|
||||
// NewRecordUpsert creates a new [RecordUpsert] form from the provided [core.App] and [core.Record] instances
|
||||
@@ -130,6 +131,8 @@ func (form *RecordUpsert) validateFormFields() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
form.syncPasswordFields()
|
||||
|
||||
isNew := form.record.IsNew()
|
||||
|
||||
original := form.record.Original()
|
||||
@@ -165,17 +168,17 @@ func (form *RecordUpsert) validateFormFields() error {
|
||||
validation.Key(
|
||||
"password",
|
||||
validation.When(
|
||||
(isNew || form.passwordConfirm != "" || form.oldPassword != ""),
|
||||
!form.disablePasswordValidations && (isNew || form.passwordConfirm != "" || form.oldPassword != ""),
|
||||
validation.Required,
|
||||
),
|
||||
),
|
||||
validation.Key(
|
||||
"passwordConfirm",
|
||||
validation.When(
|
||||
(isNew || form.password != "" || form.oldPassword != ""),
|
||||
!form.disablePasswordValidations && (isNew || form.password != "" || form.oldPassword != ""),
|
||||
validation.Required,
|
||||
),
|
||||
validation.By(validators.Equal(form.password)),
|
||||
validation.When(!form.disablePasswordValidations, validation.By(validators.Equal(form.password))),
|
||||
),
|
||||
validation.Key(
|
||||
"oldPassword",
|
||||
@@ -183,7 +186,7 @@ func (form *RecordUpsert) validateFormFields() error {
|
||||
// - form.HasManageAccess() is not satisfied
|
||||
// - changing the existing password
|
||||
validation.When(
|
||||
!isNew && !form.HasManageAccess() && (form.password != "" || form.passwordConfirm != ""),
|
||||
!form.disablePasswordValidations && !isNew && !form.HasManageAccess() && (form.password != "" || form.passwordConfirm != ""),
|
||||
validation.Required,
|
||||
validation.By(form.checkOldPassword),
|
||||
),
|
||||
@@ -287,3 +290,27 @@ func (form *RecordUpsert) Submit() error {
|
||||
// run record validations and persist in db
|
||||
return form.app.SaveWithContext(form.ctx, form.record)
|
||||
}
|
||||
|
||||
// syncPasswordFields syncs the form's auth password fields with their
|
||||
// corresponding record field values.
|
||||
//
|
||||
// This could be useful in case the password fields were programmatically set
|
||||
// directly by modifying the related record model.
|
||||
func (form *RecordUpsert) syncPasswordFields() {
|
||||
if !form.record.Collection().IsAuth() {
|
||||
return // not an auth collection
|
||||
}
|
||||
|
||||
form.disablePasswordValidations = false
|
||||
|
||||
rawPassword := form.record.GetRaw(core.FieldNamePassword)
|
||||
if v, ok := rawPassword.(*core.PasswordFieldValue); ok && v != nil {
|
||||
if
|
||||
// programmatically set custom plain password value
|
||||
(v.Plain != "" && v.Plain != form.password) ||
|
||||
// random generated password for new record
|
||||
(v.Plain == "" && v.Hash != "" && form.record.IsNew()) {
|
||||
form.disablePasswordValidations = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,6 +887,119 @@ func TestRecordUpsertSubmitSuccess(t *testing.T) {
|
||||
testFilesCount(t, testApp, record, 2) // the file + attrs
|
||||
}
|
||||
|
||||
func TestRecordUpsertPasswordsSync(t *testing.T) {
|
||||
testApp, _ := tests.NewTestApp()
|
||||
defer testApp.Cleanup()
|
||||
|
||||
users, err := testApp.FindCollectionByNameOrId("users")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Run("new user without password", func(t *testing.T) {
|
||||
record := core.NewRecord(users)
|
||||
|
||||
form := forms.NewRecordUpsert(testApp, record)
|
||||
|
||||
err := form.Submit()
|
||||
|
||||
tests.TestValidationErrors(t, err, []string{"password", "passwordConfirm"})
|
||||
})
|
||||
|
||||
t.Run("new user with manual password", func(t *testing.T) {
|
||||
record := core.NewRecord(users)
|
||||
|
||||
form := forms.NewRecordUpsert(testApp, record)
|
||||
|
||||
record.SetPassword("1234567890")
|
||||
|
||||
err := form.Submit()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no errors, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("new user with random password", func(t *testing.T) {
|
||||
record := core.NewRecord(users)
|
||||
|
||||
form := forms.NewRecordUpsert(testApp, record)
|
||||
|
||||
record.SetRandomPassword()
|
||||
|
||||
err := form.Submit()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no errors, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("update user with no password change", func(t *testing.T) {
|
||||
record, err := testApp.FindAuthRecordByEmail(users, "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oldHash := record.GetString("password:hash")
|
||||
|
||||
form := forms.NewRecordUpsert(testApp, record)
|
||||
|
||||
err = form.Submit()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no errors, got %v", err)
|
||||
}
|
||||
|
||||
newHash := record.GetString("password:hash")
|
||||
if newHash == "" || newHash != oldHash {
|
||||
t.Fatal("Expected no password change")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("update user with manual password change", func(t *testing.T) {
|
||||
record, err := testApp.FindAuthRecordByEmail(users, "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oldHash := record.GetString("password:hash")
|
||||
|
||||
form := forms.NewRecordUpsert(testApp, record)
|
||||
|
||||
record.SetPassword("1234567890")
|
||||
|
||||
err = form.Submit()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no errors, got %v", err)
|
||||
}
|
||||
|
||||
newHash := record.GetString("password:hash")
|
||||
if newHash == "" || newHash == oldHash {
|
||||
t.Fatal("Expected password change")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("update user with random password change", func(t *testing.T) {
|
||||
record, err := testApp.FindAuthRecordByEmail(users, "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oldHash := record.GetString("password:hash")
|
||||
|
||||
form := forms.NewRecordUpsert(testApp, record)
|
||||
|
||||
record.SetRandomPassword()
|
||||
|
||||
err = form.Submit()
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no errors, got %v", err)
|
||||
}
|
||||
|
||||
newHash := record.GetString("password:hash")
|
||||
if newHash == "" || newHash == oldHash {
|
||||
t.Fatal("Expected password change")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
func testFilesCount(t *testing.T, app core.App, record *core.Record, count int) {
|
||||
|
||||
@@ -10,18 +10,19 @@ require (
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.8
|
||||
github.com/ganigeorgiev/fexpr v0.4.1
|
||||
github.com/ganigeorgiev/fexpr v0.5.0
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||
github.com/pocketbase/dbx v1.11.0
|
||||
github.com/pocketbase/tygoja v0.0.0-20250103200817-ca580d8c5119
|
||||
github.com/spf13/cast v1.7.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
golang.org/x/crypto v0.36.0
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sync v0.12.0
|
||||
modernc.org/sqlite v1.36.1
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/image v0.26.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/oauth2 v0.29.0
|
||||
golang.org/x/sync v0.13.0
|
||||
modernc.org/sqlite v1.37.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -30,7 +31,7 @@ require (
|
||||
github.com/dop251/base64dec v0.0.0-20231022112746-c6c9f9a96217 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/google/pprof v0.0.0-20250315033105-103756e64e1d // indirect
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
@@ -38,13 +39,12 @@ require (
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/image v0.25.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
modernc.org/libc v1.61.13 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/tools v0.32.0 // indirect
|
||||
modernc.org/libc v1.62.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.8.2 // indirect
|
||||
modernc.org/memory v1.9.1 // indirect
|
||||
)
|
||||
|
||||
@@ -28,8 +28,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k=
|
||||
github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
||||
github.com/ganigeorgiev/fexpr v0.5.0 h1:XA9JxtTE/Xm+g/JFI6RfZEHSiQlk+1glLvRK1Lpv/Tk=
|
||||
github.com/ganigeorgiev/fexpr v0.5.0/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE=
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
|
||||
@@ -41,8 +41,8 @@ github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVI
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20250315033105-103756e64e1d h1:tx51Lf+wdE+aavqH8TcPJoCjTf4cE8hrMzROghCely0=
|
||||
github.com/google/pprof v0.0.0-20250315033105-103756e64e1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@@ -78,33 +78,33 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
|
||||
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
|
||||
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -112,26 +112,26 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
||||
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
|
||||
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
|
||||
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
|
||||
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=
|
||||
modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
|
||||
modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
|
||||
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
|
||||
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
|
||||
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
|
||||
modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
|
||||
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ=
|
||||
modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
|
||||
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
|
||||
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
+6
-7
@@ -1,28 +1,27 @@
|
||||
version: "2"
|
||||
run:
|
||||
go: 1.23
|
||||
concurrency: 4
|
||||
timeout: 10m
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- asasalint
|
||||
- asciicheck
|
||||
- gofmt
|
||||
- goimports
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nakedret
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- prealloc
|
||||
- reassign
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unused
|
||||
- whitespace
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
expectedDriverVersion = "v1.36.1"
|
||||
expectedLibcVersion = "v1.61.13"
|
||||
expectedDriverVersion = "v1.37.0"
|
||||
expectedLibcVersion = "v1.62.1"
|
||||
|
||||
// ModerncDepsCheckHookId is the id of the hook that performs the modernc.org/* deps checks.
|
||||
// It could be used for removing/unbinding the hook if you don't want the checks.
|
||||
|
||||
@@ -204,13 +204,13 @@ func (p *plugin) update(withBackup bool) error {
|
||||
// try again with an .exe extension
|
||||
newExec = newExec + ".exe"
|
||||
if _, fallbackErr := os.Stat(newExec); fallbackErr != nil {
|
||||
return fmt.Errorf("The executable in the extracted path is missing or it is inaccessible: %v, %v", err, fallbackErr)
|
||||
return fmt.Errorf("the executable in the extracted path is missing or it is inaccessible: %v, %v", err, fallbackErr)
|
||||
}
|
||||
}
|
||||
|
||||
// rename the current executable
|
||||
if err := os.Rename(oldExec, renamedOldExec); err != nil {
|
||||
return fmt.Errorf("Failed to rename the current executable: %w", err)
|
||||
return fmt.Errorf("failed to rename the current executable: %w", err)
|
||||
}
|
||||
|
||||
tryToRevertExecChanges := func() {
|
||||
@@ -227,7 +227,7 @@ func (p *plugin) update(withBackup bool) error {
|
||||
// replace with the extracted binary
|
||||
if err := os.Rename(newExec, oldExec); err != nil {
|
||||
tryToRevertExecChanges()
|
||||
return fmt.Errorf("Failed replacing the executable: %w", err)
|
||||
return fmt.Errorf("failed replacing the executable: %w", err)
|
||||
}
|
||||
|
||||
if withBackup {
|
||||
|
||||
+27
-9
@@ -495,6 +495,10 @@ func baseBinds(vm *goja.Runtime) {
|
||||
instance := &core.FileField{}
|
||||
return structConstructorUnmarshal(vm, call, instance)
|
||||
})
|
||||
vm.Set("GeoPointField", func(call goja.ConstructorCall) *goja.Object {
|
||||
instance := &core.GeoPointField{}
|
||||
return structConstructorUnmarshal(vm, call, instance)
|
||||
})
|
||||
// ---
|
||||
|
||||
vm.Set("MailerMessage", func(call goja.ConstructorCall) *goja.Object {
|
||||
@@ -548,9 +552,18 @@ func baseBinds(vm *goja.Runtime) {
|
||||
vm.Set("DateTime", func(call goja.ConstructorCall) *goja.Object {
|
||||
instance := types.NowDateTime()
|
||||
|
||||
val, _ := call.Argument(0).Export().(string)
|
||||
if val != "" {
|
||||
instance, _ = types.ParseDateTime(val)
|
||||
rawDate, _ := call.Argument(0).Export().(string)
|
||||
locName, _ := call.Argument(1).Export().(string)
|
||||
if rawDate != "" && locName != "" {
|
||||
loc, err := time.LoadLocation(locName)
|
||||
if err != nil {
|
||||
loc = time.UTC
|
||||
}
|
||||
|
||||
instance, _ = types.ParseDateTime(cast.ToTimeInDefaultLocation(rawDate, loc))
|
||||
} else if rawDate != "" {
|
||||
// forward directly to ParseDateTime to preserve the original behavior
|
||||
instance, _ = types.ParseDateTime(rawDate)
|
||||
}
|
||||
|
||||
instanceValue := vm.ToValue(instance).(*goja.Object)
|
||||
@@ -774,11 +787,15 @@ func httpClientBinds(vm *goja.Runtime) {
|
||||
})
|
||||
|
||||
type sendResult struct {
|
||||
JSON any `json:"json"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Cookies map[string]*http.Cookie `json:"cookies"`
|
||||
Raw string `json:"raw"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
JSON any `json:"json"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Cookies map[string]*http.Cookie `json:"cookies"`
|
||||
|
||||
// Deprecated: consider using Body instead
|
||||
Raw string `json:"raw"`
|
||||
|
||||
Body []byte `json:"body"`
|
||||
StatusCode int `json:"statusCode"`
|
||||
}
|
||||
|
||||
type sendConfig struct {
|
||||
@@ -883,6 +900,7 @@ func httpClientBinds(vm *goja.Runtime) {
|
||||
Headers: map[string][]string{},
|
||||
Cookies: map[string]*http.Cookie{},
|
||||
Raw: string(bodyRaw),
|
||||
Body: bodyRaw,
|
||||
}
|
||||
|
||||
for k, v := range res.Header {
|
||||
@@ -893,7 +911,7 @@ func httpClientBinds(vm *goja.Runtime) {
|
||||
result.Cookies[v.Name] = v
|
||||
}
|
||||
|
||||
if len(result.Raw) != 0 {
|
||||
if len(result.Body) > 0 {
|
||||
// try as map
|
||||
result.JSON = map[string]any{}
|
||||
if err := json.Unmarshal(bodyRaw, &result.JSON); err != nil {
|
||||
|
||||
+48
-10
@@ -44,7 +44,7 @@ func TestBaseBindsCount(t *testing.T) {
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
|
||||
testBindsCount(vm, "this", 33, t)
|
||||
testBindsCount(vm, "this", 34, t)
|
||||
}
|
||||
|
||||
func TestBaseBindsSleep(t *testing.T) {
|
||||
@@ -399,6 +399,10 @@ func TestBaseBindsNamedFields(t *testing.T) {
|
||||
"new FileField({name: 'test'})",
|
||||
isType[*core.FileField],
|
||||
},
|
||||
{
|
||||
"new GeoPointField({name: 'test'})",
|
||||
isType[*core.GeoPointField],
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
@@ -568,15 +572,35 @@ func TestBaseBindsDateTime(t *testing.T) {
|
||||
baseBinds(vm)
|
||||
|
||||
_, err := vm.RunString(`
|
||||
const v0 = new DateTime();
|
||||
if (v0.isZero()) {
|
||||
throw new Error('Expected to fallback to now, got zero value');
|
||||
const now = new DateTime();
|
||||
if (now.isZero()) {
|
||||
throw new Error('(now) Expected to fallback to now, got zero value');
|
||||
}
|
||||
|
||||
const v1 = new DateTime('2023-01-01 00:00:00.000Z');
|
||||
const expected = "2023-01-01 00:00:00.000Z"
|
||||
if (v1.string() != expected) {
|
||||
throw new Error('Expected ' + expected + ', got ', v1.string());
|
||||
const nowPart = now.string().substring(0, 19)
|
||||
|
||||
const scenarios = [
|
||||
// empty datetime string and no custom location
|
||||
{date: new DateTime(''), expected: nowPart},
|
||||
// empty datetime string and custom default location (should be ignored)
|
||||
{date: new DateTime('', 'Asia/Tokyo'), expected: nowPart},
|
||||
// full datetime string and no custom default location
|
||||
{date: new DateTime('2023-01-01 00:00:00.000Z'), expected: "2023-01-01 00:00:00.000Z"},
|
||||
// invalid location (fallback to UTC)
|
||||
{date: new DateTime('2025-10-26 03:00:00', 'invalid'), expected: "2025-10-26 03:00:00.000Z"},
|
||||
// CET
|
||||
{date: new DateTime('2025-10-26 03:00:00', 'Europe/Amsterdam'), expected: "2025-10-26 02:00:00.000Z"},
|
||||
// CEST
|
||||
{date: new DateTime('2025-10-26 01:00:00', 'Europe/Amsterdam'), expected: "2025-10-25 23:00:00.000Z"},
|
||||
// with timezone/offset in the date string (aka. should ignore the custom default location)
|
||||
{date: new DateTime('2025-10-26 01:00:00 +0200', 'Asia/Tokyo'), expected: "2025-10-25 23:00:00.000Z"},
|
||||
];
|
||||
|
||||
for (let i = 0; i < scenarios.length; i++) {
|
||||
const s = scenarios[i];
|
||||
if (!s.date.string().includes(s.expected)) {
|
||||
throw new Error('(' + i + ') ' + s.date.string() + ' does not contain expected ' + s.expected);
|
||||
}
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
@@ -1363,6 +1387,13 @@ func TestHttpClientBindsSend(t *testing.T) {
|
||||
headers: {"content-type": "text/plain"}, // should be ignored
|
||||
})
|
||||
|
||||
// raw body response field check
|
||||
const test4 = $http.send({
|
||||
method: "post",
|
||||
url: testURL,
|
||||
body: 'test',
|
||||
})
|
||||
|
||||
const scenarios = [
|
||||
[test0, {
|
||||
"statusCode": "400",
|
||||
@@ -1395,6 +1426,13 @@ func TestHttpClientBindsSend(t *testing.T) {
|
||||
"multipart/form-data; boundary="
|
||||
],
|
||||
}],
|
||||
[test4, {
|
||||
"statusCode": "200",
|
||||
"headers.X-Custom.0": "custom_header",
|
||||
"cookies.sessionId.value": "123456",
|
||||
// {"body":"test","headers":{"accept_encoding":"gzip","content_length":"4","user_agent":"Go-http-client/1.1"},"method":"POST"}
|
||||
"body": [123,34,98,111,100,121,34,58,34,116,101,115,116,34,44,34,104,101,97,100,101,114,115,34,58,123,34,97,99,99,101,112,116,95,101,110,99,111,100,105,110,103,34,58,34,103,122,105,112,34,44,34,99,111,110,116,101,110,116,95,108,101,110,103,116,104,34,58,34,52,34,44,34,117,115,101,114,95,97,103,101,110,116,34,58,34,71,111,45,104,116,116,112,45,99,108,105,101,110,116,47,49,46,49,34,125,44,34,109,101,116,104,111,100,34,58,34,80,79,83,84,34,125],
|
||||
}],
|
||||
]
|
||||
|
||||
for (let scenario of scenarios) {
|
||||
@@ -1408,13 +1446,13 @@ func TestHttpClientBindsSend(t *testing.T) {
|
||||
// check for partial match(es)
|
||||
for (let exp of expectation) {
|
||||
if (!value.includes(exp)) {
|
||||
throw new Error('Expected ' + key + ' to contain ' + exp + ', got: ' + result.raw);
|
||||
throw new Error('Expected ' + key + ' to contain ' + exp + ', got: ' + toString(result.body));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// check for direct match
|
||||
if (value != expectation) {
|
||||
throw new Error('Expected ' + key + ' ' + expectation + ', got: ' + result.raw);
|
||||
throw new Error('Expected ' + key + ' ' + expectation + ', got: ' + toString(result.body));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,8 @@ func (data FormData) toMultipart() (*bytes.Buffer, *multipart.Writer, error) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err := io.Copy(mpw, file); err != nil {
|
||||
_, err = io.Copy(mpw, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -136,7 +137,8 @@ func (data FormData) toMultipart() (*bytes.Buffer, *multipart.Writer, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
default:
|
||||
if err := mp.WriteField(k, cast.ToString(v)); err != nil {
|
||||
err := mp.WriteField(k, cast.ToString(v))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
+4059
-3870
File diff suppressed because it is too large
Load Diff
@@ -482,6 +482,16 @@ declare class FileField implements core.FileField {
|
||||
constructor(data?: Partial<core.FileField>)
|
||||
}
|
||||
|
||||
interface GeoPointField extends core.GeoPointField{} // merge
|
||||
/**
|
||||
* {@inheritDoc core.GeoPointField}
|
||||
*
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare class GeoPointField implements core.GeoPointField {
|
||||
constructor(data?: Partial<core.GeoPointField>)
|
||||
}
|
||||
|
||||
interface MailerMessage extends mailer.Message{} // merge
|
||||
/**
|
||||
* MailerMessage defines a single email message.
|
||||
@@ -608,19 +618,27 @@ declare class Timezone implements time.Location {
|
||||
interface DateTime extends types.DateTime{} // merge
|
||||
/**
|
||||
* DateTime defines a single DateTime type instance.
|
||||
* The returned date is always represented in UTC.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ` + "```" + `js
|
||||
* const dt0 = new DateTime() // now
|
||||
*
|
||||
* // full datetime string
|
||||
* const dt1 = new DateTime('2023-07-01 00:00:00.000Z')
|
||||
*
|
||||
* // datetime string with default "parse in" timezone location
|
||||
* //
|
||||
* // similar to new DateTime('2023-07-01 00:00:00 +01:00') or new DateTime('2023-07-01 00:00:00 +02:00')
|
||||
* // but accounts for the daylight saving time (DST)
|
||||
* const dt2 = new DateTime('2023-07-01 00:00:00', 'Europe/Amsterdam')
|
||||
* ` + "```" + `
|
||||
*
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare class DateTime implements types.DateTime {
|
||||
constructor(date?: string)
|
||||
constructor(date?: string, defaultParseInLocation?: string)
|
||||
}
|
||||
|
||||
interface ValidationError extends ozzo_validation.Error{} // merge
|
||||
@@ -1077,7 +1095,7 @@ declare namespace $http {
|
||||
* console.log(res.statusCode) // the response HTTP status code
|
||||
* console.log(res.headers) // the response headers (eg. res.headers['X-Custom'][0])
|
||||
* console.log(res.cookies) // the response cookies (eg. res.cookies.sessionId.value)
|
||||
* console.log(res.raw) // the response body as plain text
|
||||
* console.log(res.body) // the response body as raw bytes slice
|
||||
* console.log(res.json) // the response body as parsed json array or map
|
||||
* ` + "```" + `
|
||||
*/
|
||||
@@ -1094,8 +1112,11 @@ declare namespace $http {
|
||||
statusCode: number,
|
||||
headers: { [key:string]: Array<string> },
|
||||
cookies: { [key:string]: http.Cookie },
|
||||
raw: string,
|
||||
json: any,
|
||||
body: Array<number>,
|
||||
|
||||
// @deprecated please use toString(result.body) instead
|
||||
raw: string,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ func (p *plugin) registerHooks() error {
|
||||
func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmtErr := fmt.Errorf("Failed to execute %s:\n - %v", file, err)
|
||||
fmtErr := fmt.Errorf("failed to execute %s:\n - %v", file, err)
|
||||
|
||||
if p.config.HooksWatch {
|
||||
color.Red("%v", fmtErr)
|
||||
|
||||
@@ -145,7 +145,7 @@ func (p *plugin) createCommand() *cobra.Command {
|
||||
|
||||
func (p *plugin) migrateCreateHandler(template string, args []string, interactive bool) (string, error) {
|
||||
if len(args) < 1 {
|
||||
return "", errors.New("Missing migration file name")
|
||||
return "", errors.New("missing migration file name")
|
||||
}
|
||||
|
||||
name := args[0]
|
||||
@@ -172,7 +172,7 @@ func (p *plugin) migrateCreateHandler(template string, args []string, interactiv
|
||||
template, templateErr = p.goBlankTemplate()
|
||||
}
|
||||
if templateErr != nil {
|
||||
return "", fmt.Errorf("Failed to resolve create template: %v\n", templateErr)
|
||||
return "", fmt.Errorf("failed to resolve create template: %v", templateErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ func (p *plugin) migrateCreateHandler(template string, args []string, interactiv
|
||||
|
||||
// save the migration file
|
||||
if err := os.WriteFile(resultFilePath, []byte(template), 0644); err != nil {
|
||||
return "", fmt.Errorf("Failed to save migration file %q: %v\n", resultFilePath, err)
|
||||
return "", fmt.Errorf("failed to save migration file %q: %v", resultFilePath, err)
|
||||
}
|
||||
|
||||
if interactive {
|
||||
@@ -199,7 +199,7 @@ func (p *plugin) migrateCollectionsHandler(args []string, interactive bool) (str
|
||||
|
||||
collections := []*core.Collection{}
|
||||
if err := p.app.CollectionQuery().OrderBy("created ASC").All(&collections); err != nil {
|
||||
return "", fmt.Errorf("Failed to fetch migrations list: %v\n", err)
|
||||
return "", fmt.Errorf("failed to fetch migrations list: %v", err)
|
||||
}
|
||||
|
||||
var template string
|
||||
@@ -210,7 +210,7 @@ func (p *plugin) migrateCollectionsHandler(args []string, interactive bool) (str
|
||||
template, templateErr = p.goSnapshotTemplate(collections)
|
||||
}
|
||||
if templateErr != nil {
|
||||
return "", fmt.Errorf("Failed to resolve template: %v\n", templateErr)
|
||||
return "", fmt.Errorf("failed to resolve template: %v", templateErr)
|
||||
}
|
||||
|
||||
return p.migrateCreateHandler(template, createArgs, interactive)
|
||||
|
||||
Binary file not shown.
+2
-2
@@ -92,7 +92,7 @@ func (p *OIDC) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
Username string `json:"preferred_username"`
|
||||
Picture string `json:"picture"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
EmailVerified any `json:"email_verified"` // see #6657
|
||||
}{}
|
||||
if err := json.Unmarshal(data, &extracted); err != nil {
|
||||
return nil, err
|
||||
@@ -110,7 +110,7 @@ func (p *OIDC) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
|
||||
user.Expiry, _ = types.ParseDateTime(token.Expiry)
|
||||
|
||||
if extracted.EmailVerified {
|
||||
if cast.ToBool(extracted.EmailVerified) {
|
||||
user.Email = extracted.Email
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/endpoints"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -29,8 +30,8 @@ func NewPatreonProvider() *Patreon {
|
||||
displayName: "Patreon",
|
||||
pkce: true,
|
||||
scopes: []string{"identity", "identity[email]"},
|
||||
authURL: "https://www.patreon.com/oauth2/authorize",
|
||||
tokenURL: "https://www.patreon.com/api/oauth2/token",
|
||||
authURL: endpoints.Patreon.AuthURL,
|
||||
tokenURL: endpoints.Patreon.TokenURL,
|
||||
userInfoURL: "https://www.patreon.com/api/oauth2/v2/identity?fields%5Buser%5D=full_name,email,vanity,image_url,is_email_verified",
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func (p *Twitch) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
}
|
||||
|
||||
if len(extracted.Data) == 0 {
|
||||
return nil, errors.New("Failed to fetch AuthUser data")
|
||||
return nil, errors.New("failed to fetch AuthUser data")
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
|
||||
@@ -21,6 +21,9 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem/internal/s3blob"
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem/internal/s3blob/s3"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
|
||||
// explicit webp decoder because disintegration/imaging does not support webp
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
// note: the same as blob.ErrNotFound for backward compatibility with earlier versions
|
||||
@@ -358,8 +361,10 @@ var inlineServeContentTypes = []string{
|
||||
|
||||
// manualExtensionContentTypes is a map of file extensions to content types.
|
||||
var manualExtensionContentTypes = map[string]string{
|
||||
".svg": "image/svg+xml", // (see https://github.com/whatwg/mimesniff/issues/7)
|
||||
".css": "text/css", // (see https://github.com/gabriel-vasile/mimetype/pull/113)
|
||||
".svg": "image/svg+xml", // (see https://github.com/whatwg/mimesniff/issues/7)
|
||||
".css": "text/css", // (see https://github.com/gabriel-vasile/mimetype/pull/113)
|
||||
".js": "text/javascript", // (see https://github.com/pocketbase/pocketbase/issues/6597)
|
||||
".mjs": "text/javascript",
|
||||
}
|
||||
|
||||
// forceAttachmentParam is the name of the request query parameter to
|
||||
@@ -394,7 +399,7 @@ func (s *System) Serve(res http.ResponseWriter, req *http.Request, fileKey strin
|
||||
// make an exception for specific content types and force a custom
|
||||
// content type to send in the response so that it can be loaded properly
|
||||
extContentType := realContentType
|
||||
if ct, found := manualExtensionContentTypes[filepath.Ext(name)]; found && extContentType != ct {
|
||||
if ct, found := manualExtensionContentTypes[filepath.Ext(name)]; found {
|
||||
extContentType = ct
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
)
|
||||
|
||||
@@ -430,6 +432,36 @@ func TestFileSystemServe(t *testing.T) {
|
||||
"Cache-Control": cacheControl,
|
||||
},
|
||||
},
|
||||
{
|
||||
// js exception
|
||||
"main.js",
|
||||
"test_name.js",
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
map[string]string{
|
||||
"Content-Disposition": "attachment; filename=test_name.js",
|
||||
"Content-Type": "text/javascript",
|
||||
"Content-Length": "0",
|
||||
"Content-Security-Policy": csp,
|
||||
"Cache-Control": cacheControl,
|
||||
},
|
||||
},
|
||||
{
|
||||
// mjs exception
|
||||
"main.mjs",
|
||||
"test_name.mjs",
|
||||
nil,
|
||||
nil,
|
||||
false,
|
||||
map[string]string{
|
||||
"Content-Disposition": "attachment; filename=test_name.mjs",
|
||||
"Content-Type": "text/javascript",
|
||||
"Content-Length": "0",
|
||||
"Content-Security-Policy": csp,
|
||||
"Cache-Control": cacheControl,
|
||||
},
|
||||
},
|
||||
{
|
||||
// custom header
|
||||
"test/sub2.txt",
|
||||
@@ -597,9 +629,13 @@ func TestFileSystemList(t *testing.T) {
|
||||
"",
|
||||
[]string{
|
||||
"image.png",
|
||||
"image.jpg",
|
||||
"image.svg",
|
||||
"image.webp",
|
||||
"image_! noext",
|
||||
"style.css",
|
||||
"main.js",
|
||||
"main.mjs",
|
||||
"test/sub1.txt",
|
||||
"test/sub2.txt",
|
||||
},
|
||||
@@ -618,25 +654,30 @@ func TestFileSystemList(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
objs, err := fsys.List(s.prefix)
|
||||
if err != nil {
|
||||
t.Fatalf("[%s] %v", s.prefix, err)
|
||||
}
|
||||
|
||||
if len(s.expected) != len(objs) {
|
||||
t.Fatalf("[%s] Expected %d files, got \n%v", s.prefix, len(s.expected), objs)
|
||||
}
|
||||
|
||||
ObjsLoop:
|
||||
for _, obj := range objs {
|
||||
for _, name := range s.expected {
|
||||
if name == obj.Key {
|
||||
continue ObjsLoop
|
||||
}
|
||||
t.Run("prefix_"+s.prefix, func(t *testing.T) {
|
||||
objs, err := fsys.List(s.prefix)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Fatalf("[%s] Unexpected file %q", s.prefix, obj.Key)
|
||||
}
|
||||
if len(s.expected) != len(objs) {
|
||||
t.Fatalf("Expected %d files, got \n%v", len(s.expected), objs)
|
||||
}
|
||||
|
||||
for _, obj := range objs {
|
||||
var exists bool
|
||||
for _, name := range s.expected {
|
||||
if name == obj.Key {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
t.Fatalf("Unexpected file %q", obj.Key)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,39 +755,67 @@ func TestFileSystemCreateThumb(t *testing.T) {
|
||||
defer fsys.Close()
|
||||
|
||||
scenarios := []struct {
|
||||
file string
|
||||
thumb string
|
||||
cropCenter bool
|
||||
expectError bool
|
||||
file string
|
||||
thumb string
|
||||
size string
|
||||
expectedMimeType string
|
||||
}{
|
||||
// missing
|
||||
{"missing.txt", "thumb_test_missing", true, true},
|
||||
{"missing.txt", "thumb_test_missing", "100x100", ""},
|
||||
// non-image existing file
|
||||
{"test/sub1.txt", "thumb_test_sub1", true, true},
|
||||
// existing image file - crop center
|
||||
{"image.png", "thumb_file_center", true, false},
|
||||
// existing image file - crop top
|
||||
{"image.png", "thumb_file_top", false, false},
|
||||
{"test/sub1.txt", "thumb_test_sub1", "100x100", ""},
|
||||
// existing image file with existing thumb path = should fail
|
||||
{"image.png", "test", true, true},
|
||||
{"image.png", "test", "100x100", ""},
|
||||
// existing image file with invalid thumb size
|
||||
{"image.png", "thumb0", "invalid", ""},
|
||||
// existing image file with 0xH thumb size
|
||||
{"image.png", "thumb_0xH", "0x100", "image/png"},
|
||||
// existing image file with Wx0 thumb size
|
||||
{"image.png", "thumb_Wx0", "100x0", "image/png"},
|
||||
// existing image file with WxH thumb size
|
||||
{"image.png", "thumb_WxH", "100x100", "image/png"},
|
||||
// existing image file with WxHt thumb size
|
||||
{"image.png", "thumb_WxHt", "100x100t", "image/png"},
|
||||
// existing image file with WxHb thumb size
|
||||
{"image.png", "thumb_WxHb", "100x100b", "image/png"},
|
||||
// existing image file with WxHf thumb size
|
||||
{"image.png", "thumb_WxHf", "100x100f", "image/png"},
|
||||
// jpg
|
||||
{"image.jpg", "thumb.jpg", "100x100", "image/jpeg"},
|
||||
// webp (should produce png)
|
||||
{"image.webp", "thumb.webp", "100x100", "image/png"},
|
||||
}
|
||||
|
||||
for i, scenario := range scenarios {
|
||||
err := fsys.CreateThumb(scenario.file, scenario.thumb, "100x100")
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.file+"_"+s.thumb+"_"+s.size, func(t *testing.T) {
|
||||
err := fsys.CreateThumb(s.file, s.thumb, s.size)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != scenario.expectError {
|
||||
t.Errorf("(%d) Expected hasErr to be %v, got %v (%v)", i, scenario.expectError, hasErr, err)
|
||||
continue
|
||||
}
|
||||
expectErr := s.expectedMimeType == ""
|
||||
|
||||
if scenario.expectError {
|
||||
continue
|
||||
}
|
||||
hasErr := err != nil
|
||||
if hasErr != expectErr {
|
||||
t.Fatalf("Expected hasErr to be %v, got %v (%v)", expectErr, hasErr, err)
|
||||
}
|
||||
|
||||
if exists, _ := fsys.Exists(scenario.thumb); !exists {
|
||||
t.Errorf("(%d) Couldn't find %q thumb", i, scenario.thumb)
|
||||
}
|
||||
if hasErr {
|
||||
return
|
||||
}
|
||||
|
||||
f, err := fsys.GetFile(s.thumb)
|
||||
if err != nil {
|
||||
t.Fatalf("Missing expected thumb %s (%v)", s.thumb, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
mt, err := mimetype.DetectReader(f)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to detect thumb %s mimetype (%v)", s.thumb, err)
|
||||
}
|
||||
|
||||
if mtStr := mt.String(); mtStr != s.expectedMimeType {
|
||||
t.Fatalf("Expected thumb %s MimeType %q, got %q", s.thumb, s.expectedMimeType, mtStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,36 +847,94 @@ func createTestDir(t *testing.T) string {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
file3, err := os.OpenFile(filepath.Join(dir, "image.png"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
imgRect := image.Rect(0, 0, 1, 1) // tiny 1x1 png
|
||||
png.Encode(file3, imgRect)
|
||||
file3.Close()
|
||||
err2 := os.WriteFile(filepath.Join(dir, "image.png.attrs"), []byte(`{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"image/png","user.metadata":null}`), 0644)
|
||||
if err2 != nil {
|
||||
t.Fatal(err2)
|
||||
// png
|
||||
{
|
||||
|
||||
file, err := os.OpenFile(filepath.Join(dir, "image.png"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
imgRect := image.Rect(0, 0, 1, 1) // tiny 1x1 png
|
||||
_ = png.Encode(file, imgRect)
|
||||
file.Close()
|
||||
err = os.WriteFile(filepath.Join(dir, "image.png.attrs"), []byte(`{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"image/png","user.metadata":null}`), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
file4, err := os.OpenFile(filepath.Join(dir, "image.svg"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// jpg
|
||||
{
|
||||
file, err := os.OpenFile(filepath.Join(dir, "image.jpg"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
imgRect := image.Rect(0, 0, 1, 1) // tiny 1x1 jpg
|
||||
_ = jpeg.Encode(file, imgRect, nil)
|
||||
file.Close()
|
||||
err = os.WriteFile(filepath.Join(dir, "image.jpg.attrs"), []byte(`{"user.cache_control":"","user.content_disposition":"","user.content_encoding":"","user.content_language":"","user.content_type":"image/jpeg","user.metadata":null}`), 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
file4.Close()
|
||||
|
||||
file5, err := os.OpenFile(filepath.Join(dir, "style.css"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// svg
|
||||
{
|
||||
file, err := os.OpenFile(filepath.Join(dir, "image.svg"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
file5.Close()
|
||||
|
||||
file6, err := os.OpenFile(filepath.Join(dir, "image_! noext"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
// webp
|
||||
{
|
||||
err := os.WriteFile(filepath.Join(dir, "image.webp"), []byte{
|
||||
82, 73, 70, 70, 36, 0, 0, 0, 87, 69, 66, 80, 86, 80, 56, 32,
|
||||
24, 0, 0, 0, 48, 1, 0, 157, 1, 42, 1, 0, 1, 0, 2, 0, 52, 37,
|
||||
164, 0, 3, 112, 0, 254, 251, 253, 80, 0,
|
||||
}, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// no extension and invalid characters
|
||||
{
|
||||
file, err := os.OpenFile(filepath.Join(dir, "image_! noext"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = png.Encode(file, image.Rect(0, 0, 1, 1)) // tiny 1x1 png
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// css
|
||||
{
|
||||
file, err := os.OpenFile(filepath.Join(dir, "style.css"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// js
|
||||
{
|
||||
file, err := os.OpenFile(filepath.Join(dir, "main.js"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
|
||||
// mjs
|
||||
{
|
||||
file, err := os.OpenFile(filepath.Join(dir, "main.mjs"), os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
png.Encode(file6, image.Rect(0, 0, 1, 1)) // tiny 1x1 png
|
||||
file6.Close()
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
@@ -653,7 +653,7 @@ func (w *writer) Close() error {
|
||||
|
||||
// Always delete the temp file. On success, it will have been renamed so
|
||||
// the Remove will fail.
|
||||
tempname := w.File.Name()
|
||||
tempname := w.Name()
|
||||
defer os.Remove(tempname)
|
||||
|
||||
// Check if the write was cancelled.
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ error = (*ResponseError)(nil)
|
||||
|
||||
// ResponseError defines a general S3 response error.
|
||||
//
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
||||
@@ -20,7 +22,7 @@ type ResponseError struct {
|
||||
}
|
||||
|
||||
// Error implements the std error interface.
|
||||
func (err ResponseError) Error() string {
|
||||
func (err *ResponseError) Error() string {
|
||||
var strBuilder strings.Builder
|
||||
|
||||
strBuilder.WriteString(strconv.Itoa(err.Status))
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestResponseErrorSerialization(t *testing.T) {
|
||||
</Error>
|
||||
`
|
||||
|
||||
respErr := s3.ResponseError{
|
||||
respErr := &s3.ResponseError{
|
||||
Status: 123,
|
||||
Raw: []byte("test"),
|
||||
}
|
||||
@@ -45,17 +45,17 @@ func TestResponseErrorSerialization(t *testing.T) {
|
||||
func TestResponseErrorErrorInterface(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
err s3.ResponseError
|
||||
err *s3.ResponseError
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"empty",
|
||||
s3.ResponseError{},
|
||||
&s3.ResponseError{},
|
||||
"0 S3ResponseError",
|
||||
},
|
||||
{
|
||||
"with code and message (nil raw)",
|
||||
s3.ResponseError{
|
||||
&s3.ResponseError{
|
||||
Status: 123,
|
||||
Code: "test_code",
|
||||
Message: "test_message",
|
||||
@@ -64,7 +64,7 @@ func TestResponseErrorErrorInterface(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"with code and message (non-nil raw)",
|
||||
s3.ResponseError{
|
||||
&s3.ResponseError{
|
||||
Status: 123,
|
||||
Code: "test_code",
|
||||
Message: "test_message",
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -61,7 +62,7 @@ type S3 struct {
|
||||
}
|
||||
|
||||
// URL constructs an S3 request URL based on the current configuration.
|
||||
func (s3 *S3) URL(key string) string {
|
||||
func (s3 *S3) URL(path string) string {
|
||||
scheme := "https"
|
||||
endpoint := strings.TrimRight(s3.Endpoint, "/")
|
||||
if after, ok := strings.CutPrefix(endpoint, "https://"); ok {
|
||||
@@ -71,13 +72,13 @@ func (s3 *S3) URL(key string) string {
|
||||
scheme = "http"
|
||||
}
|
||||
|
||||
key = strings.TrimLeft(key, "/")
|
||||
path = strings.TrimLeft(path, "/")
|
||||
|
||||
if s3.UsePathStyle {
|
||||
return fmt.Sprintf("%s://%s/%s/%s", scheme, endpoint, s3.Bucket, key)
|
||||
return fmt.Sprintf("%s://%s/%s/%s", scheme, endpoint, s3.Bucket, path)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s://%s.%s/%s", scheme, s3.Bucket, endpoint, key)
|
||||
return fmt.Sprintf("%s://%s.%s/%s", scheme, s3.Bucket, endpoint, path)
|
||||
}
|
||||
|
||||
// SignAndSend signs the provided request per AWS Signature v4 and sends it.
|
||||
@@ -150,8 +151,8 @@ func (s3 *S3) sign(req *http.Request) {
|
||||
|
||||
canonicalParts := []string{
|
||||
req.Method,
|
||||
req.URL.EscapedPath(),
|
||||
encodeQuery(req),
|
||||
escapePath(req.URL.Path),
|
||||
escapeQuery(req.URL.Query()),
|
||||
canonicalHeaders,
|
||||
signedHeaders,
|
||||
req.Header.Get("x-amz-content-sha256"),
|
||||
@@ -202,12 +203,6 @@ func (s3 *S3) sign(req *http.Request) {
|
||||
req.Header.Set("authorization", authorization)
|
||||
}
|
||||
|
||||
// encodeQuery encodes the request query parameters according to the AWS requirements
|
||||
// (see UriEncode description in https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html).
|
||||
func encodeQuery(req *http.Request) string {
|
||||
return strings.ReplaceAll(req.URL.Query().Encode(), "+", "%20")
|
||||
}
|
||||
|
||||
func sha256Hex(content []byte) string {
|
||||
h := sha256.New()
|
||||
h.Write(content)
|
||||
@@ -280,3 +275,96 @@ func extractMetadata(headers http.Header) map[string]string {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// escapeQuery returns the URI encoded request query parameters according to the AWS S3 spec requirements
|
||||
// (it is similar to url.Values.Encode but instead of url.QueryEscape uses our own escape method).
|
||||
func escapeQuery(values url.Values) string {
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
|
||||
keys := make([]string, 0, len(values))
|
||||
for k := range values {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
slices.Sort(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
vs := values[k]
|
||||
keyEscaped := escape(k)
|
||||
for _, values := range vs {
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
buf.WriteString(keyEscaped)
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(escape(values))
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// escapePath returns the URI encoded request path according to the AWS S3 spec requirements.
|
||||
func escapePath(path string) string {
|
||||
parts := strings.Split(path, "/")
|
||||
|
||||
for i, part := range parts {
|
||||
parts[i] = escape(part)
|
||||
}
|
||||
|
||||
return strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
const upperhex = "0123456789ABCDEF"
|
||||
|
||||
// escape is similar to the std url.escape but implements the AWS [UriEncode requirements]:
|
||||
// - URI encode every byte except the unreserved characters: 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', and '~'.
|
||||
// - The space character is a reserved character and must be encoded as "%20" (and not as "+").
|
||||
// - Each URI encoded byte is formed by a '%' and the two-digit hexadecimal value of the byte.
|
||||
// - Letters in the hexadecimal value must be uppercase, for example "%1A".
|
||||
//
|
||||
// [UriEncode requirements]: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html
|
||||
func escape(s string) string {
|
||||
hexCount := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if shouldEscape(c) {
|
||||
hexCount++
|
||||
}
|
||||
}
|
||||
|
||||
if hexCount == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
result := make([]byte, len(s)+2*hexCount)
|
||||
|
||||
j := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if shouldEscape(c) {
|
||||
result[j] = '%'
|
||||
result[j+1] = upperhex[c>>4]
|
||||
result[j+2] = upperhex[c&15]
|
||||
j += 3
|
||||
} else {
|
||||
result[j] = c
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
|
||||
// > "URI encode every byte except the unreserved characters: 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', and '~'."
|
||||
func shouldEscape(c byte) bool {
|
||||
isUnreserved := (c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
c == '-' || c == '.' || c == '_' || c == '~'
|
||||
|
||||
return !isUnreserved
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEscapePath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
escaped := escapePath("/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~ !@#$%^&*()+={}[]?><\\|,`'\"/@sub1/@sub2/a/b/c/1/2/3")
|
||||
|
||||
expected := "/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~%20%21%40%23%24%25%5E%26%2A%28%29%2B%3D%7B%7D%5B%5D%3F%3E%3C%5C%7C%2C%60%27%22/%40sub1/%40sub2/a/b/c/1/2/3"
|
||||
|
||||
if escaped != expected {
|
||||
t.Fatalf("Expected\n%s\ngot\n%s", expected, escaped)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEscapeQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
escaped := escapeQuery(url.Values{
|
||||
"abc": []string{"123"},
|
||||
"/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~ !@#$%^&*()+={}[]?><\\|,`'\"": []string{
|
||||
"/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~ !@#$%^&*()+={}[]?><\\|,`'\"",
|
||||
},
|
||||
})
|
||||
|
||||
expected := "%2FABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~%20%21%40%23%24%25%5E%26%2A%28%29%2B%3D%7B%7D%5B%5D%3F%3E%3C%5C%7C%2C%60%27%22=%2FABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~%20%21%40%23%24%25%5E%26%2A%28%29%2B%3D%7B%7D%5B%5D%3F%3E%3C%5C%7C%2C%60%27%22&abc=123"
|
||||
|
||||
if escaped != expected {
|
||||
t.Fatalf("Expected\n%s\ngot\n%s", expected, escaped)
|
||||
}
|
||||
}
|
||||
@@ -98,11 +98,13 @@ func TestS3SignAndSend(t *testing.T) {
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
path string
|
||||
reqFunc func(req *http.Request)
|
||||
s3Client *s3.S3
|
||||
}{
|
||||
{
|
||||
"minimal",
|
||||
"/test",
|
||||
func(req *http.Request) {
|
||||
req.Header.Set("x-amz-date", "20250102T150405Z")
|
||||
},
|
||||
@@ -129,6 +131,7 @@ func TestS3SignAndSend(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"minimal with different access and secret keys",
|
||||
"/test",
|
||||
func(req *http.Request) {
|
||||
req.Header.Set("x-amz-date", "20250102T150405Z")
|
||||
},
|
||||
@@ -153,8 +156,36 @@ func TestS3SignAndSend(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
"minimal with special characters",
|
||||
"/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~!@#$^&*()=/@sub?a=1&@b=@2",
|
||||
func(req *http.Request) {
|
||||
req.Header.Set("x-amz-date", "20250102T150405Z")
|
||||
},
|
||||
&s3.S3{
|
||||
Region: "test_region",
|
||||
Bucket: "test_bucket",
|
||||
Endpoint: "https://example.com/",
|
||||
AccessKey: "456",
|
||||
SecretKey: "def",
|
||||
Client: tests.NewClient(&tests.RequestStub{
|
||||
Method: http.MethodGet,
|
||||
URL: "https://test_bucket.example.com/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~!@#$%5E&*()=/@sub?a=1&@b=@2",
|
||||
Response: testResponse(),
|
||||
Match: func(req *http.Request) bool {
|
||||
return tests.ExpectHeaders(req.Header, map[string]string{
|
||||
"Authorization": "AWS4-HMAC-SHA256 Credential=456/20250102/test_region/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=e0001982deef1652704f74503203e77d83d4c88369421f9fca644d96f2a62a3c",
|
||||
"Host": "test_bucket.example.com",
|
||||
"X-Amz-Content-Sha256": "UNSIGNED-PAYLOAD",
|
||||
"X-Amz-Date": "20250102T150405Z",
|
||||
})
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
"with extra headers",
|
||||
"/test",
|
||||
func(req *http.Request) {
|
||||
req.Header.Set("x-amz-date", "20250102T150405Z")
|
||||
req.Header.Set("x-amz-content-sha256", "test_sha256")
|
||||
@@ -191,7 +222,7 @@ func TestS3SignAndSend(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, s.s3Client.URL("/test"), strings.NewReader("test_request"))
|
||||
req, err := http.NewRequest(http.MethodGet, s.s3Client.URL(s.path), strings.NewReader("test_request"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -79,9 +79,13 @@ func (drv *driver) NormalizeError(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// normalize base on its S3 error code
|
||||
var ae s3.ResponseError
|
||||
// normalize base on its S3 error status or code
|
||||
var ae *s3.ResponseError
|
||||
if errors.As(err, &ae) {
|
||||
if ae.Status == 404 {
|
||||
return errors.Join(err, blob.ErrNotFound)
|
||||
}
|
||||
|
||||
switch ae.Code {
|
||||
case "NoSuchBucket", "NoSuchKey", "NotFound":
|
||||
return errors.Join(err, blob.ErrNotFound)
|
||||
|
||||
@@ -99,29 +99,39 @@ func TestDriverNormilizeError(t *testing.T) {
|
||||
errors.New("test"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
"response error with only status (non-404)",
|
||||
&s3.ResponseError{Status: 123},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"response error with only status (404)",
|
||||
&s3.ResponseError{Status: 404},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"response error with custom code",
|
||||
s3.ResponseError{Code: "test"},
|
||||
&s3.ResponseError{Code: "test"},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"response error with NoSuchBucket code",
|
||||
s3.ResponseError{Code: "NoSuchBucket"},
|
||||
&s3.ResponseError{Code: "NoSuchBucket"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"response error with NoSuchKey code",
|
||||
s3.ResponseError{Code: "NoSuchKey"},
|
||||
&s3.ResponseError{Code: "NoSuchKey"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"response error with NotFound code",
|
||||
s3.ResponseError{Code: "NotFound"},
|
||||
&s3.ResponseError{Code: "NotFound"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"wrapped response error with NotFound code", // ensures that the entire error's tree is checked
|
||||
fmt.Errorf("test: %w", s3.ResponseError{Code: "NotFound"}),
|
||||
fmt.Errorf("test: %w", &s3.ResponseError{Code: "NotFound"}),
|
||||
true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"sync"
|
||||
@@ -252,26 +253,73 @@ func (h *BatchHandler) resolveAttr(data map[string]any, attr slog.Attr) error {
|
||||
data[attr.Key] = groupData
|
||||
}
|
||||
default:
|
||||
switch v := attr.Value.Any().(type) {
|
||||
case validation.Errors:
|
||||
data[attr.Key] = map[string]any{
|
||||
"data": v,
|
||||
"raw": v.Error(),
|
||||
}
|
||||
case error:
|
||||
var ve validation.Errors
|
||||
if errors.As(v, &ve) {
|
||||
data[attr.Key] = map[string]any{
|
||||
"data": ve,
|
||||
"raw": v.Error(),
|
||||
}
|
||||
} else {
|
||||
data[attr.Key] = v.Error()
|
||||
}
|
||||
default:
|
||||
data[attr.Key] = v
|
||||
}
|
||||
data[attr.Key] = normalizeLogAttrValue(attr.Value.Any())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeLogAttrValue(rawAttrValue any) any {
|
||||
switch attrV := rawAttrValue.(type) {
|
||||
case validation.Errors:
|
||||
out := make(map[string]any, len(attrV))
|
||||
for k, v := range attrV {
|
||||
out[k] = serializeLogError(v)
|
||||
}
|
||||
return out
|
||||
case map[string]validation.Error:
|
||||
out := make(map[string]any, len(attrV))
|
||||
for k, v := range attrV {
|
||||
out[k] = serializeLogError(v)
|
||||
}
|
||||
return out
|
||||
case map[string]error:
|
||||
out := make(map[string]any, len(attrV))
|
||||
for k, v := range attrV {
|
||||
out[k] = serializeLogError(v)
|
||||
}
|
||||
return out
|
||||
case map[string]any:
|
||||
out := make(map[string]any, len(attrV))
|
||||
for k, v := range attrV {
|
||||
switch vv := v.(type) {
|
||||
case error:
|
||||
out[k] = serializeLogError(vv)
|
||||
default:
|
||||
out[k] = normalizeLogAttrValue(vv)
|
||||
}
|
||||
}
|
||||
return out
|
||||
case error:
|
||||
// check for wrapped validation.Errors
|
||||
var ve validation.Errors
|
||||
if errors.As(attrV, &ve) {
|
||||
out := make(map[string]any, len(ve))
|
||||
for k, v := range ve {
|
||||
out[k] = serializeLogError(v)
|
||||
}
|
||||
return map[string]any{
|
||||
"data": out,
|
||||
"raw": serializeLogError(attrV),
|
||||
}
|
||||
}
|
||||
return serializeLogError(attrV)
|
||||
default:
|
||||
return attrV
|
||||
}
|
||||
}
|
||||
|
||||
func serializeLogError(err error) any {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// prioritize a json structured format (e.g. validation.Errors)
|
||||
jsonErr, ok := err.(json.Marshaler)
|
||||
if ok {
|
||||
return jsonErr
|
||||
}
|
||||
|
||||
// fallback to its original string representation
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"log/slog"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
)
|
||||
|
||||
func TestNewBatchHandlerPanic(t *testing.T) {
|
||||
@@ -280,14 +282,45 @@ func TestBatchHandlerAttrsFormat(t *testing.T) {
|
||||
h1.Handle(ctx, record)
|
||||
h2.Handle(ctx, record)
|
||||
|
||||
// errors serialization checks
|
||||
errorsRecord := slog.NewRecord(time.Now(), slog.LevelError, "details", 0)
|
||||
errorsRecord.Add("validation.Errors", validation.Errors{
|
||||
"a": validation.NewError("validation_code", "validation_message"),
|
||||
"b": errors.New("plain"),
|
||||
})
|
||||
errorsRecord.Add("wrapped_validation.Errors", fmt.Errorf("wrapped: %w", validation.Errors{
|
||||
"a": validation.NewError("validation_code", "validation_message"),
|
||||
"b": errors.New("plain"),
|
||||
}))
|
||||
errorsRecord.Add("map[string]any", map[string]any{
|
||||
"a": validation.NewError("validation_code", "validation_message"),
|
||||
"b": errors.New("plain"),
|
||||
"c": "test_any",
|
||||
"d": map[string]any{
|
||||
"nestedA": validation.NewError("nested_code", "nested_message"),
|
||||
"nestedB": errors.New("nested_plain"),
|
||||
},
|
||||
})
|
||||
errorsRecord.Add("map[string]error", map[string]error{
|
||||
"a": validation.NewError("validation_code", "validation_message"),
|
||||
"b": errors.New("plain"),
|
||||
})
|
||||
errorsRecord.Add("map[string]validation.Error", map[string]validation.Error{
|
||||
"a": validation.NewError("validation_code", "validation_message"),
|
||||
"b": nil,
|
||||
})
|
||||
errorsRecord.Add("plain_error", errors.New("plain"))
|
||||
h0.Handle(ctx, errorsRecord)
|
||||
|
||||
expected := []string{
|
||||
`{"name":"test"}`,
|
||||
`{"a":1,"b":"123","name":"test"}`,
|
||||
`{"a":1,"b":"123","sub":{"c":3,"d":{"d.1":1},"e":"example error","name":"test"}}`,
|
||||
`{"map[string]any":{"a":"validation_message","b":"plain","c":"test_any","d":{"nestedA":"nested_message","nestedB":"nested_plain"}},"map[string]error":{"a":"validation_message","b":"plain"},"map[string]validation.Error":{"a":"validation_message","b":null},"plain_error":"plain","validation.Errors":{"a":"validation_message","b":"plain"},"wrapped_validation.Errors":{"data":{"a":"validation_message","b":"plain"},"raw":"wrapped: a: validation_message; b: plain."}}`,
|
||||
}
|
||||
|
||||
if len(beforeLogs) != len(expected) {
|
||||
t.Fatalf("Expected %d logs, got %d", len(beforeLogs), len(expected))
|
||||
t.Fatalf("Expected %d logs, got %d", len(expected), len(beforeLogs))
|
||||
}
|
||||
|
||||
for i, data := range expected {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package mailer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/mail"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
)
|
||||
|
||||
@@ -54,3 +56,17 @@ func addressesToStrings(addresses []mail.Address, withName bool) []string {
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// detectReaderMimeType reads the first couple bytes of the reader to detect its MIME type.
|
||||
//
|
||||
// Returns a new combined reader from the partial read + the remaining of the original reader.
|
||||
func detectReaderMimeType(r io.Reader) (io.Reader, string, error) {
|
||||
readCopy := new(bytes.Buffer)
|
||||
|
||||
mime, err := mimetype.DetectReader(io.TeeReader(r, readCopy))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return io.MultiReader(readCopy, r), mime.String(), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package mailer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/mail"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddressesToStrings(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []struct {
|
||||
withName bool
|
||||
addresses []mail.Address
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
true,
|
||||
[]mail.Address{{Name: "John Doe", Address: "test1@example.com"}, {Name: "Jane Doe", Address: "test2@example.com"}},
|
||||
[]string{`"John Doe" <test1@example.com>`, `"Jane Doe" <test2@example.com>`},
|
||||
},
|
||||
{
|
||||
true,
|
||||
[]mail.Address{{Name: "John Doe", Address: "test1@example.com"}, {Address: "test2@example.com"}},
|
||||
[]string{`"John Doe" <test1@example.com>`, `test2@example.com`},
|
||||
},
|
||||
{
|
||||
false,
|
||||
[]mail.Address{{Name: "John Doe", Address: "test1@example.com"}, {Name: "Jane Doe", Address: "test2@example.com"}},
|
||||
[]string{`test1@example.com`, `test2@example.com`},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("%v_%v", s.withName, s.addresses), func(t *testing.T) {
|
||||
result := addressesToStrings(s.addresses, s.withName)
|
||||
|
||||
if len(s.expected) != len(result) {
|
||||
t.Fatalf("Expected\n%v\ngot\n%v", s.expected, result)
|
||||
}
|
||||
|
||||
for k, v := range s.expected {
|
||||
if v != result[k] {
|
||||
t.Fatalf("Expected %d address %q, got %q", k, v, result[k])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectReaderMimeType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
str := "#!/bin/node\n" + strings.Repeat("a", 10000) // ensure that it is large enough to remain after the signature sniffing
|
||||
|
||||
r, mime, err := detectReaderMimeType(strings.NewReader(str))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedMime := "text/javascript"
|
||||
if mime != expectedMime {
|
||||
t.Fatalf("Expected mime %q, got %q", expectedMime, mime)
|
||||
}
|
||||
|
||||
raw, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rawStr := string(raw)
|
||||
|
||||
if rawStr != str {
|
||||
t.Fatalf("Expected content\n%s\ngot\n%s", str, rawStr)
|
||||
}
|
||||
}
|
||||
@@ -95,5 +95,5 @@ func findSendmailPath() (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("Failed to locate a sendmail executable path.")
|
||||
return "", errors.New("failed to locate a sendmail executable path")
|
||||
}
|
||||
|
||||
+10
-2
@@ -116,12 +116,20 @@ func (c *SMTPClient) send(m *Message) error {
|
||||
|
||||
// add regular attachements (if any)
|
||||
for name, data := range m.Attachments {
|
||||
yak.Attach(name, data)
|
||||
r, mime, err := detectReaderMimeType(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
yak.AttachWithMimeType(name, r, mime)
|
||||
}
|
||||
|
||||
// add inline attachments (if any)
|
||||
for name, data := range m.InlineAttachments {
|
||||
yak.AttachInline(name, data)
|
||||
r, mime, err := detectReaderMimeType(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
yak.AttachInlineWithMimeType(name, r, mime)
|
||||
}
|
||||
|
||||
// add custom headers (if any)
|
||||
|
||||
@@ -135,10 +135,14 @@ func (m *excerptModifier) Modify(value any) (any, error) {
|
||||
result := strings.TrimSpace(builder.String())
|
||||
|
||||
if len(result) > m.max {
|
||||
result = strings.TrimSpace(result[:m.max])
|
||||
|
||||
if m.withEllipsis {
|
||||
result += "..."
|
||||
// note: casted to []rune to properly account for multi-byte chars
|
||||
runes := []rune(result)
|
||||
if len(runes) > m.max {
|
||||
result = string(runes[:m.max])
|
||||
result = strings.TrimSpace(result)
|
||||
if m.withEllipsis {
|
||||
result += "..."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,6 +134,20 @@ func TestExcerptModifierModify(t *testing.T) {
|
||||
html,
|
||||
plainText,
|
||||
},
|
||||
|
||||
// multibyte chars
|
||||
{
|
||||
"mutibyte chars <= max",
|
||||
[]string{"4", "t"},
|
||||
"аб\nв ",
|
||||
"аб в",
|
||||
},
|
||||
{
|
||||
"mutibyte chars > max",
|
||||
[]string{"3", "t"},
|
||||
"аб\nв ",
|
||||
"аб...",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
+30
-18
@@ -249,13 +249,22 @@ func buildResolversExpr(
|
||||
return expr, nil
|
||||
}
|
||||
|
||||
var normalizedIdentifiers = map[string]string{
|
||||
// if `null` field is missing, treat `null` identifier as NULL token
|
||||
"null": "NULL",
|
||||
// if `true` field is missing, treat `true` identifier as TRUE token
|
||||
"true": "1",
|
||||
// if `false` field is missing, treat `false` identifier as FALSE token
|
||||
"false": "0",
|
||||
}
|
||||
|
||||
func resolveToken(token fexpr.Token, fieldResolver FieldResolver) (*ResolverResult, error) {
|
||||
switch token.Type {
|
||||
case fexpr.TokenIdentifier:
|
||||
// check for macros
|
||||
// ---
|
||||
if macroFunc, ok := identifierMacros[token.Literal]; ok {
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
placeholder := "t" + security.PseudorandomString(8)
|
||||
|
||||
macroValue, err := macroFunc()
|
||||
if err != nil {
|
||||
@@ -271,40 +280,43 @@ func resolveToken(token fexpr.Token, fieldResolver FieldResolver) (*ResolverResu
|
||||
// custom resolver
|
||||
// ---
|
||||
result, err := fieldResolver.Resolve(token.Literal)
|
||||
|
||||
if err != nil || result.Identifier == "" {
|
||||
m := map[string]string{
|
||||
// if `null` field is missing, treat `null` identifier as NULL token
|
||||
"null": "NULL",
|
||||
// if `true` field is missing, treat `true` identifier as TRUE token
|
||||
"true": "1",
|
||||
// if `false` field is missing, treat `false` identifier as FALSE token
|
||||
"false": "0",
|
||||
}
|
||||
if v, ok := m[strings.ToLower(token.Literal)]; ok {
|
||||
return &ResolverResult{Identifier: v}, nil
|
||||
for k, v := range normalizedIdentifiers {
|
||||
if strings.EqualFold(k, token.Literal) {
|
||||
return &ResolverResult{Identifier: v}, nil
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, err
|
||||
case fexpr.TokenText:
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
placeholder := "t" + security.PseudorandomString(8)
|
||||
|
||||
return &ResolverResult{
|
||||
Identifier: "{:" + placeholder + "}",
|
||||
Params: dbx.Params{placeholder: token.Literal},
|
||||
}, nil
|
||||
case fexpr.TokenNumber:
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
placeholder := "t" + security.PseudorandomString(8)
|
||||
|
||||
return &ResolverResult{
|
||||
Identifier: "{:" + placeholder + "}",
|
||||
Params: dbx.Params{placeholder: cast.ToFloat64(token.Literal)},
|
||||
}, nil
|
||||
case fexpr.TokenFunction:
|
||||
fn, ok := TokenFunctions[token.Literal]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown function %q", token.Literal)
|
||||
}
|
||||
|
||||
args, _ := token.Meta.([]fexpr.Token)
|
||||
return fn(func(argToken fexpr.Token) (*ResolverResult, error) {
|
||||
return resolveToken(argToken, fieldResolver)
|
||||
}, args...)
|
||||
}
|
||||
|
||||
return nil, errors.New("unresolvable token type")
|
||||
return nil, fmt.Errorf("unsupported token type %q", token.Type)
|
||||
}
|
||||
|
||||
// Resolves = and != expressions in an attempt to minimize the COALESCE
|
||||
@@ -614,8 +626,8 @@ func (e *manyVsManyExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
return "0=1"
|
||||
}
|
||||
|
||||
lAlias := "__ml" + security.PseudorandomString(5)
|
||||
rAlias := "__mr" + security.PseudorandomString(5)
|
||||
lAlias := "__ml" + security.PseudorandomString(8)
|
||||
rAlias := "__mr" + security.PseudorandomString(8)
|
||||
|
||||
whereExpr, buildErr := buildResolversExpr(
|
||||
&ResolverResult{
|
||||
@@ -671,7 +683,7 @@ func (e *manyVsOneExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
return "0=1"
|
||||
}
|
||||
|
||||
alias := "__sm" + security.PseudorandomString(5)
|
||||
alias := "__sm" + security.PseudorandomString(8)
|
||||
|
||||
r1 := &ResolverResult{
|
||||
NoCoalesce: e.noCoalesce,
|
||||
|
||||
@@ -139,6 +139,12 @@ func TestFilterDataBuildExpr(t *testing.T) {
|
||||
false,
|
||||
"((COALESCE([[test1]], '') = COALESCE([[test2]], '') OR COALESCE([[test2]], '') IS NOT COALESCE([[test3]], '')) AND ([[test2]] LIKE {:TEST} ESCAPE '\\' OR [[test2]] NOT LIKE {:TEST} ESCAPE '\\') AND {:TEST} LIKE ('%' || [[test1]] || '%') ESCAPE '\\' AND {:TEST} NOT LIKE ('%' || [[test2]] || '%') ESCAPE '\\' AND [[test3]] > {:TEST} AND [[test3]] >= {:TEST} AND [[test3]] <= {:TEST} AND {:TEST} < {:TEST})",
|
||||
},
|
||||
{
|
||||
"geoDistance function",
|
||||
"geoDistance(1,2,3,4) < 567",
|
||||
false,
|
||||
"(6371 * acos(cos(radians({:TEST})) * cos(radians({:TEST})) * cos(radians({:TEST}) - radians({:TEST})) + sin(radians({:TEST})) * sin(radians({:TEST})))) < {:TEST}",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
@@ -76,7 +76,7 @@ func (r *SimpleFieldResolver) UpdateQuery(query *dbx.SelectQuery) error {
|
||||
// Returns error if `field` is not in `r.allowedFields`.
|
||||
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)
|
||||
}
|
||||
|
||||
parts := strings.Split(field, ".")
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ganigeorgiev/fexpr"
|
||||
)
|
||||
|
||||
var TokenFunctions = map[string]func(
|
||||
argTokenResolverFunc func(fexpr.Token) (*ResolverResult, error),
|
||||
args ...fexpr.Token,
|
||||
) (*ResolverResult, error){
|
||||
// geoDistance(lonA, latA, lonB, latB) calculates the Haversine
|
||||
// distance between 2 points in kilometres (https://www.movable-type.co.uk/scripts/latlong.html).
|
||||
//
|
||||
// The accepted arguments at the moment could be either a plain number or a column identifier (including NULL).
|
||||
// If the column identifier cannot be resolved and converted to a numeric value, it resolves to NULL.
|
||||
//
|
||||
// Similar to the built-in SQLite functions, geoDistance doesn't apply
|
||||
// a "match-all" constraints in case there are multiple relation fields arguments.
|
||||
// Or in other words, if a collection has "orgs" multiple relation field pointing to "orgs" collection that has "office" as "geoPoint" field,
|
||||
// then the filter: `geoDistance(orgs.office.lon, orgs.office.lat, 1, 2) < 200`
|
||||
// will evaluate to true if for at-least-one of the "orgs.office" records the function result in a value satisfying the condition (aka. "result < 200").
|
||||
"geoDistance": func(argTokenResolverFunc func(fexpr.Token) (*ResolverResult, error), args ...fexpr.Token) (*ResolverResult, error) {
|
||||
if len(args) != 4 {
|
||||
return nil, fmt.Errorf("[geoDistance] expected 4 arguments, got %d", len(args))
|
||||
}
|
||||
|
||||
resolvedArgs := make([]*ResolverResult, 4)
|
||||
for i, arg := range args {
|
||||
if arg.Type != fexpr.TokenIdentifier && arg.Type != fexpr.TokenNumber {
|
||||
return nil, fmt.Errorf("[geoDistance] argument %d must be an identifier or number", i)
|
||||
}
|
||||
resolved, err := argTokenResolverFunc(arg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("[geoDistance] failed to resolve argument %d: %w", i, err)
|
||||
}
|
||||
resolvedArgs[i] = resolved
|
||||
}
|
||||
|
||||
lonA := resolvedArgs[0].Identifier
|
||||
latA := resolvedArgs[1].Identifier
|
||||
lonB := resolvedArgs[2].Identifier
|
||||
latB := resolvedArgs[3].Identifier
|
||||
|
||||
return &ResolverResult{
|
||||
NoCoalesce: true,
|
||||
Identifier: `(6371 * acos(` +
|
||||
`cos(radians(` + latA + `)) * cos(radians(` + latB + `)) * ` +
|
||||
`cos(radians(` + lonB + `) - radians(` + lonA + `)) + ` +
|
||||
`sin(radians(` + latA + `)) * sin(radians(` + latB + `))` +
|
||||
`))`,
|
||||
Params: mergeParams(resolvedArgs[0].Params, resolvedArgs[1].Params, resolvedArgs[2].Params, resolvedArgs[3].Params),
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ganigeorgiev/fexpr"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
func TestTokenFunctionsGeoDistance(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testDB, err := createTestDB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testDB.Close()
|
||||
|
||||
fn, ok := TokenFunctions["geoDistance"]
|
||||
if !ok {
|
||||
t.Error("Expected geoDistance token function to be registered.")
|
||||
}
|
||||
|
||||
baseTokenResolver := func(t fexpr.Token) (*ResolverResult, error) {
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
return &ResolverResult{Identifier: "{:" + placeholder + "}", Params: map[string]any{placeholder: t.Literal}}, nil
|
||||
}
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
args []fexpr.Token
|
||||
resolver func(t fexpr.Token) (*ResolverResult, error)
|
||||
result *ResolverResult
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
"no args",
|
||||
nil,
|
||||
baseTokenResolver,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"< 4 args",
|
||||
[]fexpr.Token{
|
||||
{Literal: "1", Type: fexpr.TokenNumber},
|
||||
{Literal: "2", Type: fexpr.TokenNumber},
|
||||
{Literal: "3", Type: fexpr.TokenNumber},
|
||||
},
|
||||
baseTokenResolver,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"> 4 args",
|
||||
[]fexpr.Token{
|
||||
{Literal: "1", Type: fexpr.TokenNumber},
|
||||
{Literal: "2", Type: fexpr.TokenNumber},
|
||||
{Literal: "3", Type: fexpr.TokenNumber},
|
||||
{Literal: "4", Type: fexpr.TokenNumber},
|
||||
{Literal: "5", Type: fexpr.TokenNumber},
|
||||
},
|
||||
baseTokenResolver,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"unsupported function argument",
|
||||
[]fexpr.Token{
|
||||
{Literal: "1", Type: fexpr.TokenFunction},
|
||||
{Literal: "2", Type: fexpr.TokenNumber},
|
||||
{Literal: "3", Type: fexpr.TokenNumber},
|
||||
{Literal: "4", Type: fexpr.TokenNumber},
|
||||
},
|
||||
baseTokenResolver,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"unsupported text argument",
|
||||
[]fexpr.Token{
|
||||
{Literal: "1", Type: fexpr.TokenText},
|
||||
{Literal: "2", Type: fexpr.TokenNumber},
|
||||
{Literal: "3", Type: fexpr.TokenNumber},
|
||||
{Literal: "4", Type: fexpr.TokenNumber},
|
||||
},
|
||||
baseTokenResolver,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"4 valid arguments but with resolver error",
|
||||
[]fexpr.Token{
|
||||
{Literal: "1", Type: fexpr.TokenNumber},
|
||||
{Literal: "2", Type: fexpr.TokenNumber},
|
||||
{Literal: "3", Type: fexpr.TokenNumber},
|
||||
{Literal: "4", Type: fexpr.TokenNumber},
|
||||
},
|
||||
func(t fexpr.Token) (*ResolverResult, error) {
|
||||
return nil, errors.New("test")
|
||||
},
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"4 valid arguments",
|
||||
[]fexpr.Token{
|
||||
{Literal: "1", Type: fexpr.TokenNumber},
|
||||
{Literal: "2", Type: fexpr.TokenNumber},
|
||||
{Literal: "3", Type: fexpr.TokenNumber},
|
||||
{Literal: "4", Type: fexpr.TokenNumber},
|
||||
},
|
||||
baseTokenResolver,
|
||||
&ResolverResult{
|
||||
NoCoalesce: true,
|
||||
Identifier: `(6371 * acos(cos(radians({:latA})) * cos(radians({:latB})) * cos(radians({:lonB}) - radians({:lonA})) + sin(radians({:latA})) * sin(radians({:latB}))))`,
|
||||
Params: map[string]any{
|
||||
"lonA": 1,
|
||||
"latA": 2,
|
||||
"lonB": 3,
|
||||
"latB": 4,
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"mixed arguments",
|
||||
[]fexpr.Token{
|
||||
{Literal: "null", Type: fexpr.TokenIdentifier},
|
||||
{Literal: "2", Type: fexpr.TokenNumber},
|
||||
{Literal: "false", Type: fexpr.TokenIdentifier},
|
||||
{Literal: "4", Type: fexpr.TokenNumber},
|
||||
},
|
||||
baseTokenResolver,
|
||||
&ResolverResult{
|
||||
NoCoalesce: true,
|
||||
Identifier: `(6371 * acos(cos(radians({:latA})) * cos(radians({:latB})) * cos(radians({:lonB}) - radians({:lonA})) + sin(radians({:latA})) * sin(radians({:latB}))))`,
|
||||
Params: map[string]any{
|
||||
"lonA": "null",
|
||||
"latA": 2,
|
||||
"lonB": false,
|
||||
"latB": 4,
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
result, err := fn(s.resolver, s.args...)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectErr {
|
||||
t.Fatalf("Expected hasErr %v, got %v (%v)", s.expectErr, hasErr, err)
|
||||
}
|
||||
|
||||
testCompareResults(t, s.result, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenFunctionsGeoDistanceExec(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testDB, err := createTestDB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testDB.Close()
|
||||
|
||||
fn, ok := TokenFunctions["geoDistance"]
|
||||
if !ok {
|
||||
t.Error("Expected geoDistance token function to be registered.")
|
||||
}
|
||||
|
||||
result, err := fn(
|
||||
func(t fexpr.Token) (*ResolverResult, error) {
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
return &ResolverResult{Identifier: "{:" + placeholder + "}", Params: map[string]any{placeholder: t.Literal}}, nil
|
||||
},
|
||||
fexpr.Token{Literal: "23.23033854945808", Type: fexpr.TokenNumber},
|
||||
fexpr.Token{Literal: "42.713146090563384", Type: fexpr.TokenNumber},
|
||||
fexpr.Token{Literal: "23.44920680886216", Type: fexpr.TokenNumber},
|
||||
fexpr.Token{Literal: "42.7078484153991", Type: fexpr.TokenNumber},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
column := []float64{}
|
||||
err = testDB.NewQuery("select " + result.Identifier).Bind(result.Params).Column(&column)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(column) != 1 {
|
||||
t.Fatalf("Expected exactly 1 column value as result, got %v", column)
|
||||
}
|
||||
|
||||
expected := "17.89"
|
||||
distance := fmt.Sprintf("%.2f", column[0])
|
||||
if distance != expected {
|
||||
t.Fatalf("Expected distance value %s, got %s", expected, distance)
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
func testCompareResults(t *testing.T, a, b *ResolverResult) {
|
||||
testDB, err := createTestDB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testDB.Close()
|
||||
|
||||
aIsNil := a == nil
|
||||
bIsNil := b == nil
|
||||
if aIsNil != bIsNil {
|
||||
t.Fatalf("Expected aIsNil and bIsNil to be the same, got %v vs %v", aIsNil, bIsNil)
|
||||
}
|
||||
|
||||
if aIsNil && bIsNil {
|
||||
return
|
||||
}
|
||||
|
||||
aHasAfterBuild := a.AfterBuild == nil
|
||||
bHasAfterBuild := b.AfterBuild == nil
|
||||
if aHasAfterBuild != bHasAfterBuild {
|
||||
t.Fatalf("Expected aHasAfterBuild and bHasAfterBuild to be the same, got %v vs %v", aHasAfterBuild, bHasAfterBuild)
|
||||
}
|
||||
|
||||
var aAfterBuild string
|
||||
if a.AfterBuild != nil {
|
||||
aAfterBuild = a.AfterBuild(dbx.NewExp("test")).Build(testDB.DB, a.Params)
|
||||
}
|
||||
var bAfterBuild string
|
||||
if b.AfterBuild != nil {
|
||||
bAfterBuild = b.AfterBuild(dbx.NewExp("test")).Build(testDB.DB, a.Params)
|
||||
}
|
||||
if aAfterBuild != bAfterBuild {
|
||||
t.Fatalf("Expected bAfterBuild and bAfterBuild to be the same, got\n%s\nvs\n%s", aAfterBuild, bAfterBuild)
|
||||
}
|
||||
|
||||
var aMultiMatchSubQuery string
|
||||
if a.MultiMatchSubQuery != nil {
|
||||
aMultiMatchSubQuery = a.MultiMatchSubQuery.Build(testDB.DB, a.Params)
|
||||
}
|
||||
var bMultiMatchSubQuery string
|
||||
if b.MultiMatchSubQuery != nil {
|
||||
bMultiMatchSubQuery = b.MultiMatchSubQuery.Build(testDB.DB, b.Params)
|
||||
}
|
||||
if aMultiMatchSubQuery != bMultiMatchSubQuery {
|
||||
t.Fatalf("Expected bMultiMatchSubQuery and bMultiMatchSubQuery to be the same, got\n%s\nvs\n%s", aMultiMatchSubQuery, bMultiMatchSubQuery)
|
||||
}
|
||||
|
||||
if a.NoCoalesce != b.NoCoalesce {
|
||||
t.Fatalf("Expected NoCoalesce to match, got %v vs %v", a.NoCoalesce, b.NoCoalesce)
|
||||
}
|
||||
|
||||
// loose placeholders replacement
|
||||
var aResolved = a.Identifier
|
||||
for k, v := range a.Params {
|
||||
aResolved = strings.ReplaceAll(aResolved, "{:"+k+"}", fmt.Sprintf("%v", v))
|
||||
}
|
||||
var bResolved = b.Identifier
|
||||
for k, v := range b.Params {
|
||||
bResolved = strings.ReplaceAll(bResolved, "{:"+k+"}", fmt.Sprintf("%v", v))
|
||||
}
|
||||
if aResolved != bResolved {
|
||||
t.Fatalf("Expected resolved identifiers to match, got\n%s\nvs\n%s", aResolved, bResolved)
|
||||
}
|
||||
}
|
||||
@@ -142,9 +142,10 @@ func (t *Tokenizer) readToken() (string, error) {
|
||||
} else if !t.ignoreParenthesis && ch == ')' && parenthesis > 0 && quoteCh == eof {
|
||||
parenthesis-- // closing parenthesis
|
||||
} else if t.isQuoteRune(ch) {
|
||||
if quoteCh == ch {
|
||||
switch quoteCh {
|
||||
case ch:
|
||||
quoteCh = eof // closing quote
|
||||
} else if quoteCh == eof {
|
||||
case eof:
|
||||
quoteCh = ch // opening quote
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GeoPoint defines a struct for storing geo coordinates as serialized json object
|
||||
// (e.g. {lon:0,lat:0}).
|
||||
//
|
||||
// Note: using object notation and not a plain array to avoid the confusion
|
||||
// as there doesn't seem to be a fixed standard for the coordinates order.
|
||||
type GeoPoint struct {
|
||||
Lon float64 `form:"lon" json:"lon"`
|
||||
Lat float64 `form:"lat" json:"lat"`
|
||||
}
|
||||
|
||||
// String returns the string representation of the current GeoPoint instance.
|
||||
func (p GeoPoint) String() string {
|
||||
raw, _ := json.Marshal(p)
|
||||
return string(raw)
|
||||
}
|
||||
|
||||
// AsMap implements [core.mapExtractor] and returns a value suitable
|
||||
// to be used in an API rule expression.
|
||||
func (p GeoPoint) AsMap() map[string]any {
|
||||
return map[string]any{
|
||||
"lon": p.Lon,
|
||||
"lat": p.Lat,
|
||||
}
|
||||
}
|
||||
|
||||
// Value implements the [driver.Valuer] interface.
|
||||
func (p GeoPoint) Value() (driver.Value, error) {
|
||||
data, err := json.Marshal(p)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// Scan implements [sql.Scanner] interface to scan the provided value
|
||||
// into the current GeoPoint instance.
|
||||
//
|
||||
// The value argument could be nil (no-op), another GeoPoint instance,
|
||||
// map or serialized json object with lat-lon props.
|
||||
func (p *GeoPoint) Scan(value any) error {
|
||||
var err error
|
||||
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
// no cast needed
|
||||
case *GeoPoint:
|
||||
p.Lon = v.Lon
|
||||
p.Lat = v.Lat
|
||||
case GeoPoint:
|
||||
p.Lon = v.Lon
|
||||
p.Lat = v.Lat
|
||||
case JSONRaw:
|
||||
if len(v) != 0 {
|
||||
err = json.Unmarshal(v, p)
|
||||
}
|
||||
case []byte:
|
||||
if len(v) != 0 {
|
||||
err = json.Unmarshal(v, p)
|
||||
}
|
||||
case string:
|
||||
if len(v) != 0 {
|
||||
err = json.Unmarshal([]byte(v), p)
|
||||
}
|
||||
default:
|
||||
var raw []byte
|
||||
raw, err = json.Marshal(v)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("unable to marshalize value for scanning: %w", err)
|
||||
} else {
|
||||
err = json.Unmarshal(raw, p)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("[GeoPoint] unable to scan value %v: %w", value, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
func TestGeoPointAsMap(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
point types.GeoPoint
|
||||
expected map[string]any
|
||||
}{
|
||||
{"zero", types.GeoPoint{}, map[string]any{"lon": 0.0, "lat": 0.0}},
|
||||
{"non-zero", types.GeoPoint{Lon: -10, Lat: 20.123}, map[string]any{"lon": -10.0, "lat": 20.123}},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
result := s.point.AsMap()
|
||||
|
||||
if len(result) != len(s.expected) {
|
||||
t.Fatalf("Expected %d keys, got %d: %v", len(s.expected), len(result), result)
|
||||
}
|
||||
|
||||
for k, v := range s.expected {
|
||||
found, ok := result[k]
|
||||
if !ok {
|
||||
t.Fatalf("Missing expected %q key: %v", k, result)
|
||||
}
|
||||
|
||||
if found != v {
|
||||
t.Fatalf("Expected %q key value %v, got %v", k, v, found)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoPointStringAndValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
point types.GeoPoint
|
||||
expected string
|
||||
}{
|
||||
{"zero", types.GeoPoint{}, `{"lon":0,"lat":0}`},
|
||||
{"non-zero", types.GeoPoint{Lon: -10, Lat: 20.123}, `{"lon":-10,"lat":20.123}`},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
str := s.point.String()
|
||||
|
||||
val, err := s.point.Value()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if str != val {
|
||||
t.Fatalf("Expected String and Value to return the same value")
|
||||
}
|
||||
|
||||
if str != s.expected {
|
||||
t.Fatalf("Expected\n%s\ngot\n%s", s.expected, str)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeoPointScan(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []struct {
|
||||
value any
|
||||
expectErr bool
|
||||
expectStr string
|
||||
}{
|
||||
{nil, false, `{"lon":1,"lat":2}`},
|
||||
{"", false, `{"lon":1,"lat":2}`},
|
||||
{types.JSONRaw{}, false, `{"lon":1,"lat":2}`},
|
||||
{[]byte{}, false, `{"lon":1,"lat":2}`},
|
||||
{`{}`, false, `{"lon":1,"lat":2}`},
|
||||
{`[]`, true, `{"lon":1,"lat":2}`},
|
||||
{0, true, `{"lon":1,"lat":2}`},
|
||||
{`{"lon":"1.23","lat":"4.56"}`, true, `{"lon":1,"lat":2}`},
|
||||
{`{"lon":1.23,"lat":4.56}`, false, `{"lon":1.23,"lat":4.56}`},
|
||||
{[]byte(`{"lon":1.23,"lat":4.56}`), false, `{"lon":1.23,"lat":4.56}`},
|
||||
{types.JSONRaw(`{"lon":1.23,"lat":4.56}`), false, `{"lon":1.23,"lat":4.56}`},
|
||||
{types.GeoPoint{}, false, `{"lon":0,"lat":0}`},
|
||||
{types.GeoPoint{Lon: 1.23, Lat: 4.56}, false, `{"lon":1.23,"lat":4.56}`},
|
||||
{&types.GeoPoint{Lon: 1.23, Lat: 4.56}, false, `{"lon":1.23,"lat":4.56}`},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("%d_%#v", i, s.value), func(t *testing.T) {
|
||||
point := types.GeoPoint{Lon: 1, Lat: 2}
|
||||
|
||||
err := point.Scan(s.value)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectErr {
|
||||
t.Errorf("Expected hasErr %v, got %v (%v)", s.expectErr, hasErr, err)
|
||||
}
|
||||
|
||||
if str := point.String(); str != s.expectStr {
|
||||
t.Errorf("Expected\n%s\ngot\n%s", s.expectStr, str)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ func (m *JSONArray[T]) Scan(value any) error {
|
||||
case string:
|
||||
data = []byte(v)
|
||||
default:
|
||||
return fmt.Errorf("Failed to unmarshal JSONArray value: %q.", value)
|
||||
return fmt.Errorf("failed to unmarshal JSONArray value: %q", value)
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
|
||||
@@ -9,4 +9,4 @@ PB_DOCS_URL = "https://pocketbase.io/docs"
|
||||
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
|
||||
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
|
||||
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
|
||||
PB_VERSION = "v0.26.2"
|
||||
PB_VERSION = "v0.27.2"
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as Ce,i as Be,s as Te,V as Le,X as J,h as u,d as ae,t as Q,a as G,I as N,Z as we,_ as Se,C as De,$ as Re,D as Ue,l as d,n as a,m as ne,u as c,A as y,v as k,c as ie,w as h,p as oe,J as je,k as O,o as qe,W as Ee}from"./index-ChG9Kpfq.js";import{F as Fe}from"./FieldsQueryParam-DkRoNbFY.js";function ye(n,s,l){const o=n.slice();return o[8]=s[l],o}function Me(n,s,l){const o=n.slice();return o[8]=s[l],o}function Ae(n,s){let l,o=s[8].code+"",p,b,i,f;function m(){return s[6](s[8])}return{key:n,first:null,c(){l=c("button"),p=y(o),b=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(v,$){d(v,l,$),a(l,p),a(l,b),i||(f=qe(l,"click",m),i=!0)},p(v,$){s=v,$&4&&o!==(o=s[8].code+"")&&N(p,o),$&6&&O(l,"active",s[1]===s[8].code)},d(v){v&&u(l),i=!1,f()}}}function Pe(n,s){let l,o,p,b;return o=new Ee({props:{content:s[8].body}}),{key:n,first:null,c(){l=c("div"),ie(o.$$.fragment),p=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(i,f){d(i,l,f),ne(o,l,null),a(l,p),b=!0},p(i,f){s=i;const m={};f&4&&(m.content=s[8].body),o.$set(m),(!b||f&6)&&O(l,"active",s[1]===s[8].code)},i(i){b||(G(o.$$.fragment,i),b=!0)},o(i){Q(o.$$.fragment,i),b=!1},d(i){i&&u(l),ae(o)}}}function He(n){var ke,ge;let s,l,o=n[0].name+"",p,b,i,f,m,v,$,g=n[0].name+"",V,ce,W,M,X,L,Z,A,E,re,F,S,ue,z,H=n[0].name+"",K,de,Y,D,x,P,ee,fe,te,T,le,R,se,C,U,w=[],me=new Map,pe,j,_=[],be=new Map,B;M=new Le({props:{js:`
|
||||
import{S as Ce,i as Be,s as Te,V as Le,X as J,h as u,d as ae,t as Q,a as G,I as N,Z as we,_ as Se,C as De,$ as Re,D as Ue,l as d,n as a,m as ne,u as c,A as y,v as k,c as ie,w as h,p as oe,J as je,k as O,o as qe,W as Ee}from"./index-rRnVk674.js";import{F as Fe}from"./FieldsQueryParam-BEgqkb0I.js";function ye(n,s,l){const o=n.slice();return o[8]=s[l],o}function Me(n,s,l){const o=n.slice();return o[8]=s[l],o}function Ae(n,s){let l,o=s[8].code+"",p,b,i,f;function m(){return s[6](s[8])}return{key:n,first:null,c(){l=c("button"),p=y(o),b=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(v,$){d(v,l,$),a(l,p),a(l,b),i||(f=qe(l,"click",m),i=!0)},p(v,$){s=v,$&4&&o!==(o=s[8].code+"")&&N(p,o),$&6&&O(l,"active",s[1]===s[8].code)},d(v){v&&u(l),i=!1,f()}}}function Pe(n,s){let l,o,p,b;return o=new Ee({props:{content:s[8].body}}),{key:n,first:null,c(){l=c("div"),ie(o.$$.fragment),p=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(i,f){d(i,l,f),ne(o,l,null),a(l,p),b=!0},p(i,f){s=i;const m={};f&4&&(m.content=s[8].body),o.$set(m),(!b||f&6)&&O(l,"active",s[1]===s[8].code)},i(i){b||(G(o.$$.fragment,i),b=!0)},o(i){Q(o.$$.fragment,i),b=!1},d(i){i&&u(l),ae(o)}}}function He(n){var ke,ge;let s,l,o=n[0].name+"",p,b,i,f,m,v,$,g=n[0].name+"",V,ce,W,M,X,L,Z,A,E,re,F,S,ue,z,H=n[0].name+"",K,de,Y,D,x,P,ee,fe,te,T,le,R,se,C,U,w=[],me=new Map,pe,j,_=[],be=new Map,B;M=new Le({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[3]}');
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as je,i as xe,s as Ie,V as Ke,W as Ue,X as I,h as d,d as K,t as E,a as z,I as de,Z as Oe,_ as Qe,C as We,$ as Xe,D as Ze,l as u,n as o,m as Q,u as s,A as k,v as p,c as W,w as b,J as Ve,p as Ge,k as X,o as Ye}from"./index-ChG9Kpfq.js";import{F as et}from"./FieldsQueryParam-DkRoNbFY.js";function Ee(r,a,l){const n=r.slice();return n[5]=a[l],n}function ze(r,a,l){const n=r.slice();return n[5]=a[l],n}function Je(r,a){let l,n=a[5].code+"",m,_,i,h;function g(){return a[4](a[5])}return{key:r,first:null,c(){l=s("button"),m=k(n),_=p(),b(l,"class","tab-item"),X(l,"active",a[1]===a[5].code),this.first=l},m(v,w){u(v,l,w),o(l,m),o(l,_),i||(h=Ye(l,"click",g),i=!0)},p(v,w){a=v,w&4&&n!==(n=a[5].code+"")&&de(m,n),w&6&&X(l,"active",a[1]===a[5].code)},d(v){v&&d(l),i=!1,h()}}}function Ne(r,a){let l,n,m,_;return n=new Ue({props:{content:a[5].body}}),{key:r,first:null,c(){l=s("div"),W(n.$$.fragment),m=p(),b(l,"class","tab-item"),X(l,"active",a[1]===a[5].code),this.first=l},m(i,h){u(i,l,h),Q(n,l,null),o(l,m),_=!0},p(i,h){a=i;const g={};h&4&&(g.content=a[5].body),n.$set(g),(!_||h&6)&&X(l,"active",a[1]===a[5].code)},i(i){_||(z(n.$$.fragment,i),_=!0)},o(i){E(n.$$.fragment,i),_=!1},d(i){i&&d(l),K(n)}}}function tt(r){var qe,Fe;let a,l,n=r[0].name+"",m,_,i,h,g,v,w,D,Z,S,J,ue,N,M,pe,G,U=r[0].name+"",Y,he,fe,j,ee,q,te,T,oe,be,F,C,ae,me,le,_e,f,ke,P,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Re,A,ie,H,ce,R,L,y=[],Pe=new Map,Ae,O,$=[],Be=new Map,B;v=new Ke({props:{js:`
|
||||
import{S as je,i as xe,s as Ie,V as Ke,W as Ue,X as I,h as d,d as K,t as E,a as z,I as de,Z as Oe,_ as Qe,C as We,$ as Xe,D as Ze,l as u,n as o,m as Q,u as s,A as k,v as p,c as W,w as b,J as Ve,p as Ge,k as X,o as Ye}from"./index-rRnVk674.js";import{F as et}from"./FieldsQueryParam-BEgqkb0I.js";function Ee(r,a,l){const n=r.slice();return n[5]=a[l],n}function ze(r,a,l){const n=r.slice();return n[5]=a[l],n}function Je(r,a){let l,n=a[5].code+"",m,_,i,h;function g(){return a[4](a[5])}return{key:r,first:null,c(){l=s("button"),m=k(n),_=p(),b(l,"class","tab-item"),X(l,"active",a[1]===a[5].code),this.first=l},m(v,w){u(v,l,w),o(l,m),o(l,_),i||(h=Ye(l,"click",g),i=!0)},p(v,w){a=v,w&4&&n!==(n=a[5].code+"")&&de(m,n),w&6&&X(l,"active",a[1]===a[5].code)},d(v){v&&d(l),i=!1,h()}}}function Ne(r,a){let l,n,m,_;return n=new Ue({props:{content:a[5].body}}),{key:r,first:null,c(){l=s("div"),W(n.$$.fragment),m=p(),b(l,"class","tab-item"),X(l,"active",a[1]===a[5].code),this.first=l},m(i,h){u(i,l,h),Q(n,l,null),o(l,m),_=!0},p(i,h){a=i;const g={};h&4&&(g.content=a[5].body),n.$set(g),(!_||h&6)&&X(l,"active",a[1]===a[5].code)},i(i){_||(z(n.$$.fragment,i),_=!0)},o(i){E(n.$$.fragment,i),_=!1},d(i){i&&d(l),K(n)}}}function tt(r){var qe,Fe;let a,l,n=r[0].name+"",m,_,i,h,g,v,w,D,Z,S,J,ue,N,M,pe,G,U=r[0].name+"",Y,he,fe,j,ee,q,te,T,oe,be,F,C,ae,me,le,_e,f,ke,P,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Re,A,ie,H,ce,R,L,y=[],Pe=new Map,Ae,O,$=[],Be=new Map,B;v=new Ke({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r[3]}');
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as Je,i as xe,s as Ee,V as Ne,W as je,X as Q,h as r,d as Z,t as j,a as J,I as pe,Z as Ue,_ as Ie,C as Qe,$ as Ze,D as ze,l as c,n as a,m as z,u as o,A as _,v as h,c as K,w as p,J as Be,p as Ke,k as X,o as Xe}from"./index-ChG9Kpfq.js";import{F as Ge}from"./FieldsQueryParam-DkRoNbFY.js";function Fe(s,l,n){const i=s.slice();return i[5]=l[n],i}function Le(s,l,n){const i=s.slice();return i[5]=l[n],i}function He(s,l){let n,i=l[5].code+"",f,g,d,b;function k(){return l[4](l[5])}return{key:s,first:null,c(){n=o("button"),f=_(i),g=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(v,O){c(v,n,O),a(n,f),a(n,g),d||(b=Xe(n,"click",k),d=!0)},p(v,O){l=v,O&4&&i!==(i=l[5].code+"")&&pe(f,i),O&6&&X(n,"active",l[1]===l[5].code)},d(v){v&&r(n),d=!1,b()}}}function Ve(s,l){let n,i,f,g;return i=new je({props:{content:l[5].body}}),{key:s,first:null,c(){n=o("div"),K(i.$$.fragment),f=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(d,b){c(d,n,b),z(i,n,null),a(n,f),g=!0},p(d,b){l=d;const k={};b&4&&(k.content=l[5].body),i.$set(k),(!g||b&6)&&X(n,"active",l[1]===l[5].code)},i(d){g||(J(i.$$.fragment,d),g=!0)},o(d){j(i.$$.fragment,d),g=!1},d(d){d&&r(n),Z(i)}}}function Ye(s){let l,n,i=s[0].name+"",f,g,d,b,k,v,O,R,G,A,x,be,E,P,me,Y,N=s[0].name+"",ee,fe,te,M,ae,W,le,U,ne,y,oe,ge,B,S,se,_e,ie,ke,m,ve,C,we,$e,Oe,re,Ae,ce,ye,Se,Te,de,Ce,qe,q,ue,F,he,T,L,$=[],De=new Map,Re,H,w=[],Pe=new Map,D;v=new Ne({props:{js:`
|
||||
import{S as Je,i as xe,s as Ee,V as Ne,W as je,X as Q,h as r,d as Z,t as j,a as J,I as pe,Z as Ue,_ as Ie,C as Qe,$ as Ze,D as ze,l as c,n as a,m as z,u as o,A as _,v as h,c as K,w as p,J as Be,p as Ke,k as X,o as Xe}from"./index-rRnVk674.js";import{F as Ge}from"./FieldsQueryParam-BEgqkb0I.js";function Fe(s,l,n){const i=s.slice();return i[5]=l[n],i}function Le(s,l,n){const i=s.slice();return i[5]=l[n],i}function He(s,l){let n,i=l[5].code+"",f,g,d,b;function k(){return l[4](l[5])}return{key:s,first:null,c(){n=o("button"),f=_(i),g=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(v,O){c(v,n,O),a(n,f),a(n,g),d||(b=Xe(n,"click",k),d=!0)},p(v,O){l=v,O&4&&i!==(i=l[5].code+"")&&pe(f,i),O&6&&X(n,"active",l[1]===l[5].code)},d(v){v&&r(n),d=!1,b()}}}function Ve(s,l){let n,i,f,g;return i=new je({props:{content:l[5].body}}),{key:s,first:null,c(){n=o("div"),K(i.$$.fragment),f=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(d,b){c(d,n,b),z(i,n,null),a(n,f),g=!0},p(d,b){l=d;const k={};b&4&&(k.content=l[5].body),i.$set(k),(!g||b&6)&&X(n,"active",l[1]===l[5].code)},i(d){g||(J(i.$$.fragment,d),g=!0)},o(d){j(i.$$.fragment,d),g=!1},d(d){d&&r(n),Z(i)}}}function Ye(s){let l,n,i=s[0].name+"",f,g,d,b,k,v,O,R,G,A,x,be,E,P,me,Y,N=s[0].name+"",ee,fe,te,M,ae,W,le,U,ne,y,oe,ge,B,S,se,_e,ie,ke,m,ve,C,we,$e,Oe,re,Ae,ce,ye,Se,Te,de,Ce,qe,q,ue,F,he,T,L,$=[],De=new Map,Re,H,w=[],Pe=new Map,D;v=new Ne({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${s[3]}');
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as be,i as _e,s as ve,W as ge,X as V,h as b,d as x,t as j,a as J,I as ce,Z as de,_ as je,C as ue,$ as Qe,D as he,l as _,n as s,m as ee,u as d,v as T,A as R,c as te,w as g,J as ke,k as N,o as $e,V as Ke,Y as De,p as Xe,a0 as Me}from"./index-ChG9Kpfq.js";import{F as Ze}from"./FieldsQueryParam-DkRoNbFY.js";function Be(a,t,e){const l=a.slice();return l[4]=t[e],l}function Ie(a,t,e){const l=a.slice();return l[4]=t[e],l}function We(a,t){let e,l=t[4].code+"",h,i,c,n;function m(){return t[3](t[4])}return{key:a,first:null,c(){e=d("button"),h=R(l),i=T(),g(e,"class","tab-item"),N(e,"active",t[1]===t[4].code),this.first=e},m(v,C){_(v,e,C),s(e,h),s(e,i),c||(n=$e(e,"click",m),c=!0)},p(v,C){t=v,C&4&&l!==(l=t[4].code+"")&&ce(h,l),C&6&&N(e,"active",t[1]===t[4].code)},d(v){v&&b(e),c=!1,n()}}}function Fe(a,t){let e,l,h,i;return l=new ge({props:{content:t[4].body}}),{key:a,first:null,c(){e=d("div"),te(l.$$.fragment),h=T(),g(e,"class","tab-item"),N(e,"active",t[1]===t[4].code),this.first=e},m(c,n){_(c,e,n),ee(l,e,null),s(e,h),i=!0},p(c,n){t=c;const m={};n&4&&(m.content=t[4].body),l.$set(m),(!i||n&6)&&N(e,"active",t[1]===t[4].code)},i(c){i||(J(l.$$.fragment,c),i=!0)},o(c){j(l.$$.fragment,c),i=!1},d(c){c&&b(e),x(l)}}}function ze(a){let t,e,l,h,i,c,n,m=a[0].name+"",v,C,F,B,I,D,Q,M,U,y,O,q,k,L,Y,A,X,E,o,$,P,z,u,p,S,w,Z,we,Te,Pe,pe,Oe,ye,le,fe,oe,me,G,ae,K=[],Se=new Map,qe,ne,H=[],Ce=new Map,se;P=new ge({props:{content:"?expand=relField1,relField2.subRelField"}}),le=new Ze({props:{prefix:"record."}});let re=V(a[2]);const Ae=r=>r[4].code;for(let r=0;r<re.length;r+=1){let f=Ie(a,re,r),W=Ae(f);Se.set(W,K[r]=We(W,f))}let ie=V(a[2]);const Re=r=>r[4].code;for(let r=0;r<ie.length;r+=1){let f=Be(a,ie,r),W=Re(f);Ce.set(W,H[r]=Fe(W,f))}return{c(){t=d("div"),e=d("strong"),e.textContent="POST",l=T(),h=d("div"),i=d("p"),c=R("/api/collections/"),n=d("strong"),v=R(m),C=R("/auth-with-otp"),F=T(),B=d("div"),B.textContent="Body Parameters",I=T(),D=d("table"),D.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>otpId</span></div></td> <td><span class="label">String</span></td> <td>The id of the OTP request.</td></tr> <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 one-time password.</td></tr></tbody>',Q=T(),M=d("div"),M.textContent="Query parameters",U=T(),y=d("table"),O=d("thead"),O.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',q=T(),k=d("tbody"),L=d("tr"),Y=d("td"),Y.textContent="expand",A=T(),X=d("td"),X.innerHTML='<span class="label">String</span>',E=T(),o=d("td"),$=R(`Auto expand record relations. Ex.:
|
||||
import{S as be,i as _e,s as ve,W as ge,X as V,h as b,d as x,t as j,a as J,I as ce,Z as de,_ as je,C as ue,$ as Qe,D as he,l as _,n as s,m as ee,u as d,v as T,A as R,c as te,w as g,J as ke,k as N,o as $e,V as Ke,Y as De,p as Xe,a0 as Me}from"./index-rRnVk674.js";import{F as Ze}from"./FieldsQueryParam-BEgqkb0I.js";function Be(a,t,e){const l=a.slice();return l[4]=t[e],l}function Ie(a,t,e){const l=a.slice();return l[4]=t[e],l}function We(a,t){let e,l=t[4].code+"",h,i,c,n;function m(){return t[3](t[4])}return{key:a,first:null,c(){e=d("button"),h=R(l),i=T(),g(e,"class","tab-item"),N(e,"active",t[1]===t[4].code),this.first=e},m(v,C){_(v,e,C),s(e,h),s(e,i),c||(n=$e(e,"click",m),c=!0)},p(v,C){t=v,C&4&&l!==(l=t[4].code+"")&&ce(h,l),C&6&&N(e,"active",t[1]===t[4].code)},d(v){v&&b(e),c=!1,n()}}}function Fe(a,t){let e,l,h,i;return l=new ge({props:{content:t[4].body}}),{key:a,first:null,c(){e=d("div"),te(l.$$.fragment),h=T(),g(e,"class","tab-item"),N(e,"active",t[1]===t[4].code),this.first=e},m(c,n){_(c,e,n),ee(l,e,null),s(e,h),i=!0},p(c,n){t=c;const m={};n&4&&(m.content=t[4].body),l.$set(m),(!i||n&6)&&N(e,"active",t[1]===t[4].code)},i(c){i||(J(l.$$.fragment,c),i=!0)},o(c){j(l.$$.fragment,c),i=!1},d(c){c&&b(e),x(l)}}}function ze(a){let t,e,l,h,i,c,n,m=a[0].name+"",v,C,F,B,I,D,Q,M,U,y,O,q,k,L,Y,A,X,E,o,$,P,z,u,p,S,w,Z,we,Te,Pe,pe,Oe,ye,le,fe,oe,me,G,ae,K=[],Se=new Map,qe,ne,H=[],Ce=new Map,se;P=new ge({props:{content:"?expand=relField1,relField2.subRelField"}}),le=new Ze({props:{prefix:"record."}});let re=V(a[2]);const Ae=r=>r[4].code;for(let r=0;r<re.length;r+=1){let f=Ie(a,re,r),W=Ae(f);Se.set(W,K[r]=We(W,f))}let ie=V(a[2]);const Re=r=>r[4].code;for(let r=0;r<ie.length;r+=1){let f=Be(a,ie,r),W=Re(f);Ce.set(W,H[r]=Fe(W,f))}return{c(){t=d("div"),e=d("strong"),e.textContent="POST",l=T(),h=d("div"),i=d("p"),c=R("/api/collections/"),n=d("strong"),v=R(m),C=R("/auth-with-otp"),F=T(),B=d("div"),B.textContent="Body Parameters",I=T(),D=d("table"),D.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>otpId</span></div></td> <td><span class="label">String</span></td> <td>The id of the OTP request.</td></tr> <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 one-time password.</td></tr></tbody>',Q=T(),M=d("div"),M.textContent="Query parameters",U=T(),y=d("table"),O=d("thead"),O.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',q=T(),k=d("tbody"),L=d("tr"),Y=d("td"),Y.textContent="expand",A=T(),X=d("td"),X.innerHTML='<span class="label">String</span>',E=T(),o=d("td"),$=R(`Auto expand record relations. Ex.:
|
||||
`),te(P.$$.fragment),z=R(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),u=d("br"),p=R(`
|
||||
The expanded relations will be appended to the record under the
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as kt,i as gt,s as vt,V as St,X as L,W as _t,h as c,d as ae,Y as wt,t as X,a as Z,I as z,Z as ct,_ as yt,C as $t,$ as Pt,D as Ct,l as d,n as t,m as oe,u as s,A as f,v as u,c as se,w as k,J as dt,p as Rt,k as ne,o as Ot}from"./index-ChG9Kpfq.js";import{F as Tt}from"./FieldsQueryParam-DkRoNbFY.js";function pt(i,o,a){const n=i.slice();return n[7]=o[a],n}function ut(i,o,a){const n=i.slice();return n[7]=o[a],n}function ht(i,o,a){const n=i.slice();return n[12]=o[a],n[14]=a,n}function At(i){let o;return{c(){o=f("or")},m(a,n){d(a,o,n)},d(a){a&&c(o)}}}function bt(i){let o,a,n=i[12]+"",m,b=i[14]>0&&At();return{c(){b&&b.c(),o=u(),a=s("strong"),m=f(n)},m(r,h){b&&b.m(r,h),d(r,o,h),d(r,a,h),t(a,m)},p(r,h){h&2&&n!==(n=r[12]+"")&&z(m,n)},d(r){r&&(c(o),c(a)),b&&b.d(r)}}}function ft(i,o){let a,n=o[7].code+"",m,b,r,h;function g(){return o[6](o[7])}return{key:i,first:null,c(){a=s("button"),m=f(n),b=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m($,_){d($,a,_),t(a,m),t(a,b),r||(h=Ot(a,"click",g),r=!0)},p($,_){o=$,_&8&&n!==(n=o[7].code+"")&&z(m,n),_&12&&ne(a,"active",o[2]===o[7].code)},d($){$&&c(a),r=!1,h()}}}function mt(i,o){let a,n,m,b;return n=new _t({props:{content:o[7].body}}),{key:i,first:null,c(){a=s("div"),se(n.$$.fragment),m=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m(r,h){d(r,a,h),oe(n,a,null),t(a,m),b=!0},p(r,h){o=r;const g={};h&8&&(g.content=o[7].body),n.$set(g),(!b||h&12)&&ne(a,"active",o[2]===o[7].code)},i(r){b||(Z(n.$$.fragment,r),b=!0)},o(r){X(n.$$.fragment,r),b=!1},d(r){r&&c(a),ae(n)}}}function Dt(i){var ot,st;let o,a,n=i[0].name+"",m,b,r,h,g,$,_,G=i[1].join("/")+"",ie,De,re,We,ce,C,de,q,pe,R,x,Fe,ee,H,Me,ue,te=i[0].name+"",he,Ue,be,Y,fe,O,me,Be,j,T,_e,Le,ke,qe,V,ge,He,ve,Se,E,we,A,ye,Ye,N,D,$e,je,Pe,Ve,v,Ee,M,Ne,Ie,Je,Ce,Qe,Re,Ke,Xe,Ze,Oe,ze,Ge,U,Te,I,Ae,W,J,P=[],xe=new Map,et,Q,w=[],tt=new Map,F;C=new St({props:{js:`
|
||||
import{S as kt,i as gt,s as vt,V as St,X as L,W as _t,h as c,d as ae,Y as wt,t as X,a as Z,I as z,Z as ct,_ as yt,C as $t,$ as Pt,D as Ct,l as d,n as t,m as oe,u as s,A as f,v as u,c as se,w as k,J as dt,p as Rt,k as ne,o as Ot}from"./index-rRnVk674.js";import{F as Tt}from"./FieldsQueryParam-BEgqkb0I.js";function pt(i,o,a){const n=i.slice();return n[7]=o[a],n}function ut(i,o,a){const n=i.slice();return n[7]=o[a],n}function ht(i,o,a){const n=i.slice();return n[12]=o[a],n[14]=a,n}function At(i){let o;return{c(){o=f("or")},m(a,n){d(a,o,n)},d(a){a&&c(o)}}}function bt(i){let o,a,n=i[12]+"",m,b=i[14]>0&&At();return{c(){b&&b.c(),o=u(),a=s("strong"),m=f(n)},m(r,h){b&&b.m(r,h),d(r,o,h),d(r,a,h),t(a,m)},p(r,h){h&2&&n!==(n=r[12]+"")&&z(m,n)},d(r){r&&(c(o),c(a)),b&&b.d(r)}}}function ft(i,o){let a,n=o[7].code+"",m,b,r,h;function g(){return o[6](o[7])}return{key:i,first:null,c(){a=s("button"),m=f(n),b=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m($,_){d($,a,_),t(a,m),t(a,b),r||(h=Ot(a,"click",g),r=!0)},p($,_){o=$,_&8&&n!==(n=o[7].code+"")&&z(m,n),_&12&&ne(a,"active",o[2]===o[7].code)},d($){$&&c(a),r=!1,h()}}}function mt(i,o){let a,n,m,b;return n=new _t({props:{content:o[7].body}}),{key:i,first:null,c(){a=s("div"),se(n.$$.fragment),m=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m(r,h){d(r,a,h),oe(n,a,null),t(a,m),b=!0},p(r,h){o=r;const g={};h&8&&(g.content=o[7].body),n.$set(g),(!b||h&12)&&ne(a,"active",o[2]===o[7].code)},i(r){b||(Z(n.$$.fragment,r),b=!0)},o(r){X(n.$$.fragment,r),b=!1},d(r){r&&c(a),ae(n)}}}function Dt(i){var ot,st;let o,a,n=i[0].name+"",m,b,r,h,g,$,_,G=i[1].join("/")+"",ie,De,re,We,ce,C,de,q,pe,R,x,Fe,ee,H,Me,ue,te=i[0].name+"",he,Ue,be,Y,fe,O,me,Be,j,T,_e,Le,ke,qe,V,ge,He,ve,Se,E,we,A,ye,Ye,N,D,$e,je,Pe,Ve,v,Ee,M,Ne,Ie,Je,Ce,Qe,Re,Ke,Xe,Ze,Oe,ze,Ge,U,Te,I,Ae,W,J,P=[],xe=new Map,et,Q,w=[],tt=new Map,F;C=new St({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i[5]}');
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as St,i as At,s as Lt,V as Mt,W as Ht,X as Q,h as d,d as Re,t as Y,a as x,I as jt,Z as Pt,_ as Nt,C as Ut,$ as Jt,D as zt,l as u,n as t,m as Te,E as Wt,G as Gt,u as o,A as _,v as i,c as Pe,w as b,J as Ft,p as Kt,k as ee,o as Vt}from"./index-ChG9Kpfq.js";function Bt(a,s,n){const c=a.slice();return c[6]=s[n],c}function Et(a,s,n){const c=a.slice();return c[6]=s[n],c}function Ot(a,s){let n,c,y;function f(){return s[5](s[6])}return{key:a,first:null,c(){n=o("button"),n.textContent=`${s[6].code} `,b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,h){u(r,n,h),c||(y=Vt(n,"click",f),c=!0)},p(r,h){s=r,h&10&&ee(n,"active",s[1]===s[6].code)},d(r){r&&d(n),c=!1,y()}}}function It(a,s){let n,c,y,f;return c=new Ht({props:{content:s[6].body}}),{key:a,first:null,c(){n=o("div"),Pe(c.$$.fragment),y=i(),b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,h){u(r,n,h),Te(c,n,null),t(n,y),f=!0},p(r,h){s=r,(!f||h&10)&&ee(n,"active",s[1]===s[6].code)},i(r){f||(x(c.$$.fragment,r),f=!0)},o(r){Y(c.$$.fragment,r),f=!1},d(r){r&&d(n),Re(c)}}}function Xt(a){var pt,mt,bt,ht,ft,_t,yt,kt;let s,n,c=a[0].name+"",y,f,r,h,F,g,U,Fe,P,B,Be,E,Ee,Oe,te,le,q,oe,O,ae,I,se,H,ne,J,ie,w,ce,Ie,re,S,z,He,k,W,Se,de,Ae,C,G,Le,ue,Me,K,je,pe,Ne,D,Ue,me,Je,ze,We,V,Ge,X,Ke,be,Ve,he,Xe,fe,Ze,p,_e,Qe,ye,Ye,ke,xe,$e,et,ge,tt,ve,lt,ot,at,Ce,st,R,De,A,qe,T,L,v=[],nt=new Map,it,M,$=[],ct=new Map,j,we,rt;q=new Mt({props:{js:`
|
||||
import{S as St,i as At,s as Lt,V as Mt,W as Ht,X as Q,h as d,d as Re,t as Y,a as x,I as jt,Z as Pt,_ as Nt,C as Ut,$ as Jt,D as zt,l as u,n as t,m as Te,E as Wt,G as Gt,u as o,A as _,v as i,c as Pe,w as b,J as Ft,p as Kt,k as ee,o as Vt}from"./index-rRnVk674.js";function Bt(a,s,n){const c=a.slice();return c[6]=s[n],c}function Et(a,s,n){const c=a.slice();return c[6]=s[n],c}function Ot(a,s){let n,c,y;function f(){return s[5](s[6])}return{key:a,first:null,c(){n=o("button"),n.textContent=`${s[6].code} `,b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,h){u(r,n,h),c||(y=Vt(n,"click",f),c=!0)},p(r,h){s=r,h&10&&ee(n,"active",s[1]===s[6].code)},d(r){r&&d(n),c=!1,y()}}}function It(a,s){let n,c,y,f;return c=new Ht({props:{content:s[6].body}}),{key:a,first:null,c(){n=o("div"),Pe(c.$$.fragment),y=i(),b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,h){u(r,n,h),Te(c,n,null),t(n,y),f=!0},p(r,h){s=r,(!f||h&10)&&ee(n,"active",s[1]===s[6].code)},i(r){f||(x(c.$$.fragment,r),f=!0)},o(r){Y(c.$$.fragment,r),f=!1},d(r){r&&d(n),Re(c)}}}function Xt(a){var pt,mt,bt,ht,ft,_t,yt,kt;let s,n,c=a[0].name+"",y,f,r,h,F,g,U,Fe,P,B,Be,E,Ee,Oe,te,le,q,oe,O,ae,I,se,H,ne,J,ie,w,ce,Ie,re,S,z,He,k,W,Se,de,Ae,C,G,Le,ue,Me,K,je,pe,Ne,D,Ue,me,Je,ze,We,V,Ge,X,Ke,be,Ve,he,Xe,fe,Ze,p,_e,Qe,ye,Ye,ke,xe,$e,et,ge,tt,ve,lt,ot,at,Ce,st,R,De,A,qe,T,L,v=[],nt=new Map,it,M,$=[],ct=new Map,j,we,rt;q=new Mt({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[2]}');
|
||||
+14
File diff suppressed because one or more lines are too long
-14
File diff suppressed because one or more lines are too long
+90
@@ -0,0 +1,90 @@
|
||||
import{S as $t,i as qt,s as Tt,V as St,X as ce,W as Ct,h as o,d as $e,t as he,a as ve,I as ae,Z as Ne,_ as pt,C as Mt,$ as Pt,D as Lt,l as r,n as i,m as qe,u as a,A as b,v as p,c as Te,w,J as we,p as Ft,k as Se,o as Ht,L as Ot,H as fe}from"./index-rRnVk674.js";import{F as Rt}from"./FieldsQueryParam-BEgqkb0I.js";function mt(s,e,t){const l=s.slice();return l[10]=e[t],l}function bt(s,e,t){const l=s.slice();return l[10]=e[t],l}function _t(s,e,t){const l=s.slice();return l[15]=e[t],l}function kt(s){let e;return{c(){e=a("p"),e.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",w(e,"class","txt-hint txt-sm txt-right")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function yt(s){let e,t,l,c,f,u,_,m,q,y,g,B,S,$,R,P,I,D,M,W,L,T,k,F,ee,z,U,oe,K,X,Y;function ue(h,C){var N,x,O;return C&1&&(u=null),u==null&&(u=!!((O=(x=(N=h[0])==null?void 0:N.fields)==null?void 0:x.find(Yt))!=null&&O.required)),u?Bt:At}let te=ue(s,-1),E=te(s);function Z(h,C){var N,x,O;return C&1&&(I=null),I==null&&(I=!!((O=(x=(N=h[0])==null?void 0:N.fields)==null?void 0:x.find(Xt))!=null&&O.required)),I?Nt:Vt}let G=Z(s,-1),H=G(s);return{c(){e=a("tr"),e.innerHTML='<td colspan="3" class="txt-hint txt-bold">Auth specific fields</td>',t=p(),l=a("tr"),c=a("td"),f=a("div"),E.c(),_=p(),m=a("span"),m.textContent="email",q=p(),y=a("td"),y.innerHTML='<span class="label">String</span>',g=p(),B=a("td"),B.textContent="Auth record email address.",S=p(),$=a("tr"),R=a("td"),P=a("div"),H.c(),D=p(),M=a("span"),M.textContent="emailVisibility",W=p(),L=a("td"),L.innerHTML='<span class="label">Boolean</span>',T=p(),k=a("td"),k.textContent="Whether to show/hide the auth record email when fetching the record data.",F=p(),ee=a("tr"),ee.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>',z=p(),U=a("tr"),U.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>',oe=p(),K=a("tr"),K.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>verified</span></div></td> <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 superusers or auth records with "Manage" access.</td>`,X=p(),Y=a("tr"),Y.innerHTML='<td colspan="3" class="txt-hint txt-bold">Other fields</td>',w(f,"class","inline-flex"),w(P,"class","inline-flex")},m(h,C){r(h,e,C),r(h,t,C),r(h,l,C),i(l,c),i(c,f),E.m(f,null),i(f,_),i(f,m),i(l,q),i(l,y),i(l,g),i(l,B),r(h,S,C),r(h,$,C),i($,R),i(R,P),H.m(P,null),i(P,D),i(P,M),i($,W),i($,L),i($,T),i($,k),r(h,F,C),r(h,ee,C),r(h,z,C),r(h,U,C),r(h,oe,C),r(h,K,C),r(h,X,C),r(h,Y,C)},p(h,C){te!==(te=ue(h,C))&&(E.d(1),E=te(h),E&&(E.c(),E.m(f,_))),G!==(G=Z(h,C))&&(H.d(1),H=G(h),H&&(H.c(),H.m(P,D)))},d(h){h&&(o(e),o(t),o(l),o(S),o($),o(F),o(ee),o(z),o(U),o(oe),o(K),o(X),o(Y)),E.d(),H.d()}}}function At(s){let e;return{c(){e=a("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function Bt(s){let e;return{c(){e=a("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function Vt(s){let e;return{c(){e=a("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function Nt(s){let e;return{c(){e=a("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function jt(s){let e;return{c(){e=a("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function Jt(s){let e;return{c(){e=a("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function Dt(s){let e,t=s[15].maxSelect===1?"id":"ids",l,c;return{c(){e=b("Relation record "),l=b(t),c=b(".")},m(f,u){r(f,e,u),r(f,l,u),r(f,c,u)},p(f,u){u&32&&t!==(t=f[15].maxSelect===1?"id":"ids")&&ae(l,t)},d(f){f&&(o(e),o(l),o(c))}}}function Et(s){let e,t,l,c,f,u,_,m,q;return{c(){e=b("File object."),t=a("br"),l=b(`
|
||||
Set to empty value (`),c=a("code"),c.textContent="null",f=b(", "),u=a("code"),u.textContent='""',_=b(" or "),m=a("code"),m.textContent="[]",q=b(`) to delete
|
||||
already uploaded file(s).`)},m(y,g){r(y,e,g),r(y,t,g),r(y,l,g),r(y,c,g),r(y,f,g),r(y,u,g),r(y,_,g),r(y,m,g),r(y,q,g)},p:fe,d(y){y&&(o(e),o(t),o(l),o(c),o(f),o(u),o(_),o(m),o(q))}}}function It(s){let e,t;return{c(){e=a("code"),e.textContent='{"lon":x,"lat":y}',t=b(" object.")},m(l,c){r(l,e,c),r(l,t,c)},p:fe,d(l){l&&(o(e),o(t))}}}function Ut(s){let e;return{c(){e=b("URL address.")},m(t,l){r(t,e,l)},p:fe,d(t){t&&o(e)}}}function Qt(s){let e;return{c(){e=b("Email address.")},m(t,l){r(t,e,l)},p:fe,d(t){t&&o(e)}}}function Wt(s){let e;return{c(){e=b("JSON array or object.")},m(t,l){r(t,e,l)},p:fe,d(t){t&&o(e)}}}function xt(s){let e;return{c(){e=b("Number value.")},m(t,l){r(t,e,l)},p:fe,d(t){t&&o(e)}}}function zt(s){let e,t,l=s[15].autogeneratePattern&&ht();return{c(){e=b(`Plain text value.
|
||||
`),l&&l.c(),t=Ot()},m(c,f){r(c,e,f),l&&l.m(c,f),r(c,t,f)},p(c,f){c[15].autogeneratePattern?l||(l=ht(),l.c(),l.m(t.parentNode,t)):l&&(l.d(1),l=null)},d(c){c&&(o(e),o(t)),l&&l.d(c)}}}function ht(s){let e;return{c(){e=b("It is autogenerated if not set.")},m(t,l){r(t,e,l)},d(t){t&&o(e)}}}function vt(s,e){let t,l,c,f,u,_=e[15].name+"",m,q,y,g,B=we.getFieldValueType(e[15])+"",S,$,R,P;function I(k,F){return!k[15].required||k[15].type=="text"&&k[15].autogeneratePattern?Jt:jt}let D=I(e),M=D(e);function W(k,F){if(k[15].type==="text")return zt;if(k[15].type==="number")return xt;if(k[15].type==="json")return Wt;if(k[15].type==="email")return Qt;if(k[15].type==="url")return Ut;if(k[15].type==="geoPoint")return It;if(k[15].type==="file")return Et;if(k[15].type==="relation")return Dt}let L=W(e),T=L&&L(e);return{key:s,first:null,c(){t=a("tr"),l=a("td"),c=a("div"),M.c(),f=p(),u=a("span"),m=b(_),q=p(),y=a("td"),g=a("span"),S=b(B),$=p(),R=a("td"),T&&T.c(),P=p(),w(c,"class","inline-flex"),w(g,"class","label"),this.first=t},m(k,F){r(k,t,F),i(t,l),i(l,c),M.m(c,null),i(c,f),i(c,u),i(u,m),i(t,q),i(t,y),i(y,g),i(g,S),i(t,$),i(t,R),T&&T.m(R,null),i(t,P)},p(k,F){e=k,D!==(D=I(e))&&(M.d(1),M=D(e),M&&(M.c(),M.m(c,f))),F&32&&_!==(_=e[15].name+"")&&ae(m,_),F&32&&B!==(B=we.getFieldValueType(e[15])+"")&&ae(S,B),L===(L=W(e))&&T?T.p(e,F):(T&&T.d(1),T=L&&L(e),T&&(T.c(),T.m(R,null)))},d(k){k&&o(t),M.d(),T&&T.d()}}}function wt(s,e){let t,l=e[10].code+"",c,f,u,_;function m(){return e[9](e[10])}return{key:s,first:null,c(){t=a("button"),c=b(l),f=p(),w(t,"class","tab-item"),Se(t,"active",e[2]===e[10].code),this.first=t},m(q,y){r(q,t,y),i(t,c),i(t,f),u||(_=Ht(t,"click",m),u=!0)},p(q,y){e=q,y&8&&l!==(l=e[10].code+"")&&ae(c,l),y&12&&Se(t,"active",e[2]===e[10].code)},d(q){q&&o(t),u=!1,_()}}}function gt(s,e){let t,l,c,f;return l=new Ct({props:{content:e[10].body}}),{key:s,first:null,c(){t=a("div"),Te(l.$$.fragment),c=p(),w(t,"class","tab-item"),Se(t,"active",e[2]===e[10].code),this.first=t},m(u,_){r(u,t,_),qe(l,t,null),i(t,c),f=!0},p(u,_){e=u;const m={};_&8&&(m.content=e[10].body),l.$set(m),(!f||_&12)&&Se(t,"active",e[2]===e[10].code)},i(u){f||(ve(l.$$.fragment,u),f=!0)},o(u){he(l.$$.fragment,u),f=!1},d(u){u&&o(t),$e(l)}}}function Kt(s){var st,at,ot,rt;let e,t,l=s[0].name+"",c,f,u,_,m,q,y,g=s[0].name+"",B,S,$,R,P,I,D,M,W,L,T,k,F,ee,z,U,oe,K,X=s[0].name+"",Y,ue,te,E,Z,G,H,h,C,N,x,O=[],je=new Map,Me,pe,Pe,le,Le,Je,me,ne,Fe,De,He,Ee,A,Ie,re,Ue,Qe,We,Oe,xe,Re,ze,Ke,Xe,Ae,Ye,Ze,de,Be,be,Ve,ie,_e,Q=[],Ge=new Map,et,ke,j=[],tt=new Map,se;M=new St({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${s[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(s[7](s[0]),null,4)};
|
||||
|
||||
const record = await pb.collection('${(st=s[0])==null?void 0:st.name}').create(data);
|
||||
`+(s[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(at=s[0])==null?void 0:at.name}').requestVerification('test@example.com');
|
||||
`:""),dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${s[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(s[7](s[0]),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ot=s[0])==null?void 0:ot.name}').create(body: body);
|
||||
`+(s[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(rt=s[0])==null?void 0:rt.name}').requestVerification('test@example.com');
|
||||
`:"")}});let J=s[6]&&kt(),V=s[1]&&yt(s),ge=ce(s[5]);const lt=n=>n[15].name;for(let n=0;n<ge.length;n+=1){let d=_t(s,ge,n),v=lt(d);je.set(v,O[n]=vt(v,d))}re=new Ct({props:{content:"?expand=relField1,relField2.subRelField"}}),de=new Rt({});let Ce=ce(s[3]);const nt=n=>n[10].code;for(let n=0;n<Ce.length;n+=1){let d=bt(s,Ce,n),v=nt(d);Ge.set(v,Q[n]=wt(v,d))}let ye=ce(s[3]);const it=n=>n[10].code;for(let n=0;n<ye.length;n+=1){let d=mt(s,ye,n),v=it(d);tt.set(v,j[n]=gt(v,d))}return{c(){e=a("h3"),t=b("Create ("),c=b(l),f=b(")"),u=p(),_=a("div"),m=a("p"),q=b("Create a new "),y=a("strong"),B=b(g),S=b(" record."),$=p(),R=a("p"),R.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,P=p(),I=a("p"),I.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>.`,D=p(),Te(M.$$.fragment),W=p(),L=a("h6"),L.textContent="API details",T=p(),k=a("div"),F=a("strong"),F.textContent="POST",ee=p(),z=a("div"),U=a("p"),oe=b("/api/collections/"),K=a("strong"),Y=b(X),ue=b("/records"),te=p(),J&&J.c(),E=p(),Z=a("div"),Z.textContent="Body Parameters",G=p(),H=a("table"),h=a("thead"),h.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',C=p(),N=a("tbody"),V&&V.c(),x=p();for(let n=0;n<O.length;n+=1)O[n].c();Me=p(),pe=a("div"),pe.textContent="Query parameters",Pe=p(),le=a("table"),Le=a("thead"),Le.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',Je=p(),me=a("tbody"),ne=a("tr"),Fe=a("td"),Fe.textContent="expand",De=p(),He=a("td"),He.innerHTML='<span class="label">String</span>',Ee=p(),A=a("td"),Ie=b(`Auto expand relations when returning the created record. Ex.:
|
||||
`),Te(re.$$.fragment),Ue=b(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Qe=a("br"),We=b(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),Oe=a("code"),Oe.textContent="expand",xe=b(" property (eg. "),Re=a("code"),Re.textContent='"expand": {"relField1": {...}, ...}',ze=b(`).
|
||||
`),Ke=a("br"),Xe=b(`
|
||||
Only the relations to which the request user has permissions to `),Ae=a("strong"),Ae.textContent="view",Ye=b(" will be expanded."),Ze=p(),Te(de.$$.fragment),Be=p(),be=a("div"),be.textContent="Responses",Ve=p(),ie=a("div"),_e=a("div");for(let n=0;n<Q.length;n+=1)Q[n].c();et=p(),ke=a("div");for(let n=0;n<j.length;n+=1)j[n].c();w(e,"class","m-b-sm"),w(_,"class","content txt-lg m-b-sm"),w(L,"class","m-b-xs"),w(F,"class","label label-primary"),w(z,"class","content"),w(k,"class","alert alert-success"),w(Z,"class","section-title"),w(H,"class","table-compact table-border m-b-base"),w(pe,"class","section-title"),w(le,"class","table-compact table-border m-b-base"),w(be,"class","section-title"),w(_e,"class","tabs-header compact combined left"),w(ke,"class","tabs-content"),w(ie,"class","tabs")},m(n,d){r(n,e,d),i(e,t),i(e,c),i(e,f),r(n,u,d),r(n,_,d),i(_,m),i(m,q),i(m,y),i(y,B),i(m,S),i(_,$),i(_,R),i(_,P),i(_,I),r(n,D,d),qe(M,n,d),r(n,W,d),r(n,L,d),r(n,T,d),r(n,k,d),i(k,F),i(k,ee),i(k,z),i(z,U),i(U,oe),i(U,K),i(K,Y),i(U,ue),i(k,te),J&&J.m(k,null),r(n,E,d),r(n,Z,d),r(n,G,d),r(n,H,d),i(H,h),i(H,C),i(H,N),V&&V.m(N,null),i(N,x);for(let v=0;v<O.length;v+=1)O[v]&&O[v].m(N,null);r(n,Me,d),r(n,pe,d),r(n,Pe,d),r(n,le,d),i(le,Le),i(le,Je),i(le,me),i(me,ne),i(ne,Fe),i(ne,De),i(ne,He),i(ne,Ee),i(ne,A),i(A,Ie),qe(re,A,null),i(A,Ue),i(A,Qe),i(A,We),i(A,Oe),i(A,xe),i(A,Re),i(A,ze),i(A,Ke),i(A,Xe),i(A,Ae),i(A,Ye),i(me,Ze),qe(de,me,null),r(n,Be,d),r(n,be,d),r(n,Ve,d),r(n,ie,d),i(ie,_e);for(let v=0;v<Q.length;v+=1)Q[v]&&Q[v].m(_e,null);i(ie,et),i(ie,ke);for(let v=0;v<j.length;v+=1)j[v]&&j[v].m(ke,null);se=!0},p(n,[d]){var dt,ct,ft,ut;(!se||d&1)&&l!==(l=n[0].name+"")&&ae(c,l),(!se||d&1)&&g!==(g=n[0].name+"")&&ae(B,g);const v={};d&19&&(v.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(n[7](n[0]),null,4)};
|
||||
|
||||
const record = await pb.collection('${(dt=n[0])==null?void 0:dt.name}').create(data);
|
||||
`+(n[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ct=n[0])==null?void 0:ct.name}').requestVerification('test@example.com');
|
||||
`:"")),d&19&&(v.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(n[7](n[0]),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ft=n[0])==null?void 0:ft.name}').create(body: body);
|
||||
`+(n[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ut=n[0])==null?void 0:ut.name}').requestVerification('test@example.com');
|
||||
`:"")),M.$set(v),(!se||d&1)&&X!==(X=n[0].name+"")&&ae(Y,X),n[6]?J||(J=kt(),J.c(),J.m(k,null)):J&&(J.d(1),J=null),n[1]?V?V.p(n,d):(V=yt(n),V.c(),V.m(N,x)):V&&(V.d(1),V=null),d&32&&(ge=ce(n[5]),O=Ne(O,d,lt,1,n,ge,je,N,pt,vt,null,_t)),d&12&&(Ce=ce(n[3]),Q=Ne(Q,d,nt,1,n,Ce,Ge,_e,pt,wt,null,bt)),d&12&&(ye=ce(n[3]),Mt(),j=Ne(j,d,it,1,n,ye,tt,ke,Pt,gt,null,mt),Lt())},i(n){if(!se){ve(M.$$.fragment,n),ve(re.$$.fragment,n),ve(de.$$.fragment,n);for(let d=0;d<ye.length;d+=1)ve(j[d]);se=!0}},o(n){he(M.$$.fragment,n),he(re.$$.fragment,n),he(de.$$.fragment,n);for(let d=0;d<j.length;d+=1)he(j[d]);se=!1},d(n){n&&(o(e),o(u),o(_),o(D),o(W),o(L),o(T),o(k),o(E),o(Z),o(G),o(H),o(Me),o(pe),o(Pe),o(le),o(Be),o(be),o(Ve),o(ie)),$e(M,n),J&&J.d(),V&&V.d();for(let d=0;d<O.length;d+=1)O[d].d();$e(re),$e(de);for(let d=0;d<Q.length;d+=1)Q[d].d();for(let d=0;d<j.length;d+=1)j[d].d()}}}const Xt=s=>s.name=="emailVisibility",Yt=s=>s.name=="email";function Zt(s,e,t){let l,c,f,u,_,{collection:m}=e,q=200,y=[];function g(S){let $=we.dummyCollectionSchemaData(S,!0);return l&&($.password="12345678",$.passwordConfirm="12345678",delete $.verified),$}const B=S=>t(2,q=S.code);return s.$$set=S=>{"collection"in S&&t(0,m=S.collection)},s.$$.update=()=>{var S,$,R;s.$$.dirty&1&&t(1,l=m.type==="auth"),s.$$.dirty&1&&t(6,c=(m==null?void 0:m.createRule)===null),s.$$.dirty&2&&t(8,f=l?["password","verified","email","emailVisibility"]:[]),s.$$.dirty&257&&t(5,u=((S=m==null?void 0:m.fields)==null?void 0:S.filter(P=>!P.hidden&&P.type!="autodate"&&!f.includes(P.name)))||[]),s.$$.dirty&1&&t(3,y=[{code:200,body:JSON.stringify(we.dummyCollectionRecord(m),null,2)},{code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Failed to create record.",
|
||||
"data": {
|
||||
"${(R=($=m==null?void 0:m.fields)==null?void 0:$[0])==null?void 0:R.name}": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"status": 403,
|
||||
"message": "You are not allowed to perform this request.",
|
||||
"data": {}
|
||||
}
|
||||
`}])},t(4,_=we.getApiExampleUrl(Ft.baseURL)),[m,l,q,y,_,u,c,g,f,B]}class tl extends $t{constructor(e){super(),qt(this,e,Zt,Kt,Tt,{collection:0})}}export{tl as default};
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
import{S as $t,i as qt,s as St,V as Tt,J as ee,X as ue,W as Ct,h as r,d as $e,t as ye,a as ve,I as oe,Z as Ve,_ as pt,C as Ot,$ as Mt,D as Lt,l as d,n as i,m as qe,u as a,A as _,v as p,c as Se,w,p as Pt,k as Te,o as Ft,L as Ht,H as we}from"./index-ChG9Kpfq.js";import{F as Rt}from"./FieldsQueryParam-DkRoNbFY.js";function mt(s,e,t){const l=s.slice();return l[10]=e[t],l}function bt(s,e,t){const l=s.slice();return l[10]=e[t],l}function _t(s,e,t){const l=s.slice();return l[15]=e[t],l}function kt(s){let e;return{c(){e=a("p"),e.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",w(e,"class","txt-hint txt-sm txt-right")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function ht(s){let e,t,l,u,c,f,b,m,$,h,g,B,T,O,R,M,I,J,S,W,L,q,k,P,te,K,U,re,X,Y,Z;function fe(y,C){var V,z,H;return C&1&&(f=null),f==null&&(f=!!((H=(z=(V=y[0])==null?void 0:V.fields)==null?void 0:z.find(Yt))!=null&&H.required)),f?Bt:At}let le=fe(s,-1),E=le(s);function G(y,C){var V,z,H;return C&1&&(I=null),I==null&&(I=!!((H=(z=(V=y[0])==null?void 0:V.fields)==null?void 0:z.find(Xt))!=null&&H.required)),I?Vt:jt}let x=G(s,-1),F=x(s);return{c(){e=a("tr"),e.innerHTML='<td colspan="3" class="txt-hint txt-bold">Auth specific fields</td>',t=p(),l=a("tr"),u=a("td"),c=a("div"),E.c(),b=p(),m=a("span"),m.textContent="email",$=p(),h=a("td"),h.innerHTML='<span class="label">String</span>',g=p(),B=a("td"),B.textContent="Auth record email address.",T=p(),O=a("tr"),R=a("td"),M=a("div"),F.c(),J=p(),S=a("span"),S.textContent="emailVisibility",W=p(),L=a("td"),L.innerHTML='<span class="label">Boolean</span>',q=p(),k=a("td"),k.textContent="Whether to show/hide the auth record email when fetching the record data.",P=p(),te=a("tr"),te.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>',K=p(),U=a("tr"),U.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>',re=p(),X=a("tr"),X.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>verified</span></div></td> <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 superusers or auth records with "Manage" access.</td>`,Y=p(),Z=a("tr"),Z.innerHTML='<td colspan="3" class="txt-hint txt-bold">Other fields</td>',w(c,"class","inline-flex"),w(M,"class","inline-flex")},m(y,C){d(y,e,C),d(y,t,C),d(y,l,C),i(l,u),i(u,c),E.m(c,null),i(c,b),i(c,m),i(l,$),i(l,h),i(l,g),i(l,B),d(y,T,C),d(y,O,C),i(O,R),i(R,M),F.m(M,null),i(M,J),i(M,S),i(O,W),i(O,L),i(O,q),i(O,k),d(y,P,C),d(y,te,C),d(y,K,C),d(y,U,C),d(y,re,C),d(y,X,C),d(y,Y,C),d(y,Z,C)},p(y,C){le!==(le=fe(y,C))&&(E.d(1),E=le(y),E&&(E.c(),E.m(c,b))),x!==(x=G(y,C))&&(F.d(1),F=x(y),F&&(F.c(),F.m(M,J)))},d(y){y&&(r(e),r(t),r(l),r(T),r(O),r(P),r(te),r(K),r(U),r(re),r(X),r(Y),r(Z)),E.d(),F.d()}}}function At(s){let e;return{c(){e=a("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function Bt(s){let e;return{c(){e=a("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function jt(s){let e;return{c(){e=a("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function Vt(s){let e;return{c(){e=a("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function Dt(s){let e;return{c(){e=a("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function Nt(s){let e;return{c(){e=a("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function Jt(s){let e,t=s[15].maxSelect===1?"id":"ids",l,u;return{c(){e=_("Relation record "),l=_(t),u=_(".")},m(c,f){d(c,e,f),d(c,l,f),d(c,u,f)},p(c,f){f&64&&t!==(t=c[15].maxSelect===1?"id":"ids")&&oe(l,t)},d(c){c&&(r(e),r(l),r(u))}}}function Et(s){let e,t,l,u,c,f,b,m,$;return{c(){e=_("File object."),t=a("br"),l=_(`
|
||||
Set to empty value (`),u=a("code"),u.textContent="null",c=_(", "),f=a("code"),f.textContent='""',b=_(" or "),m=a("code"),m.textContent="[]",$=_(`) to delete
|
||||
already uploaded file(s).`)},m(h,g){d(h,e,g),d(h,t,g),d(h,l,g),d(h,u,g),d(h,c,g),d(h,f,g),d(h,b,g),d(h,m,g),d(h,$,g)},p:we,d(h){h&&(r(e),r(t),r(l),r(u),r(c),r(f),r(b),r(m),r($))}}}function It(s){let e;return{c(){e=_("URL address.")},m(t,l){d(t,e,l)},p:we,d(t){t&&r(e)}}}function Ut(s){let e;return{c(){e=_("Email address.")},m(t,l){d(t,e,l)},p:we,d(t){t&&r(e)}}}function Qt(s){let e;return{c(){e=_("JSON array or object.")},m(t,l){d(t,e,l)},p:we,d(t){t&&r(e)}}}function Wt(s){let e;return{c(){e=_("Number value.")},m(t,l){d(t,e,l)},p:we,d(t){t&&r(e)}}}function zt(s){let e,t,l=s[15].autogeneratePattern&&yt();return{c(){e=_(`Plain text value.
|
||||
`),l&&l.c(),t=Ht()},m(u,c){d(u,e,c),l&&l.m(u,c),d(u,t,c)},p(u,c){u[15].autogeneratePattern?l||(l=yt(),l.c(),l.m(t.parentNode,t)):l&&(l.d(1),l=null)},d(u){u&&(r(e),r(t)),l&&l.d(u)}}}function yt(s){let e;return{c(){e=_("It is autogenerated if not set.")},m(t,l){d(t,e,l)},d(t){t&&r(e)}}}function vt(s,e){let t,l,u,c,f,b=e[15].name+"",m,$,h,g,B=ee.getFieldValueType(e[15])+"",T,O,R,M;function I(k,P){return!k[15].required||k[15].type=="text"&&k[15].autogeneratePattern?Nt:Dt}let J=I(e),S=J(e);function W(k,P){if(k[15].type==="text")return zt;if(k[15].type==="number")return Wt;if(k[15].type==="json")return Qt;if(k[15].type==="email")return Ut;if(k[15].type==="url")return It;if(k[15].type==="file")return Et;if(k[15].type==="relation")return Jt}let L=W(e),q=L&&L(e);return{key:s,first:null,c(){t=a("tr"),l=a("td"),u=a("div"),S.c(),c=p(),f=a("span"),m=_(b),$=p(),h=a("td"),g=a("span"),T=_(B),O=p(),R=a("td"),q&&q.c(),M=p(),w(u,"class","inline-flex"),w(g,"class","label"),this.first=t},m(k,P){d(k,t,P),i(t,l),i(l,u),S.m(u,null),i(u,c),i(u,f),i(f,m),i(t,$),i(t,h),i(h,g),i(g,T),i(t,O),i(t,R),q&&q.m(R,null),i(t,M)},p(k,P){e=k,J!==(J=I(e))&&(S.d(1),S=J(e),S&&(S.c(),S.m(u,c))),P&64&&b!==(b=e[15].name+"")&&oe(m,b),P&64&&B!==(B=ee.getFieldValueType(e[15])+"")&&oe(T,B),L===(L=W(e))&&q?q.p(e,P):(q&&q.d(1),q=L&&L(e),q&&(q.c(),q.m(R,null)))},d(k){k&&r(t),S.d(),q&&q.d()}}}function wt(s,e){let t,l=e[10].code+"",u,c,f,b;function m(){return e[9](e[10])}return{key:s,first:null,c(){t=a("button"),u=_(l),c=p(),w(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m($,h){d($,t,h),i(t,u),i(t,c),f||(b=Ft(t,"click",m),f=!0)},p($,h){e=$,h&8&&l!==(l=e[10].code+"")&&oe(u,l),h&12&&Te(t,"active",e[2]===e[10].code)},d($){$&&r(t),f=!1,b()}}}function gt(s,e){let t,l,u,c;return l=new Ct({props:{content:e[10].body}}),{key:s,first:null,c(){t=a("div"),Se(l.$$.fragment),u=p(),w(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m(f,b){d(f,t,b),qe(l,t,null),i(t,u),c=!0},p(f,b){e=f;const m={};b&8&&(m.content=e[10].body),l.$set(m),(!c||b&12)&&Te(t,"active",e[2]===e[10].code)},i(f){c||(ve(l.$$.fragment,f),c=!0)},o(f){ye(l.$$.fragment,f),c=!1},d(f){f&&r(t),$e(l)}}}function Kt(s){var st,at,ot,rt;let e,t,l=s[0].name+"",u,c,f,b,m,$,h,g=s[0].name+"",B,T,O,R,M,I,J,S,W,L,q,k,P,te,K,U,re,X,Y=s[0].name+"",Z,fe,le,E,G,x,F,y,C,V,z,H=[],De=new Map,Oe,pe,Me,ne,Le,Ne,me,ie,Pe,Je,Fe,Ee,A,Ie,de,Ue,Qe,We,He,ze,Re,Ke,Xe,Ye,Ae,Ze,Ge,ce,Be,be,je,se,_e,Q=[],xe=new Map,et,ke,D=[],tt=new Map,ae;S=new Tt({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${s[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(Object.assign({},s[4],ee.dummyCollectionSchemaData(s[0],!0)),null,4)};
|
||||
|
||||
const record = await pb.collection('${(st=s[0])==null?void 0:st.name}').create(data);
|
||||
`+(s[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(at=s[0])==null?void 0:at.name}').requestVerification('test@example.com');
|
||||
`:""),dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${s[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},s[4],ee.dummyCollectionSchemaData(s[0],!0)),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ot=s[0])==null?void 0:ot.name}').create(body: body);
|
||||
`+(s[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(rt=s[0])==null?void 0:rt.name}').requestVerification('test@example.com');
|
||||
`:"")}});let N=s[7]&&kt(),j=s[1]&&ht(s),ge=ue(s[6]);const lt=n=>n[15].name;for(let n=0;n<ge.length;n+=1){let o=_t(s,ge,n),v=lt(o);De.set(v,H[n]=vt(v,o))}de=new Ct({props:{content:"?expand=relField1,relField2.subRelField"}}),ce=new Rt({});let Ce=ue(s[3]);const nt=n=>n[10].code;for(let n=0;n<Ce.length;n+=1){let o=bt(s,Ce,n),v=nt(o);xe.set(v,Q[n]=wt(v,o))}let he=ue(s[3]);const it=n=>n[10].code;for(let n=0;n<he.length;n+=1){let o=mt(s,he,n),v=it(o);tt.set(v,D[n]=gt(v,o))}return{c(){e=a("h3"),t=_("Create ("),u=_(l),c=_(")"),f=p(),b=a("div"),m=a("p"),$=_("Create a new "),h=a("strong"),B=_(g),T=_(" record."),O=p(),R=a("p"),R.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,M=p(),I=a("p"),I.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>.`,J=p(),Se(S.$$.fragment),W=p(),L=a("h6"),L.textContent="API details",q=p(),k=a("div"),P=a("strong"),P.textContent="POST",te=p(),K=a("div"),U=a("p"),re=_("/api/collections/"),X=a("strong"),Z=_(Y),fe=_("/records"),le=p(),N&&N.c(),E=p(),G=a("div"),G.textContent="Body Parameters",x=p(),F=a("table"),y=a("thead"),y.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',C=p(),V=a("tbody"),j&&j.c(),z=p();for(let n=0;n<H.length;n+=1)H[n].c();Oe=p(),pe=a("div"),pe.textContent="Query parameters",Me=p(),ne=a("table"),Le=a("thead"),Le.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',Ne=p(),me=a("tbody"),ie=a("tr"),Pe=a("td"),Pe.textContent="expand",Je=p(),Fe=a("td"),Fe.innerHTML='<span class="label">String</span>',Ee=p(),A=a("td"),Ie=_(`Auto expand relations when returning the created record. Ex.:
|
||||
`),Se(de.$$.fragment),Ue=_(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Qe=a("br"),We=_(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),He=a("code"),He.textContent="expand",ze=_(" property (eg. "),Re=a("code"),Re.textContent='"expand": {"relField1": {...}, ...}',Ke=_(`).
|
||||
`),Xe=a("br"),Ye=_(`
|
||||
Only the relations to which the request user has permissions to `),Ae=a("strong"),Ae.textContent="view",Ze=_(" will be expanded."),Ge=p(),Se(ce.$$.fragment),Be=p(),be=a("div"),be.textContent="Responses",je=p(),se=a("div"),_e=a("div");for(let n=0;n<Q.length;n+=1)Q[n].c();et=p(),ke=a("div");for(let n=0;n<D.length;n+=1)D[n].c();w(e,"class","m-b-sm"),w(b,"class","content txt-lg m-b-sm"),w(L,"class","m-b-xs"),w(P,"class","label label-primary"),w(K,"class","content"),w(k,"class","alert alert-success"),w(G,"class","section-title"),w(F,"class","table-compact table-border m-b-base"),w(pe,"class","section-title"),w(ne,"class","table-compact table-border m-b-base"),w(be,"class","section-title"),w(_e,"class","tabs-header compact combined left"),w(ke,"class","tabs-content"),w(se,"class","tabs")},m(n,o){d(n,e,o),i(e,t),i(e,u),i(e,c),d(n,f,o),d(n,b,o),i(b,m),i(m,$),i(m,h),i(h,B),i(m,T),i(b,O),i(b,R),i(b,M),i(b,I),d(n,J,o),qe(S,n,o),d(n,W,o),d(n,L,o),d(n,q,o),d(n,k,o),i(k,P),i(k,te),i(k,K),i(K,U),i(U,re),i(U,X),i(X,Z),i(U,fe),i(k,le),N&&N.m(k,null),d(n,E,o),d(n,G,o),d(n,x,o),d(n,F,o),i(F,y),i(F,C),i(F,V),j&&j.m(V,null),i(V,z);for(let v=0;v<H.length;v+=1)H[v]&&H[v].m(V,null);d(n,Oe,o),d(n,pe,o),d(n,Me,o),d(n,ne,o),i(ne,Le),i(ne,Ne),i(ne,me),i(me,ie),i(ie,Pe),i(ie,Je),i(ie,Fe),i(ie,Ee),i(ie,A),i(A,Ie),qe(de,A,null),i(A,Ue),i(A,Qe),i(A,We),i(A,He),i(A,ze),i(A,Re),i(A,Ke),i(A,Xe),i(A,Ye),i(A,Ae),i(A,Ze),i(me,Ge),qe(ce,me,null),d(n,Be,o),d(n,be,o),d(n,je,o),d(n,se,o),i(se,_e);for(let v=0;v<Q.length;v+=1)Q[v]&&Q[v].m(_e,null);i(se,et),i(se,ke);for(let v=0;v<D.length;v+=1)D[v]&&D[v].m(ke,null);ae=!0},p(n,[o]){var dt,ct,ut,ft;(!ae||o&1)&&l!==(l=n[0].name+"")&&oe(u,l),(!ae||o&1)&&g!==(g=n[0].name+"")&&oe(B,g);const v={};o&51&&(v.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(Object.assign({},n[4],ee.dummyCollectionSchemaData(n[0],!0)),null,4)};
|
||||
|
||||
const record = await pb.collection('${(dt=n[0])==null?void 0:dt.name}').create(data);
|
||||
`+(n[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ct=n[0])==null?void 0:ct.name}').requestVerification('test@example.com');
|
||||
`:"")),o&51&&(v.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},n[4],ee.dummyCollectionSchemaData(n[0],!0)),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ut=n[0])==null?void 0:ut.name}').create(body: body);
|
||||
`+(n[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ft=n[0])==null?void 0:ft.name}').requestVerification('test@example.com');
|
||||
`:"")),S.$set(v),(!ae||o&1)&&Y!==(Y=n[0].name+"")&&oe(Z,Y),n[7]?N||(N=kt(),N.c(),N.m(k,null)):N&&(N.d(1),N=null),n[1]?j?j.p(n,o):(j=ht(n),j.c(),j.m(V,z)):j&&(j.d(1),j=null),o&64&&(ge=ue(n[6]),H=Ve(H,o,lt,1,n,ge,De,V,pt,vt,null,_t)),o&12&&(Ce=ue(n[3]),Q=Ve(Q,o,nt,1,n,Ce,xe,_e,pt,wt,null,bt)),o&12&&(he=ue(n[3]),Ot(),D=Ve(D,o,it,1,n,he,tt,ke,Mt,gt,null,mt),Lt())},i(n){if(!ae){ve(S.$$.fragment,n),ve(de.$$.fragment,n),ve(ce.$$.fragment,n);for(let o=0;o<he.length;o+=1)ve(D[o]);ae=!0}},o(n){ye(S.$$.fragment,n),ye(de.$$.fragment,n),ye(ce.$$.fragment,n);for(let o=0;o<D.length;o+=1)ye(D[o]);ae=!1},d(n){n&&(r(e),r(f),r(b),r(J),r(W),r(L),r(q),r(k),r(E),r(G),r(x),r(F),r(Oe),r(pe),r(Me),r(ne),r(Be),r(be),r(je),r(se)),$e(S,n),N&&N.d(),j&&j.d();for(let o=0;o<H.length;o+=1)H[o].d();$e(de),$e(ce);for(let o=0;o<Q.length;o+=1)Q[o].d();for(let o=0;o<D.length;o+=1)D[o].d()}}}const Xt=s=>s.name=="emailVisibility",Yt=s=>s.name=="email";function Zt(s,e,t){let l,u,c,f,b,{collection:m}=e,$=200,h=[],g={};const B=T=>t(2,$=T.code);return s.$$set=T=>{"collection"in T&&t(0,m=T.collection)},s.$$.update=()=>{var T,O,R;s.$$.dirty&1&&t(1,l=m.type==="auth"),s.$$.dirty&1&&t(7,u=(m==null?void 0:m.createRule)===null),s.$$.dirty&2&&t(8,c=l?["password","verified","email","emailVisibility"]:[]),s.$$.dirty&257&&t(6,f=((T=m==null?void 0:m.fields)==null?void 0:T.filter(M=>!M.hidden&&M.type!="autodate"&&!c.includes(M.name)))||[]),s.$$.dirty&1&&t(3,h=[{code:200,body:JSON.stringify(ee.dummyCollectionRecord(m),null,2)},{code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Failed to create record.",
|
||||
"data": {
|
||||
"${(R=(O=m==null?void 0:m.fields)==null?void 0:O[0])==null?void 0:R.name}": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"status": 403,
|
||||
"message": "You are not allowed to perform this request.",
|
||||
"data": {}
|
||||
}
|
||||
`}]),s.$$.dirty&2&&(l?t(4,g={password:"12345678",passwordConfirm:"12345678"}):t(4,g={}))},t(5,b=ee.getApiExampleUrl(Pt.baseURL)),[m,l,$,h,g,b,f,u,c,B]}class el extends $t{constructor(e){super(),qt(this,e,Zt,Kt,St,{collection:0})}}export{el as default};
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as Re,i as Ee,s as Pe,V as Te,X as j,h as p,d as De,t as te,a as le,I as ee,Z as he,_ as Be,C as Ie,$ as Oe,D as Ae,l as f,n as i,m as Ce,u as c,A as $,v as k,c as we,w as m,J as Me,p as qe,k as z,o as Le,W as Se}from"./index-ChG9Kpfq.js";function ke(a,l,s){const n=a.slice();return n[6]=l[s],n}function ge(a,l,s){const n=a.slice();return n[6]=l[s],n}function ve(a){let l;return{c(){l=c("p"),l.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,n){f(s,l,n)},d(s){s&&p(l)}}}function $e(a,l){let s,n,h;function r(){return l[5](l[6])}return{key:a,first:null,c(){s=c("button"),s.textContent=`${l[6].code} `,m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(o,d){f(o,s,d),n||(h=Le(s,"click",r),n=!0)},p(o,d){l=o,d&20&&z(s,"active",l[2]===l[6].code)},d(o){o&&p(s),n=!1,h()}}}function ye(a,l){let s,n,h,r;return n=new Se({props:{content:l[6].body}}),{key:a,first:null,c(){s=c("div"),we(n.$$.fragment),h=k(),m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(o,d){f(o,s,d),Ce(n,s,null),i(s,h),r=!0},p(o,d){l=o,(!r||d&20)&&z(s,"active",l[2]===l[6].code)},i(o){r||(le(n.$$.fragment,o),r=!0)},o(o){te(n.$$.fragment,o),r=!1},d(o){o&&p(s),De(n)}}}function He(a){var fe,me;let l,s,n=a[0].name+"",h,r,o,d,y,D,F,q=a[0].name+"",J,se,K,C,N,P,V,g,L,ae,S,E,ne,W,H=a[0].name+"",X,oe,Z,ie,G,T,Q,B,Y,I,x,w,O,v=[],ce=new Map,re,A,b=[],de=new Map,R;C=new Te({props:{js:`
|
||||
import{S as Re,i as Ee,s as Pe,V as Te,X as j,h as p,d as De,t as te,a as le,I as ee,Z as he,_ as Be,C as Ie,$ as Oe,D as Ae,l as f,n as i,m as Ce,u as c,A as $,v as k,c as we,w as m,J as Me,p as qe,k as z,o as Le,W as Se}from"./index-rRnVk674.js";function ke(a,l,s){const n=a.slice();return n[6]=l[s],n}function ge(a,l,s){const n=a.slice();return n[6]=l[s],n}function ve(a){let l;return{c(){l=c("p"),l.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,n){f(s,l,n)},d(s){s&&p(l)}}}function $e(a,l){let s,n,h;function r(){return l[5](l[6])}return{key:a,first:null,c(){s=c("button"),s.textContent=`${l[6].code} `,m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(o,d){f(o,s,d),n||(h=Le(s,"click",r),n=!0)},p(o,d){l=o,d&20&&z(s,"active",l[2]===l[6].code)},d(o){o&&p(s),n=!1,h()}}}function ye(a,l){let s,n,h,r;return n=new Se({props:{content:l[6].body}}),{key:a,first:null,c(){s=c("div"),we(n.$$.fragment),h=k(),m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(o,d){f(o,s,d),Ce(n,s,null),i(s,h),r=!0},p(o,d){l=o,(!r||d&20)&&z(s,"active",l[2]===l[6].code)},i(o){r||(le(n.$$.fragment,o),r=!0)},o(o){te(n.$$.fragment,o),r=!1},d(o){o&&p(s),De(n)}}}function He(a){var fe,me;let l,s,n=a[0].name+"",h,r,o,d,y,D,F,q=a[0].name+"",J,se,K,C,N,P,V,g,L,ae,S,E,ne,W,H=a[0].name+"",X,oe,Z,ie,G,T,Q,B,Y,I,x,w,O,v=[],ce=new Map,re,A,b=[],de=new Map,R;C=new Te({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[3]}');
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as se,i as oe,s as ie,X as K,h as g,t as X,a as V,I as F,Z as le,_ as Re,C as ne,$ as Se,D as ae,l as v,n as u,u as p,v as y,A as U,w as b,k as Y,o as ce,W as Oe,d as x,m as ee,c as te,V as Me,Y as _e,J as Be,p as De,a0 as be}from"./index-ChG9Kpfq.js";function ge(n,e,t){const l=n.slice();return l[4]=e[t],l}function ve(n,e,t){const l=n.slice();return l[4]=e[t],l}function ke(n,e){let t,l=e[4].code+"",d,i,r,a;function m(){return e[3](e[4])}return{key:n,first:null,c(){t=p("button"),d=U(l),i=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(k,q){v(k,t,q),u(t,d),u(t,i),r||(a=ce(t,"click",m),r=!0)},p(k,q){e=k,q&4&&l!==(l=e[4].code+"")&&F(d,l),q&6&&Y(t,"active",e[1]===e[4].code)},d(k){k&&g(t),r=!1,a()}}}function $e(n,e){let t,l,d,i;return l=new Oe({props:{content:e[4].body}}),{key:n,first:null,c(){t=p("div"),te(l.$$.fragment),d=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(r,a){v(r,t,a),ee(l,t,null),u(t,d),i=!0},p(r,a){e=r;const m={};a&4&&(m.content=e[4].body),l.$set(m),(!i||a&6)&&Y(t,"active",e[1]===e[4].code)},i(r){i||(V(l.$$.fragment,r),i=!0)},o(r){X(l.$$.fragment,r),i=!1},d(r){r&&g(t),x(l)}}}function Ne(n){let e,t,l,d,i,r,a,m=n[0].name+"",k,q,G,H,J,L,z,B,D,S,N,A=[],O=new Map,P,j,T=[],W=new Map,w,E=K(n[2]);const M=c=>c[4].code;for(let c=0;c<E.length;c+=1){let f=ve(n,E,c),s=M(f);O.set(s,A[c]=ke(s,f))}let _=K(n[2]);const Z=c=>c[4].code;for(let c=0;c<_.length;c+=1){let f=ge(n,_,c),s=Z(f);W.set(s,T[c]=$e(s,f))}return{c(){e=p("div"),t=p("strong"),t.textContent="POST",l=y(),d=p("div"),i=p("p"),r=U("/api/collections/"),a=p("strong"),k=U(m),q=U("/confirm-email-change"),G=y(),H=p("div"),H.textContent="Body Parameters",J=y(),L=p("table"),L.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 change email request email.</td></tr> <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>',z=y(),B=p("div"),B.textContent="Responses",D=y(),S=p("div"),N=p("div");for(let c=0;c<A.length;c+=1)A[c].c();P=y(),j=p("div");for(let c=0;c<T.length;c+=1)T[c].c();b(t,"class","label label-primary"),b(d,"class","content"),b(e,"class","alert alert-success"),b(H,"class","section-title"),b(L,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(N,"class","tabs-header compact combined left"),b(j,"class","tabs-content"),b(S,"class","tabs")},m(c,f){v(c,e,f),u(e,t),u(e,l),u(e,d),u(d,i),u(i,r),u(i,a),u(a,k),u(i,q),v(c,G,f),v(c,H,f),v(c,J,f),v(c,L,f),v(c,z,f),v(c,B,f),v(c,D,f),v(c,S,f),u(S,N);for(let s=0;s<A.length;s+=1)A[s]&&A[s].m(N,null);u(S,P),u(S,j);for(let s=0;s<T.length;s+=1)T[s]&&T[s].m(j,null);w=!0},p(c,[f]){(!w||f&1)&&m!==(m=c[0].name+"")&&F(k,m),f&6&&(E=K(c[2]),A=le(A,f,M,1,c,E,O,N,Re,ke,null,ve)),f&6&&(_=K(c[2]),ne(),T=le(T,f,Z,1,c,_,W,j,Se,$e,null,ge),ae())},i(c){if(!w){for(let f=0;f<_.length;f+=1)V(T[f]);w=!0}},o(c){for(let f=0;f<T.length;f+=1)X(T[f]);w=!1},d(c){c&&(g(e),g(G),g(H),g(J),g(L),g(z),g(B),g(D),g(S));for(let f=0;f<A.length;f+=1)A[f].d();for(let f=0;f<T.length;f+=1)T[f].d()}}}function We(n,e,t){let{collection:l}=e,d=204,i=[];const r=a=>t(1,d=a.code);return n.$$set=a=>{"collection"in a&&t(0,l=a.collection)},t(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
import{S as se,i as oe,s as ie,X as K,h as g,t as X,a as V,I as F,Z as le,_ as Re,C as ne,$ as Se,D as ae,l as v,n as u,u as p,v as y,A as U,w as b,k as Y,o as ce,W as Oe,d as x,m as ee,c as te,V as Me,Y as _e,J as Be,p as De,a0 as be}from"./index-rRnVk674.js";function ge(n,e,t){const l=n.slice();return l[4]=e[t],l}function ve(n,e,t){const l=n.slice();return l[4]=e[t],l}function ke(n,e){let t,l=e[4].code+"",d,i,r,a;function m(){return e[3](e[4])}return{key:n,first:null,c(){t=p("button"),d=U(l),i=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(k,q){v(k,t,q),u(t,d),u(t,i),r||(a=ce(t,"click",m),r=!0)},p(k,q){e=k,q&4&&l!==(l=e[4].code+"")&&F(d,l),q&6&&Y(t,"active",e[1]===e[4].code)},d(k){k&&g(t),r=!1,a()}}}function $e(n,e){let t,l,d,i;return l=new Oe({props:{content:e[4].body}}),{key:n,first:null,c(){t=p("div"),te(l.$$.fragment),d=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(r,a){v(r,t,a),ee(l,t,null),u(t,d),i=!0},p(r,a){e=r;const m={};a&4&&(m.content=e[4].body),l.$set(m),(!i||a&6)&&Y(t,"active",e[1]===e[4].code)},i(r){i||(V(l.$$.fragment,r),i=!0)},o(r){X(l.$$.fragment,r),i=!1},d(r){r&&g(t),x(l)}}}function Ne(n){let e,t,l,d,i,r,a,m=n[0].name+"",k,q,G,H,J,L,z,B,D,S,N,A=[],O=new Map,P,j,T=[],W=new Map,w,E=K(n[2]);const M=c=>c[4].code;for(let c=0;c<E.length;c+=1){let f=ve(n,E,c),s=M(f);O.set(s,A[c]=ke(s,f))}let _=K(n[2]);const Z=c=>c[4].code;for(let c=0;c<_.length;c+=1){let f=ge(n,_,c),s=Z(f);W.set(s,T[c]=$e(s,f))}return{c(){e=p("div"),t=p("strong"),t.textContent="POST",l=y(),d=p("div"),i=p("p"),r=U("/api/collections/"),a=p("strong"),k=U(m),q=U("/confirm-email-change"),G=y(),H=p("div"),H.textContent="Body Parameters",J=y(),L=p("table"),L.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 change email request email.</td></tr> <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>',z=y(),B=p("div"),B.textContent="Responses",D=y(),S=p("div"),N=p("div");for(let c=0;c<A.length;c+=1)A[c].c();P=y(),j=p("div");for(let c=0;c<T.length;c+=1)T[c].c();b(t,"class","label label-primary"),b(d,"class","content"),b(e,"class","alert alert-success"),b(H,"class","section-title"),b(L,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(N,"class","tabs-header compact combined left"),b(j,"class","tabs-content"),b(S,"class","tabs")},m(c,f){v(c,e,f),u(e,t),u(e,l),u(e,d),u(d,i),u(i,r),u(i,a),u(a,k),u(i,q),v(c,G,f),v(c,H,f),v(c,J,f),v(c,L,f),v(c,z,f),v(c,B,f),v(c,D,f),v(c,S,f),u(S,N);for(let s=0;s<A.length;s+=1)A[s]&&A[s].m(N,null);u(S,P),u(S,j);for(let s=0;s<T.length;s+=1)T[s]&&T[s].m(j,null);w=!0},p(c,[f]){(!w||f&1)&&m!==(m=c[0].name+"")&&F(k,m),f&6&&(E=K(c[2]),A=le(A,f,M,1,c,E,O,N,Re,ke,null,ve)),f&6&&(_=K(c[2]),ne(),T=le(T,f,Z,1,c,_,W,j,Se,$e,null,ge),ae())},i(c){if(!w){for(let f=0;f<_.length;f+=1)V(T[f]);w=!0}},o(c){for(let f=0;f<T.length;f+=1)X(T[f]);w=!1},d(c){c&&(g(e),g(G),g(H),g(J),g(L),g(z),g(B),g(D),g(S));for(let f=0;f<A.length;f+=1)A[f].d();for(let f=0;f<T.length;f+=1)T[f].d()}}}function We(n,e,t){let{collection:l}=e,d=204,i=[];const r=a=>t(1,d=a.code);return n.$$set=a=>{"collection"in a&&t(0,l=a.collection)},t(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as J,i as N,s as O,W as P,h as Q,d as R,t as W,a as j,I as z,l as D,n as e,m as G,u as t,v as c,A as i,c as K,w as U}from"./index-ChG9Kpfq.js";function V(f){let n,o,u,d,k,s,p,w,g,y,r,F,_,S,b,E,C,a,$,L,q,H,I,M,m,T,v,A,x;return r=new P({props:{content:"?fields=*,"+f[0]+"expand.relField.name"}}),{c(){n=t("tr"),o=t("td"),o.textContent="fields",u=c(),d=t("td"),d.innerHTML='<span class="label">String</span>',k=c(),s=t("td"),p=t("p"),w=i(`Comma separated string of the fields to return in the JSON response
|
||||
import{S as J,i as N,s as O,W as P,h as Q,d as R,t as W,a as j,I as z,l as D,n as e,m as G,u as t,v as c,A as i,c as K,w as U}from"./index-rRnVk674.js";function V(f){let n,o,u,d,k,s,p,w,g,y,r,F,_,S,b,E,C,a,$,L,q,H,I,M,m,T,v,A,x;return r=new P({props:{content:"?fields=*,"+f[0]+"expand.relField.name"}}),{c(){n=t("tr"),o=t("td"),o.textContent="fields",u=c(),d=t("td"),d.innerHTML='<span class="label">String</span>',k=c(),s=t("td"),p=t("p"),w=i(`Comma separated string of the fields to return in the JSON response
|
||||
`),g=t("em"),g.textContent="(by default returns all fields)",y=i(`. Ex.:
|
||||
`),K(r.$$.fragment),F=c(),_=t("p"),_.innerHTML="<code>*</code> targets all keys from the specific depth level.",S=c(),b=t("p"),b.textContent="In addition, the following field modifiers are also supported:",E=c(),C=t("ul"),a=t("li"),$=t("code"),$.textContent=":excerpt(maxLength, withEllipsis?)",L=c(),q=t("br"),H=i(`
|
||||
Returns a short plain text version of the field string value.
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+4
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as el,i as ll,s as sl,H as ze,h as m,l as h,o as nl,u as e,v as s,L as ol,w as a,n as t,A as g,V as al,W as Le,X as ae,d as Kt,Y as il,t as Ct,a as kt,I as ve,Z as Je,_ as rl,C as cl,$ as dl,D as pl,m as Qt,c as Vt,J as Te,p as fl,k as Ae}from"./index-ChG9Kpfq.js";import{F as ul}from"./FieldsQueryParam-DkRoNbFY.js";function ml(r){let n,o,i;return{c(){n=e("span"),n.textContent="Show details",o=s(),i=e("i"),a(n,"class","txt"),a(i,"class","ri-arrow-down-s-line")},m(f,b){h(f,n,b),h(f,o,b),h(f,i,b)},d(f){f&&(m(n),m(o),m(i))}}}function hl(r){let n,o,i;return{c(){n=e("span"),n.textContent="Hide details",o=s(),i=e("i"),a(n,"class","txt"),a(i,"class","ri-arrow-up-s-line")},m(f,b){h(f,n,b),h(f,o,b),h(f,i,b)},d(f){f&&(m(n),m(o),m(i))}}}function Ke(r){let n,o,i,f,b,p,u,C,_,x,d,Y,yt,Wt,E,Xt,D,it,P,Z,ie,j,U,re,rt,vt,tt,Ft,ce,ct,dt,et,N,Yt,Lt,k,lt,At,Zt,Tt,z,st,Pt,te,Rt,v,pt,Ot,de,ft,pe,H,St,nt,Et,F,ut,fe,J,Nt,ee,qt,le,Dt,ue,L,mt,me,ht,he,M,be,T,Ht,ot,Mt,K,bt,ge,I,It,y,Bt,at,Gt,_e,Q,gt,we,_t,xe,jt,$e,B,Ut,Ce,G,ke,wt,se,R,xt,V,W,O,zt,ne,X;return{c(){n=e("p"),n.innerHTML=`The syntax basically follows the format
|
||||
import{S as el,i as ll,s as sl,H as ze,h as m,l as h,o as nl,u as e,v as s,L as ol,w as a,n as t,A as g,V as al,W as Le,X as ae,d as Kt,Y as il,t as Ct,a as kt,I as ve,Z as Je,_ as rl,C as cl,$ as dl,D as pl,m as Qt,c as Vt,J as Te,p as fl,k as Ae}from"./index-rRnVk674.js";import{F as ul}from"./FieldsQueryParam-BEgqkb0I.js";function ml(r){let n,o,i;return{c(){n=e("span"),n.textContent="Show details",o=s(),i=e("i"),a(n,"class","txt"),a(i,"class","ri-arrow-down-s-line")},m(f,b){h(f,n,b),h(f,o,b),h(f,i,b)},d(f){f&&(m(n),m(o),m(i))}}}function hl(r){let n,o,i;return{c(){n=e("span"),n.textContent="Hide details",o=s(),i=e("i"),a(n,"class","txt"),a(i,"class","ri-arrow-up-s-line")},m(f,b){h(f,n,b),h(f,o,b),h(f,i,b)},d(f){f&&(m(n),m(o),m(i))}}}function Ke(r){let n,o,i,f,b,p,u,C,_,x,d,Y,yt,Wt,E,Xt,D,it,P,Z,ie,j,U,re,rt,vt,tt,Ft,ce,ct,dt,et,N,Yt,Lt,k,lt,At,Zt,Tt,z,st,Pt,te,Rt,v,pt,Ot,de,ft,pe,H,St,nt,Et,F,ut,fe,J,Nt,ee,qt,le,Dt,ue,L,mt,me,ht,he,M,be,T,Ht,ot,Mt,K,bt,ge,I,It,y,Bt,at,Gt,_e,Q,gt,we,_t,xe,jt,$e,B,Ut,Ce,G,ke,wt,se,R,xt,V,W,O,zt,ne,X;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:`,o=s(),i=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`,b=s(),p=e("li"),u=e("code"),u.textContent="OPERATOR",C=g(` - is one of:
|
||||
`),_=e("br"),x=s(),d=e("ul"),Y=e("li"),yt=e("code"),yt.textContent="=",Wt=s(),E=e("span"),E.textContent="Equal",Xt=s(),D=e("li"),it=e("code"),it.textContent="!=",P=s(),Z=e("span"),Z.textContent="NOT equal",ie=s(),j=e("li"),U=e("code"),U.textContent=">",re=s(),rt=e("span"),rt.textContent="Greater than",vt=s(),tt=e("li"),Ft=e("code"),Ft.textContent=">=",ce=s(),ct=e("span"),ct.textContent="Greater than or equal",dt=s(),et=e("li"),N=e("code"),N.textContent="<",Yt=s(),Lt=e("span"),Lt.textContent="Less than",k=s(),lt=e("li"),At=e("code"),At.textContent="<=",Zt=s(),Tt=e("span"),Tt.textContent="Less than or equal",z=s(),st=e("li"),Pt=e("code"),Pt.textContent="~",te=s(),Rt=e("span"),Rt.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
import{S as r,i as c,s as l,H as n,h as u,l as h,u as p,w as d,O as f,P as m,Q as g,R as o}from"./index-ChG9Kpfq.js";function _(s){let t;return{c(){t=p("div"),t.innerHTML='<h3 class="m-b-sm">Auth failed.</h3> <h5>You can close this window and go back to the app to try again.</h5>',d(t,"class","content txt-hint txt-center p-base")},m(e,a){h(e,t,a)},p:n,i:n,o:n,d(e){e&&u(t)}}}function b(s,t,e){let a;return f(s,o,i=>e(0,a=i)),m(o,a="OAuth2 auth failed",a),g(()=>{window.close()}),[]}class x extends r{constructor(t){super(),c(this,t,b,_,l,{})}}export{x as default};
|
||||
import{S as r,i as c,s as l,H as n,h as u,l as h,u as p,w as d,O as f,P as m,Q as g,R as o}from"./index-rRnVk674.js";function _(s){let t;return{c(){t=p("div"),t.innerHTML='<h3 class="m-b-sm">Auth failed.</h3> <h5>You can close this window and go back to the app to try again.</h5>',d(t,"class","content txt-hint txt-center p-base")},m(e,a){h(e,t,a)},p:n,i:n,o:n,d(e){e&&u(t)}}}function b(s,t,e){let a;return f(s,o,i=>e(0,a=i)),m(o,a="OAuth2 auth failed",a),g(()=>{window.close()}),[]}class x extends r{constructor(t){super(),c(this,t,b,_,l,{})}}export{x as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{S as i,i as r,s as u,H as n,h as l,l as p,u as h,w as d,O as m,P as f,Q as _,R as o}from"./index-ChG9Kpfq.js";function b(a){let t;return{c(){t=h("div"),t.innerHTML='<h3 class="m-b-sm">Auth completed.</h3> <h5>You can close this window and go back to the app.</h5>',d(t,"class","content txt-hint txt-center p-base")},m(e,s){p(e,t,s)},p:n,i:n,o:n,d(e){e&&l(t)}}}function g(a,t,e){let s;return m(a,o,c=>e(0,s=c)),f(o,s="OAuth2 auth completed",s),_(()=>{window.close()}),[]}class x extends i{constructor(t){super(),r(this,t,g,b,u,{})}}export{x as default};
|
||||
import{S as i,i as r,s as u,H as n,h as l,l as p,u as h,w as d,O as m,P as f,Q as _,R as o}from"./index-rRnVk674.js";function b(a){let t;return{c(){t=h("div"),t.innerHTML='<h3 class="m-b-sm">Auth completed.</h3> <h5>You can close this window and go back to the app.</h5>',d(t,"class","content txt-hint txt-center p-base")},m(e,s){p(e,t,s)},p:n,i:n,o:n,d(e){e&&l(t)}}}function g(a,t,e){let s;return m(a,o,c=>e(0,s=c)),f(o,s="OAuth2 auth completed",s),_(()=>{window.close()}),[]}class x extends i{constructor(t){super(),r(this,t,g,b,u,{})}}export{x as default};
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
import{S as J,i as M,s as z,F as A,d as L,t as h,a as v,m as S,c as I,J as D,h as _,D as N,l as b,L as R,M as W,g as Y,p as j,H as P,o as q,u as m,v as y,w as p,f as B,k as T,n as g,q as G,A as C,I as K,z as F,C as O}from"./index-ChG9Kpfq.js";function Q(i){let e,t,n,l,s,o,f,a,r,u,k,$,d=i[3]&&H(i);return o=new B({props:{class:"form-field required",name:"password",$$slots:{default:[V,({uniqueId:c})=>({8:c}),({uniqueId:c})=>c?256:0]},$$scope:{ctx:i}}}),{c(){e=m("form"),t=m("div"),n=m("h5"),l=C(`Type your password to confirm changing your email address
|
||||
import{S as J,i as M,s as z,F as A,d as L,t as h,a as v,m as S,c as I,J as D,h as _,D as N,l as b,L as R,M as W,g as Y,p as j,H as P,o as q,u as m,v as y,w as p,f as B,k as T,n as g,q as G,A as C,I as K,z as F,C as O}from"./index-rRnVk674.js";function Q(i){let e,t,n,l,s,o,f,a,r,u,k,$,d=i[3]&&H(i);return o=new B({props:{class:"form-field required",name:"password",$$slots:{default:[V,({uniqueId:c})=>({8:c}),({uniqueId:c})=>c?256:0]},$$scope:{ctx:i}}}),{c(){e=m("form"),t=m("div"),n=m("h5"),l=C(`Type your password to confirm changing your email address
|
||||
`),d&&d.c(),s=y(),I(o.$$.fragment),f=y(),a=m("button"),r=m("span"),r.textContent="Confirm new email",p(t,"class","content txt-center m-b-base"),p(r,"class","txt"),p(a,"type","submit"),p(a,"class","btn btn-lg btn-block"),a.disabled=i[1],T(a,"btn-loading",i[1])},m(c,w){b(c,e,w),g(e,t),g(t,n),g(n,l),d&&d.m(n,null),g(e,s),S(o,e,null),g(e,f),g(e,a),g(a,r),u=!0,k||($=q(e,"submit",G(i[4])),k=!0)},p(c,w){c[3]?d?d.p(c,w):(d=H(c),d.c(),d.m(n,null)):d&&(d.d(1),d=null);const E={};w&769&&(E.$$scope={dirty:w,ctx:c}),o.$set(E),(!u||w&2)&&(a.disabled=c[1]),(!u||w&2)&&T(a,"btn-loading",c[1])},i(c){u||(v(o.$$.fragment,c),u=!0)},o(c){h(o.$$.fragment,c),u=!1},d(c){c&&_(e),d&&d.d(),L(o),k=!1,$()}}}function U(i){let e,t,n,l,s;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=y(),n=m("button"),n.textContent="Close",p(e,"class","alert alert-success"),p(n,"type","button"),p(n,"class","btn btn-transparent btn-block")},m(o,f){b(o,e,f),b(o,t,f),b(o,n,f),l||(s=q(n,"click",i[6]),l=!0)},p:P,i:P,o:P,d(o){o&&(_(e),_(t),_(n)),l=!1,s()}}}function H(i){let e,t,n;return{c(){e=C("to "),t=m("strong"),n=C(i[3]),p(t,"class","txt-nowrap")},m(l,s){b(l,e,s),b(l,t,s),g(t,n)},p(l,s){s&8&&K(n,l[3])},d(l){l&&(_(e),_(t))}}}function V(i){let e,t,n,l,s,o,f,a;return{c(){e=m("label"),t=C("Password"),l=y(),s=m("input"),p(e,"for",n=i[8]),p(s,"type","password"),p(s,"id",o=i[8]),s.required=!0,s.autofocus=!0},m(r,u){b(r,e,u),g(e,t),b(r,l,u),b(r,s,u),F(s,i[0]),s.focus(),f||(a=q(s,"input",i[7]),f=!0)},p(r,u){u&256&&n!==(n=r[8])&&p(e,"for",n),u&256&&o!==(o=r[8])&&p(s,"id",o),u&1&&s.value!==r[0]&&F(s,r[0])},d(r){r&&(_(e),_(l),_(s)),f=!1,a()}}}function X(i){let e,t,n,l;const s=[U,Q],o=[];function f(a,r){return a[2]?0:1}return e=f(i),t=o[e]=s[e](i),{c(){t.c(),n=R()},m(a,r){o[e].m(a,r),b(a,n,r),l=!0},p(a,r){let u=e;e=f(a),e===u?o[e].p(a,r):(O(),h(o[u],1,1,()=>{o[u]=null}),N(),t=o[e],t?t.p(a,r):(t=o[e]=s[e](a),t.c()),v(t,1),t.m(n.parentNode,n))},i(a){l||(v(t),l=!0)},o(a){h(t),l=!1},d(a){a&&_(n),o[e].d(a)}}}function Z(i){let e,t;return e=new A({props:{nobranding:!0,$$slots:{default:[X]},$$scope:{ctx:i}}}),{c(){I(e.$$.fragment)},m(n,l){S(e,n,l),t=!0},p(n,[l]){const s={};l&527&&(s.$$scope={dirty:l,ctx:n}),e.$set(s)},i(n){t||(v(e.$$.fragment,n),t=!0)},o(n){h(e.$$.fragment,n),t=!1},d(n){L(e,n)}}}function x(i,e,t){let n,{params:l}=e,s="",o=!1,f=!1;async function a(){if(o)return;t(1,o=!0);const k=new W("../");try{const $=Y(l==null?void 0:l.token);await k.collection($.collectionId).confirmEmailChange(l==null?void 0:l.token,s),t(2,f=!0)}catch($){j.error($)}t(1,o=!1)}const r=()=>window.close();function u(){s=this.value,t(0,s)}return i.$$set=k=>{"params"in k&&t(5,l=k.params)},i.$$.update=()=>{i.$$.dirty&32&&t(3,n=D.getJWTPayload(l==null?void 0:l.token).newEmail||"")},[s,o,f,n,a,l,r,u]}class te extends J{constructor(e){super(),M(this,e,x,Z,z,{params:5})}}export{te as default};
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
import{S as A,i as D,s as W,F as Y,d as H,t as P,a as q,m as L,c as N,J as j,h as _,C as B,D as E,l as m,L as G,M as K,g as O,p as Q,H as F,o as S,u as b,v as C,w as p,f as J,k as M,n as w,q as U,A as y,I as V,z as R}from"./index-ChG9Kpfq.js";function X(a){let e,l,s,n,t,o,c,r,i,u,v,g,k,h,d=a[4]&&z(a);return o=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:a}}}),r=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:a}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=y(`Reset your user password
|
||||
import{S as A,i as D,s as W,F as Y,d as H,t as P,a as q,m as L,c as N,J as j,h as _,C as B,D as E,l as m,L as G,M as K,g as O,p as Q,H as F,o as S,u as b,v as C,w as p,f as J,k as M,n as w,q as U,A as y,I as V,z as R}from"./index-rRnVk674.js";function X(a){let e,l,s,n,t,o,c,r,i,u,v,g,k,h,d=a[4]&&z(a);return o=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:a}}}),r=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:a}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=y(`Reset your user password
|
||||
`),d&&d.c(),t=C(),N(o.$$.fragment),c=C(),N(r.$$.fragment),i=C(),u=b("button"),v=b("span"),v.textContent="Set new password",p(l,"class","content txt-center m-b-base"),p(v,"class","txt"),p(u,"type","submit"),p(u,"class","btn btn-lg btn-block"),u.disabled=a[2],M(u,"btn-loading",a[2])},m(f,$){m(f,e,$),w(e,l),w(l,s),w(s,n),d&&d.m(s,null),w(e,t),L(o,e,null),w(e,c),L(r,e,null),w(e,i),w(e,u),w(u,v),g=!0,k||(h=S(e,"submit",U(a[5])),k=!0)},p(f,$){f[4]?d?d.p(f,$):(d=z(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 I={};$&3074&&(I.$$scope={dirty:$,ctx:f}),r.$set(I),(!g||$&4)&&(u.disabled=f[2]),(!g||$&4)&&M(u,"btn-loading",f[2])},i(f){g||(q(o.$$.fragment,f),q(r.$$.fragment,f),g=!0)},o(f){P(o.$$.fragment,f),P(r.$$.fragment,f),g=!1},d(f){f&&_(e),d&&d.d(),H(o),H(r),k=!1,h()}}}function Z(a){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=C(),s=b("button"),s.textContent="Close",p(e,"class","alert alert-success"),p(s,"type","button"),p(s,"class","btn btn-transparent btn-block")},m(o,c){m(o,e,c),m(o,l,c),m(o,s,c),n||(t=S(s,"click",a[7]),n=!0)},p:F,i:F,o:F,d(o){o&&(_(e),_(l),_(s)),n=!1,t()}}}function z(a){let e,l,s;return{c(){e=y("for "),l=b("strong"),s=y(a[4])},m(n,t){m(n,e,t),m(n,l,t),w(l,s)},p(n,t){t&16&&V(s,n[4])},d(n){n&&(_(e),_(l))}}}function x(a){let e,l,s,n,t,o,c,r;return{c(){e=b("label"),l=y("New password"),n=C(),t=b("input"),p(e,"for",s=a[10]),p(t,"type","password"),p(t,"id",o=a[10]),t.required=!0,t.autofocus=!0},m(i,u){m(i,e,u),w(e,l),m(i,n,u),m(i,t,u),R(t,a[0]),t.focus(),c||(r=S(t,"input",a[8]),c=!0)},p(i,u){u&1024&&s!==(s=i[10])&&p(e,"for",s),u&1024&&o!==(o=i[10])&&p(t,"id",o),u&1&&t.value!==i[0]&&R(t,i[0])},d(i){i&&(_(e),_(n),_(t)),c=!1,r()}}}function ee(a){let e,l,s,n,t,o,c,r;return{c(){e=b("label"),l=y("New password confirm"),n=C(),t=b("input"),p(e,"for",s=a[10]),p(t,"type","password"),p(t,"id",o=a[10]),t.required=!0},m(i,u){m(i,e,u),w(e,l),m(i,n,u),m(i,t,u),R(t,a[1]),c||(r=S(t,"input",a[9]),c=!0)},p(i,u){u&1024&&s!==(s=i[10])&&p(e,"for",s),u&1024&&o!==(o=i[10])&&p(t,"id",o),u&2&&t.value!==i[1]&&R(t,i[1])},d(i){i&&(_(e),_(n),_(t)),c=!1,r()}}}function te(a){let e,l,s,n;const t=[Z,X],o=[];function c(r,i){return r[3]?0:1}return e=c(a),l=o[e]=t[e](a),{c(){l.c(),s=G()},m(r,i){o[e].m(r,i),m(r,s,i),n=!0},p(r,i){let u=e;e=c(r),e===u?o[e].p(r,i):(B(),P(o[u],1,1,()=>{o[u]=null}),E(),l=o[e],l?l.p(r,i):(l=o[e]=t[e](r),l.c()),q(l,1),l.m(s.parentNode,s))},i(r){n||(q(l),n=!0)},o(r){P(l),n=!1},d(r){r&&_(s),o[e].d(r)}}}function se(a){let e,l;return e=new Y({props:{nobranding:!0,$$slots:{default:[te]},$$scope:{ctx:a}}}),{c(){N(e.$$.fragment)},m(s,n){L(e,s,n),l=!0},p(s,[n]){const t={};n&2079&&(t.$$scope={dirty:n,ctx:s}),e.$set(t)},i(s){l||(q(e.$$.fragment,s),l=!0)},o(s){P(e.$$.fragment,s),l=!1},d(s){H(e,s)}}}function le(a,e,l){let s,{params:n}=e,t="",o="",c=!1,r=!1;async function i(){if(c)return;l(2,c=!0);const k=new K("../");try{const h=O(n==null?void 0:n.token);await k.collection(h.collectionId).confirmPasswordReset(n==null?void 0:n.token,t,o),l(3,r=!0)}catch(h){Q.error(h)}l(2,c=!1)}const u=()=>window.close();function v(){t=this.value,l(0,t)}function g(){o=this.value,l(1,o)}return a.$$set=k=>{"params"in k&&l(6,n=k.params)},a.$$.update=()=>{a.$$.dirty&64&&l(4,s=j.getJWTPayload(n==null?void 0:n.token).email||"")},[t,o,c,r,s,i,n,u,v,g]}class oe extends A{constructor(e){super(),D(this,e,le,se,W,{params:6})}}export{oe as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{S as M,i as P,s as R,F as I,d as N,t as S,a as V,m as q,c as F,M as w,g as y,N as E,h as r,l as a,L as g,p as j,H as k,u,w as d,o as m,v,k as C,n as z}from"./index-ChG9Kpfq.js";function A(o){let e,l,n;function t(i,f){return i[4]?K:J}let s=t(o),c=s(o);return{c(){e=u("div"),e.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>',l=v(),c.c(),n=g(),d(e,"class","alert alert-danger")},m(i,f){a(i,e,f),a(i,l,f),c.m(i,f),a(i,n,f)},p(i,f){s===(s=t(i))&&c?c.p(i,f):(c.d(1),c=s(i),c&&(c.c(),c.m(n.parentNode,n)))},d(i){i&&(r(e),r(l),r(n)),c.d(i)}}}function B(o){let e,l,n,t,s;return{c(){e=u("div"),e.innerHTML='<div class="icon"><i class="ri-checkbox-circle-line"></i></div> <div class="content txt-bold"><p>Please check your email for the new verification link.</p></div>',l=v(),n=u("button"),n.textContent="Close",d(e,"class","alert alert-success"),d(n,"type","button"),d(n,"class","btn btn-transparent btn-block")},m(c,i){a(c,e,i),a(c,l,i),a(c,n,i),t||(s=m(n,"click",o[8]),t=!0)},p:k,d(c){c&&(r(e),r(l),r(n)),t=!1,s()}}}function D(o){let e,l,n,t,s;return{c(){e=u("div"),e.innerHTML='<div class="icon"><i class="ri-checkbox-circle-line"></i></div> <div class="content txt-bold"><p>Successfully verified email address.</p></div>',l=v(),n=u("button"),n.textContent="Close",d(e,"class","alert alert-success"),d(n,"type","button"),d(n,"class","btn btn-transparent btn-block")},m(c,i){a(c,e,i),a(c,l,i),a(c,n,i),t||(s=m(n,"click",o[7]),t=!0)},p:k,d(c){c&&(r(e),r(l),r(n)),t=!1,s()}}}function G(o){let e;return{c(){e=u("div"),e.innerHTML='<div class="loader loader-lg"><em>Please wait...</em></div>',d(e,"class","txt-center")},m(l,n){a(l,e,n)},p:k,d(l){l&&r(e)}}}function J(o){let e,l,n;return{c(){e=u("button"),e.textContent="Close",d(e,"type","button"),d(e,"class","btn btn-transparent btn-block")},m(t,s){a(t,e,s),l||(n=m(e,"click",o[9]),l=!0)},p:k,d(t){t&&r(e),l=!1,n()}}}function K(o){let e,l,n,t;return{c(){e=u("button"),l=u("span"),l.textContent="Resend",d(l,"class","txt"),d(e,"type","button"),d(e,"class","btn btn-transparent btn-block"),e.disabled=o[3],C(e,"btn-loading",o[3])},m(s,c){a(s,e,c),z(e,l),n||(t=m(e,"click",o[5]),n=!0)},p(s,c){c&8&&(e.disabled=s[3]),c&8&&C(e,"btn-loading",s[3])},d(s){s&&r(e),n=!1,t()}}}function O(o){let e;function l(s,c){return s[1]?G:s[0]?D:s[2]?B:A}let n=l(o),t=n(o);return{c(){t.c(),e=g()},m(s,c){t.m(s,c),a(s,e,c)},p(s,c){n===(n=l(s))&&t?t.p(s,c):(t.d(1),t=n(s),t&&(t.c(),t.m(e.parentNode,e)))},d(s){s&&r(e),t.d(s)}}}function Q(o){let e,l;return e=new I({props:{nobranding:!0,$$slots:{default:[O]},$$scope:{ctx:o}}}),{c(){F(e.$$.fragment)},m(n,t){q(e,n,t),l=!0},p(n,[t]){const s={};t&2079&&(s.$$scope={dirty:t,ctx:n}),e.$set(s)},i(n){l||(V(e.$$.fragment,n),l=!0)},o(n){S(e.$$.fragment,n),l=!1},d(n){N(e,n)}}}function U(o,e,l){let n,{params:t}=e,s=!1,c=!1,i=!1,f=!1;x();async function x(){if(c)return;l(1,c=!0);const p=new w("../");try{const b=y(t==null?void 0:t.token);await p.collection(b.collectionId).confirmVerification(t==null?void 0:t.token),l(0,s=!0)}catch{l(0,s=!1)}l(1,c=!1)}async function T(){const p=y(t==null?void 0:t.token);if(f||!p.collectionId||!p.email)return;l(3,f=!0);const b=new w("../");try{const _=y(t==null?void 0:t.token);await b.collection(_.collectionId).requestVerification(_.email),l(2,i=!0)}catch(_){j.error(_),l(2,i=!1)}l(3,f=!1)}const h=()=>window.close(),H=()=>window.close(),L=()=>window.close();return o.$$set=p=>{"params"in p&&l(6,t=p.params)},o.$$.update=()=>{o.$$.dirty&64&&l(4,n=(t==null?void 0:t.token)&&E(t.token))},[s,c,i,f,n,T,t,h,H,L]}class X extends M{constructor(e){super(),P(this,e,U,Q,R,{params:6})}}export{X as default};
|
||||
import{S as M,i as P,s as R,F as I,d as N,t as S,a as V,m as q,c as F,M as w,g as y,N as E,h as r,l as a,L as g,p as j,H as k,u,w as d,o as m,v,k as C,n as z}from"./index-rRnVk674.js";function A(o){let e,l,n;function t(i,f){return i[4]?K:J}let s=t(o),c=s(o);return{c(){e=u("div"),e.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>',l=v(),c.c(),n=g(),d(e,"class","alert alert-danger")},m(i,f){a(i,e,f),a(i,l,f),c.m(i,f),a(i,n,f)},p(i,f){s===(s=t(i))&&c?c.p(i,f):(c.d(1),c=s(i),c&&(c.c(),c.m(n.parentNode,n)))},d(i){i&&(r(e),r(l),r(n)),c.d(i)}}}function B(o){let e,l,n,t,s;return{c(){e=u("div"),e.innerHTML='<div class="icon"><i class="ri-checkbox-circle-line"></i></div> <div class="content txt-bold"><p>Please check your email for the new verification link.</p></div>',l=v(),n=u("button"),n.textContent="Close",d(e,"class","alert alert-success"),d(n,"type","button"),d(n,"class","btn btn-transparent btn-block")},m(c,i){a(c,e,i),a(c,l,i),a(c,n,i),t||(s=m(n,"click",o[8]),t=!0)},p:k,d(c){c&&(r(e),r(l),r(n)),t=!1,s()}}}function D(o){let e,l,n,t,s;return{c(){e=u("div"),e.innerHTML='<div class="icon"><i class="ri-checkbox-circle-line"></i></div> <div class="content txt-bold"><p>Successfully verified email address.</p></div>',l=v(),n=u("button"),n.textContent="Close",d(e,"class","alert alert-success"),d(n,"type","button"),d(n,"class","btn btn-transparent btn-block")},m(c,i){a(c,e,i),a(c,l,i),a(c,n,i),t||(s=m(n,"click",o[7]),t=!0)},p:k,d(c){c&&(r(e),r(l),r(n)),t=!1,s()}}}function G(o){let e;return{c(){e=u("div"),e.innerHTML='<div class="loader loader-lg"><em>Please wait...</em></div>',d(e,"class","txt-center")},m(l,n){a(l,e,n)},p:k,d(l){l&&r(e)}}}function J(o){let e,l,n;return{c(){e=u("button"),e.textContent="Close",d(e,"type","button"),d(e,"class","btn btn-transparent btn-block")},m(t,s){a(t,e,s),l||(n=m(e,"click",o[9]),l=!0)},p:k,d(t){t&&r(e),l=!1,n()}}}function K(o){let e,l,n,t;return{c(){e=u("button"),l=u("span"),l.textContent="Resend",d(l,"class","txt"),d(e,"type","button"),d(e,"class","btn btn-transparent btn-block"),e.disabled=o[3],C(e,"btn-loading",o[3])},m(s,c){a(s,e,c),z(e,l),n||(t=m(e,"click",o[5]),n=!0)},p(s,c){c&8&&(e.disabled=s[3]),c&8&&C(e,"btn-loading",s[3])},d(s){s&&r(e),n=!1,t()}}}function O(o){let e;function l(s,c){return s[1]?G:s[0]?D:s[2]?B:A}let n=l(o),t=n(o);return{c(){t.c(),e=g()},m(s,c){t.m(s,c),a(s,e,c)},p(s,c){n===(n=l(s))&&t?t.p(s,c):(t.d(1),t=n(s),t&&(t.c(),t.m(e.parentNode,e)))},d(s){s&&r(e),t.d(s)}}}function Q(o){let e,l;return e=new I({props:{nobranding:!0,$$slots:{default:[O]},$$scope:{ctx:o}}}),{c(){F(e.$$.fragment)},m(n,t){q(e,n,t),l=!0},p(n,[t]){const s={};t&2079&&(s.$$scope={dirty:t,ctx:n}),e.$set(s)},i(n){l||(V(e.$$.fragment,n),l=!0)},o(n){S(e.$$.fragment,n),l=!1},d(n){N(e,n)}}}function U(o,e,l){let n,{params:t}=e,s=!1,c=!1,i=!1,f=!1;x();async function x(){if(c)return;l(1,c=!0);const p=new w("../");try{const b=y(t==null?void 0:t.token);await p.collection(b.collectionId).confirmVerification(t==null?void 0:t.token),l(0,s=!0)}catch{l(0,s=!1)}l(1,c=!1)}async function T(){const p=y(t==null?void 0:t.token);if(f||!p.collectionId||!p.email)return;l(3,f=!0);const b=new w("../");try{const _=y(t==null?void 0:t.token);await b.collection(_.collectionId).requestVerification(_.email),l(2,i=!0)}catch(_){j.error(_),l(2,i=!1)}l(3,f=!1)}const h=()=>window.close(),H=()=>window.close(),L=()=>window.close();return o.$$set=p=>{"params"in p&&l(6,t=p.params)},o.$$.update=()=>{o.$$.dirty&64&&l(4,n=(t==null?void 0:t.token)&&E(t.token))},[s,c,i,f,n,T,t,h,H,L]}class X extends M{constructor(e){super(),P(this,e,U,Q,R,{params:6})}}export{X as default};
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
import{S as L,i as W,s as y,F as D,d as R,t as J,a as N,m as T,c as j,J as M,f as G,h as b,j as O,k as H,l as w,n as c,o as z,E as Q,q as U,G as V,u as _,A as P,v as h,w as f,p as I,K as X,r as Y,I as Z,z as q}from"./index-ChG9Kpfq.js";function K(r){let e,n,s;return{c(){e=P("for "),n=_("strong"),s=P(r[3]),f(n,"class","txt-nowrap")},m(l,t){w(l,e,t),w(l,n,t),c(n,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&(b(e),b(n))}}}function x(r){let e,n,s,l,t,i,p,d;return{c(){e=_("label"),n=P("New password"),l=h(),t=_("input"),f(e,"for",s=r[8]),f(t,"type","password"),f(t,"id",i=r[8]),t.required=!0,t.autofocus=!0},m(u,a){w(u,e,a),c(e,n),w(u,l,a),w(u,t,a),q(t,r[0]),t.focus(),p||(d=z(t,"input",r[6]),p=!0)},p(u,a){a&256&&s!==(s=u[8])&&f(e,"for",s),a&256&&i!==(i=u[8])&&f(t,"id",i),a&1&&t.value!==u[0]&&q(t,u[0])},d(u){u&&(b(e),b(l),b(t)),p=!1,d()}}}function ee(r){let e,n,s,l,t,i,p,d;return{c(){e=_("label"),n=P("New password confirm"),l=h(),t=_("input"),f(e,"for",s=r[8]),f(t,"type","password"),f(t,"id",i=r[8]),t.required=!0},m(u,a){w(u,e,a),c(e,n),w(u,l,a),w(u,t,a),q(t,r[1]),p||(d=z(t,"input",r[7]),p=!0)},p(u,a){a&256&&s!==(s=u[8])&&f(e,"for",s),a&256&&i!==(i=u[8])&&f(t,"id",i),a&2&&t.value!==u[1]&&q(t,u[1])},d(u){u&&(b(e),b(l),b(t)),p=!1,d()}}}function te(r){let e,n,s,l,t,i,p,d,u,a,g,S,C,v,k,F,A,m=r[3]&&K(r);return i=new G({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:o})=>({8:o}),({uniqueId:o})=>o?256:0]},$$scope:{ctx:r}}}),d=new G({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:o})=>({8:o}),({uniqueId:o})=>o?256:0]},$$scope:{ctx:r}}}),{c(){e=_("form"),n=_("div"),s=_("h4"),l=P(`Reset your superuser password
|
||||
import{S as L,i as W,s as y,F as D,d as R,t as J,a as N,m as T,c as j,J as M,f as G,h as b,j as O,k as H,l as w,n as c,o as z,E as Q,q as U,G as V,u as _,A as P,v as h,w as f,p as I,K as X,r as Y,I as Z,z as q}from"./index-rRnVk674.js";function K(r){let e,n,s;return{c(){e=P("for "),n=_("strong"),s=P(r[3]),f(n,"class","txt-nowrap")},m(l,t){w(l,e,t),w(l,n,t),c(n,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&(b(e),b(n))}}}function x(r){let e,n,s,l,t,i,p,d;return{c(){e=_("label"),n=P("New password"),l=h(),t=_("input"),f(e,"for",s=r[8]),f(t,"type","password"),f(t,"id",i=r[8]),t.required=!0,t.autofocus=!0},m(u,a){w(u,e,a),c(e,n),w(u,l,a),w(u,t,a),q(t,r[0]),t.focus(),p||(d=z(t,"input",r[6]),p=!0)},p(u,a){a&256&&s!==(s=u[8])&&f(e,"for",s),a&256&&i!==(i=u[8])&&f(t,"id",i),a&1&&t.value!==u[0]&&q(t,u[0])},d(u){u&&(b(e),b(l),b(t)),p=!1,d()}}}function ee(r){let e,n,s,l,t,i,p,d;return{c(){e=_("label"),n=P("New password confirm"),l=h(),t=_("input"),f(e,"for",s=r[8]),f(t,"type","password"),f(t,"id",i=r[8]),t.required=!0},m(u,a){w(u,e,a),c(e,n),w(u,l,a),w(u,t,a),q(t,r[1]),p||(d=z(t,"input",r[7]),p=!0)},p(u,a){a&256&&s!==(s=u[8])&&f(e,"for",s),a&256&&i!==(i=u[8])&&f(t,"id",i),a&2&&t.value!==u[1]&&q(t,u[1])},d(u){u&&(b(e),b(l),b(t)),p=!1,d()}}}function te(r){let e,n,s,l,t,i,p,d,u,a,g,S,C,v,k,F,A,m=r[3]&&K(r);return i=new G({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:o})=>({8:o}),({uniqueId:o})=>o?256:0]},$$scope:{ctx:r}}}),d=new G({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:o})=>({8:o}),({uniqueId:o})=>o?256:0]},$$scope:{ctx:r}}}),{c(){e=_("form"),n=_("div"),s=_("h4"),l=P(`Reset your superuser password
|
||||
`),m&&m.c(),t=h(),j(i.$$.fragment),p=h(),j(d.$$.fragment),u=h(),a=_("button"),g=_("span"),g.textContent="Set new password",S=h(),C=_("div"),v=_("a"),v.textContent="Back to login",f(s,"class","m-b-xs"),f(n,"class","content txt-center m-b-sm"),f(g,"class","txt"),f(a,"type","submit"),f(a,"class","btn btn-lg btn-block"),a.disabled=r[2],H(a,"btn-loading",r[2]),f(e,"class","m-b-base"),f(v,"href","/login"),f(v,"class","link-hint"),f(C,"class","content txt-center")},m(o,$){w(o,e,$),c(e,n),c(n,s),c(s,l),m&&m.m(s,null),c(e,t),T(i,e,null),c(e,p),T(d,e,null),c(e,u),c(e,a),c(a,g),w(o,S,$),w(o,C,$),c(C,v),k=!0,F||(A=[z(e,"submit",U(r[4])),Q(V.call(null,v))],F=!0)},p(o,$){o[3]?m?m.p(o,$):(m=K(o),m.c(),m.m(s,null)):m&&(m.d(1),m=null);const B={};$&769&&(B.$$scope={dirty:$,ctx:o}),i.$set(B);const E={};$&770&&(E.$$scope={dirty:$,ctx:o}),d.$set(E),(!k||$&4)&&(a.disabled=o[2]),(!k||$&4)&&H(a,"btn-loading",o[2])},i(o){k||(N(i.$$.fragment,o),N(d.$$.fragment,o),k=!0)},o(o){J(i.$$.fragment,o),J(d.$$.fragment,o),k=!1},d(o){o&&(b(e),b(S),b(C)),m&&m.d(),R(i),R(d),F=!1,O(A)}}}function se(r){let e,n;return e=new D({props:{$$slots:{default:[te]},$$scope:{ctx:r}}}),{c(){j(e.$$.fragment)},m(s,l){T(e,s,l),n=!0},p(s,[l]){const t={};l&527&&(t.$$scope={dirty:l,ctx:s}),e.$set(t)},i(s){n||(N(e.$$.fragment,s),n=!0)},o(s){J(e.$$.fragment,s),n=!1},d(s){R(e,s)}}}function le(r,e,n){let s,{params:l}=e,t="",i="",p=!1;async function d(){if(!p){n(2,p=!0);try{await I.collection("_superusers").confirmPasswordReset(l==null?void 0:l.token,t,i),X("Successfully set a new superuser password."),Y("/")}catch(g){I.error(g)}n(2,p=!1)}}function u(){t=this.value,n(0,t)}function a(){i=this.value,n(1,i)}return r.$$set=g=>{"params"in g&&n(5,l=g.params)},r.$$.update=()=>{r.$$.dirty&32&&n(3,s=M.getJWTPayload(l==null?void 0:l.token).email||"")},[t,i,p,s,d,l,u,a]}class ae extends L{constructor(e){super(),W(this,e,le,se,y,{params:5})}}export{ae as default};
|
||||
+1
-1
@@ -1 +1 @@
|
||||
import{S as M,i as T,s as z,F as A,d as E,t as w,a as y,m as H,c as L,h as g,C as B,D,l as k,n as d,E as G,G as I,v,u as m,w as p,p as C,H as F,I as N,A as h,f as j,k as P,o as R,q as J,z as S}from"./index-ChG9Kpfq.js";function K(u){let e,s,n,l,t,r,c,_,i,a,b,f;return l=new j({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:o})=>({5:o}),({uniqueId:o})=>o?32:0]},$$scope:{ctx:u}}}),{c(){e=m("form"),s=m("div"),s.innerHTML='<h4 class="m-b-xs">Forgotten superuser password</h4> <p>Enter the email associated with your account and we’ll send you a recovery link:</p>',n=v(),L(l.$$.fragment),t=v(),r=m("button"),c=m("i"),_=v(),i=m("span"),i.textContent="Send recovery link",p(s,"class","content txt-center m-b-sm"),p(c,"class","ri-mail-send-line"),p(i,"class","txt"),p(r,"type","submit"),p(r,"class","btn btn-lg btn-block"),r.disabled=u[1],P(r,"btn-loading",u[1]),p(e,"class","m-b-base")},m(o,$){k(o,e,$),d(e,s),d(e,n),H(l,e,null),d(e,t),d(e,r),d(r,c),d(r,_),d(r,i),a=!0,b||(f=R(e,"submit",J(u[3])),b=!0)},p(o,$){const q={};$&97&&(q.$$scope={dirty:$,ctx:o}),l.$set(q),(!a||$&2)&&(r.disabled=o[1]),(!a||$&2)&&P(r,"btn-loading",o[1])},i(o){a||(y(l.$$.fragment,o),a=!0)},o(o){w(l.$$.fragment,o),a=!1},d(o){o&&g(e),E(l),b=!1,f()}}}function O(u){let e,s,n,l,t,r,c,_,i;return{c(){e=m("div"),s=m("div"),s.innerHTML='<i class="ri-checkbox-circle-line"></i>',n=v(),l=m("div"),t=m("p"),r=h("Check "),c=m("strong"),_=h(u[0]),i=h(" for the recovery link."),p(s,"class","icon"),p(c,"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,r),d(t,c),d(c,_),d(t,i)},p(a,b){b&1&&N(_,a[0])},i:F,o:F,d(a){a&&g(e)}}}function Q(u){let e,s,n,l,t,r,c,_;return{c(){e=m("label"),s=h("Email"),l=v(),t=m("input"),p(e,"for",n=u[5]),p(t,"type","email"),p(t,"id",r=u[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),S(t,u[0]),t.focus(),c||(_=R(t,"input",u[4]),c=!0)},p(i,a){a&32&&n!==(n=i[5])&&p(e,"for",n),a&32&&r!==(r=i[5])&&p(t,"id",r),a&1&&t.value!==i[0]&&S(t,i[0])},d(i){i&&(g(e),g(l),g(t)),c=!1,_()}}}function U(u){let e,s,n,l,t,r,c,_;const i=[O,K],a=[];function b(f,o){return f[2]?0:1}return e=b(u),s=a[e]=i[e](u),{c(){s.c(),n=v(),l=m("div"),t=m("a"),t.textContent="Back to login",p(t,"href","/login"),p(t,"class","link-hint"),p(l,"class","content txt-center")},m(f,o){a[e].m(f,o),k(f,n,o),k(f,l,o),d(l,t),r=!0,c||(_=G(I.call(null,t)),c=!0)},p(f,o){let $=e;e=b(f),e===$?a[e].p(f,o):(B(),w(a[$],1,1,()=>{a[$]=null}),D(),s=a[e],s?s.p(f,o):(s=a[e]=i[e](f),s.c()),y(s,1),s.m(n.parentNode,n))},i(f){r||(y(s),r=!0)},o(f){w(s),r=!1},d(f){f&&(g(n),g(l)),a[e].d(f),c=!1,_()}}}function V(u){let e,s;return e=new A({props:{$$slots:{default:[U]},$$scope:{ctx:u}}}),{c(){L(e.$$.fragment)},m(n,l){H(e,n,l),s=!0},p(n,[l]){const t={};l&71&&(t.$$scope={dirty:l,ctx:n}),e.$set(t)},i(n){s||(y(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){E(e,n)}}}function W(u,e,s){let n="",l=!1,t=!1;async function r(){if(!l){s(1,l=!0);try{await C.collection("_superusers").requestPasswordReset(n),s(2,t=!0)}catch(_){C.error(_)}s(1,l=!1)}}function c(){n=this.value,s(0,n)}return[n,l,t,r,c]}class Y extends M{constructor(e){super(),T(this,e,W,V,z,{})}}export{Y as default};
|
||||
import{S as M,i as T,s as z,F as A,d as E,t as w,a as y,m as H,c as L,h as g,C as B,D,l as k,n as d,E as G,G as I,v,u as m,w as p,p as C,H as F,I as N,A as h,f as j,k as P,o as R,q as J,z as S}from"./index-rRnVk674.js";function K(u){let e,s,n,l,t,r,c,_,i,a,b,f;return l=new j({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:o})=>({5:o}),({uniqueId:o})=>o?32:0]},$$scope:{ctx:u}}}),{c(){e=m("form"),s=m("div"),s.innerHTML='<h4 class="m-b-xs">Forgotten superuser password</h4> <p>Enter the email associated with your account and we’ll send you a recovery link:</p>',n=v(),L(l.$$.fragment),t=v(),r=m("button"),c=m("i"),_=v(),i=m("span"),i.textContent="Send recovery link",p(s,"class","content txt-center m-b-sm"),p(c,"class","ri-mail-send-line"),p(i,"class","txt"),p(r,"type","submit"),p(r,"class","btn btn-lg btn-block"),r.disabled=u[1],P(r,"btn-loading",u[1]),p(e,"class","m-b-base")},m(o,$){k(o,e,$),d(e,s),d(e,n),H(l,e,null),d(e,t),d(e,r),d(r,c),d(r,_),d(r,i),a=!0,b||(f=R(e,"submit",J(u[3])),b=!0)},p(o,$){const q={};$&97&&(q.$$scope={dirty:$,ctx:o}),l.$set(q),(!a||$&2)&&(r.disabled=o[1]),(!a||$&2)&&P(r,"btn-loading",o[1])},i(o){a||(y(l.$$.fragment,o),a=!0)},o(o){w(l.$$.fragment,o),a=!1},d(o){o&&g(e),E(l),b=!1,f()}}}function O(u){let e,s,n,l,t,r,c,_,i;return{c(){e=m("div"),s=m("div"),s.innerHTML='<i class="ri-checkbox-circle-line"></i>',n=v(),l=m("div"),t=m("p"),r=h("Check "),c=m("strong"),_=h(u[0]),i=h(" for the recovery link."),p(s,"class","icon"),p(c,"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,r),d(t,c),d(c,_),d(t,i)},p(a,b){b&1&&N(_,a[0])},i:F,o:F,d(a){a&&g(e)}}}function Q(u){let e,s,n,l,t,r,c,_;return{c(){e=m("label"),s=h("Email"),l=v(),t=m("input"),p(e,"for",n=u[5]),p(t,"type","email"),p(t,"id",r=u[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),S(t,u[0]),t.focus(),c||(_=R(t,"input",u[4]),c=!0)},p(i,a){a&32&&n!==(n=i[5])&&p(e,"for",n),a&32&&r!==(r=i[5])&&p(t,"id",r),a&1&&t.value!==i[0]&&S(t,i[0])},d(i){i&&(g(e),g(l),g(t)),c=!1,_()}}}function U(u){let e,s,n,l,t,r,c,_;const i=[O,K],a=[];function b(f,o){return f[2]?0:1}return e=b(u),s=a[e]=i[e](u),{c(){s.c(),n=v(),l=m("div"),t=m("a"),t.textContent="Back to login",p(t,"href","/login"),p(t,"class","link-hint"),p(l,"class","content txt-center")},m(f,o){a[e].m(f,o),k(f,n,o),k(f,l,o),d(l,t),r=!0,c||(_=G(I.call(null,t)),c=!0)},p(f,o){let $=e;e=b(f),e===$?a[e].p(f,o):(B(),w(a[$],1,1,()=>{a[$]=null}),D(),s=a[e],s?s.p(f,o):(s=a[e]=i[e](f),s.c()),y(s,1),s.m(n.parentNode,n))},i(f){r||(y(s),r=!0)},o(f){w(s),r=!1},d(f){f&&(g(n),g(l)),a[e].d(f),c=!1,_()}}}function V(u){let e,s;return e=new A({props:{$$slots:{default:[U]},$$scope:{ctx:u}}}),{c(){L(e.$$.fragment)},m(n,l){H(e,n,l),s=!0},p(n,[l]){const t={};l&71&&(t.$$scope={dirty:l,ctx:n}),e.$set(t)},i(n){s||(y(e.$$.fragment,n),s=!0)},o(n){w(e.$$.fragment,n),s=!1},d(n){E(e,n)}}}function W(u,e,s){let n="",l=!1,t=!1;async function r(){if(!l){s(1,l=!0);try{await C.collection("_superusers").requestPasswordReset(n),s(2,t=!0)}catch(_){C.error(_)}s(1,l=!1)}}function c(){n=this.value,s(0,n)}return[n,l,t,r,c]}class Y extends M{constructor(e){super(),T(this,e,W,V,z,{})}}export{Y as default};
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as se,i as ne,s as oe,X as H,h as b,t as X,a as V,I as Z,Z as ee,_ as ye,C as te,$ as Te,D as le,l as v,n as u,u as p,v as S,A as D,w as k,k as L,o as ae,W as Ee,d as G,m as Q,c as x,V as Ce,Y as fe,J as qe,p as Oe,a0 as pe}from"./index-ChG9Kpfq.js";function me(o,t,e){const n=o.slice();return n[4]=t[e],n}function _e(o,t,e){const n=o.slice();return n[4]=t[e],n}function he(o,t){let e,n=t[4].code+"",d,c,r,a;function f(){return t[3](t[4])}return{key:o,first:null,c(){e=p("button"),d=D(n),c=S(),k(e,"class","tab-item"),L(e,"active",t[1]===t[4].code),this.first=e},m(g,y){v(g,e,y),u(e,d),u(e,c),r||(a=ae(e,"click",f),r=!0)},p(g,y){t=g,y&4&&n!==(n=t[4].code+"")&&Z(d,n),y&6&&L(e,"active",t[1]===t[4].code)},d(g){g&&b(e),r=!1,a()}}}function be(o,t){let e,n,d,c;return n=new Ee({props:{content:t[4].body}}),{key:o,first:null,c(){e=p("div"),x(n.$$.fragment),d=S(),k(e,"class","tab-item"),L(e,"active",t[1]===t[4].code),this.first=e},m(r,a){v(r,e,a),Q(n,e,null),u(e,d),c=!0},p(r,a){t=r;const f={};a&4&&(f.content=t[4].body),n.$set(f),(!c||a&6)&&L(e,"active",t[1]===t[4].code)},i(r){c||(V(n.$$.fragment,r),c=!0)},o(r){X(n.$$.fragment,r),c=!1},d(r){r&&b(e),G(n)}}}function Ae(o){let t,e,n,d,c,r,a,f=o[0].name+"",g,y,F,q,J,W,U,O,A,T,C,R=[],M=new Map,j,N,h=[],K=new Map,E,P=H(o[2]);const B=l=>l[4].code;for(let l=0;l<P.length;l+=1){let s=_e(o,P,l),_=B(s);M.set(_,R[l]=he(_,s))}let m=H(o[2]);const Y=l=>l[4].code;for(let l=0;l<m.length;l+=1){let s=me(o,m,l),_=Y(s);K.set(_,h[l]=be(_,s))}return{c(){t=p("div"),e=p("strong"),e.textContent="POST",n=S(),d=p("div"),c=p("p"),r=D("/api/collections/"),a=p("strong"),g=D(f),y=D("/confirm-password-reset"),F=S(),q=p("div"),q.textContent="Body Parameters",J=S(),W=p("table"),W.innerHTML='<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>token</span></div></td> <td><span class="label">String</span></td> <td>The token from the password reset request email.</td></tr> <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 new password to set.</td></tr> <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>',U=S(),O=p("div"),O.textContent="Responses",A=S(),T=p("div"),C=p("div");for(let l=0;l<R.length;l+=1)R[l].c();j=S(),N=p("div");for(let l=0;l<h.length;l+=1)h[l].c();k(e,"class","label label-primary"),k(d,"class","content"),k(t,"class","alert alert-success"),k(q,"class","section-title"),k(W,"class","table-compact table-border m-b-base"),k(O,"class","section-title"),k(C,"class","tabs-header compact combined left"),k(N,"class","tabs-content"),k(T,"class","tabs")},m(l,s){v(l,t,s),u(t,e),u(t,n),u(t,d),u(d,c),u(c,r),u(c,a),u(a,g),u(c,y),v(l,F,s),v(l,q,s),v(l,J,s),v(l,W,s),v(l,U,s),v(l,O,s),v(l,A,s),v(l,T,s),u(T,C);for(let _=0;_<R.length;_+=1)R[_]&&R[_].m(C,null);u(T,j),u(T,N);for(let _=0;_<h.length;_+=1)h[_]&&h[_].m(N,null);E=!0},p(l,[s]){(!E||s&1)&&f!==(f=l[0].name+"")&&Z(g,f),s&6&&(P=H(l[2]),R=ee(R,s,B,1,l,P,M,C,ye,he,null,_e)),s&6&&(m=H(l[2]),te(),h=ee(h,s,Y,1,l,m,K,N,Te,be,null,me),le())},i(l){if(!E){for(let s=0;s<m.length;s+=1)V(h[s]);E=!0}},o(l){for(let s=0;s<h.length;s+=1)X(h[s]);E=!1},d(l){l&&(b(t),b(F),b(q),b(J),b(W),b(U),b(O),b(A),b(T));for(let s=0;s<R.length;s+=1)R[s].d();for(let s=0;s<h.length;s+=1)h[s].d()}}}function We(o,t,e){let{collection:n}=t,d=204,c=[];const r=a=>e(1,d=a.code);return o.$$set=a=>{"collection"in a&&e(0,n=a.collection)},e(2,c=[{code:204,body:"null"},{code:400,body:`
|
||||
import{S as se,i as ne,s as oe,X as H,h as b,t as X,a as V,I as Z,Z as ee,_ as ye,C as te,$ as Te,D as le,l as v,n as u,u as p,v as S,A as D,w as k,k as L,o as ae,W as Ee,d as G,m as Q,c as x,V as Ce,Y as fe,J as qe,p as Oe,a0 as pe}from"./index-rRnVk674.js";function me(o,t,e){const n=o.slice();return n[4]=t[e],n}function _e(o,t,e){const n=o.slice();return n[4]=t[e],n}function he(o,t){let e,n=t[4].code+"",d,c,r,a;function f(){return t[3](t[4])}return{key:o,first:null,c(){e=p("button"),d=D(n),c=S(),k(e,"class","tab-item"),L(e,"active",t[1]===t[4].code),this.first=e},m(g,y){v(g,e,y),u(e,d),u(e,c),r||(a=ae(e,"click",f),r=!0)},p(g,y){t=g,y&4&&n!==(n=t[4].code+"")&&Z(d,n),y&6&&L(e,"active",t[1]===t[4].code)},d(g){g&&b(e),r=!1,a()}}}function be(o,t){let e,n,d,c;return n=new Ee({props:{content:t[4].body}}),{key:o,first:null,c(){e=p("div"),x(n.$$.fragment),d=S(),k(e,"class","tab-item"),L(e,"active",t[1]===t[4].code),this.first=e},m(r,a){v(r,e,a),Q(n,e,null),u(e,d),c=!0},p(r,a){t=r;const f={};a&4&&(f.content=t[4].body),n.$set(f),(!c||a&6)&&L(e,"active",t[1]===t[4].code)},i(r){c||(V(n.$$.fragment,r),c=!0)},o(r){X(n.$$.fragment,r),c=!1},d(r){r&&b(e),G(n)}}}function Ae(o){let t,e,n,d,c,r,a,f=o[0].name+"",g,y,F,q,J,W,U,O,A,T,C,R=[],M=new Map,j,N,h=[],K=new Map,E,P=H(o[2]);const B=l=>l[4].code;for(let l=0;l<P.length;l+=1){let s=_e(o,P,l),_=B(s);M.set(_,R[l]=he(_,s))}let m=H(o[2]);const Y=l=>l[4].code;for(let l=0;l<m.length;l+=1){let s=me(o,m,l),_=Y(s);K.set(_,h[l]=be(_,s))}return{c(){t=p("div"),e=p("strong"),e.textContent="POST",n=S(),d=p("div"),c=p("p"),r=D("/api/collections/"),a=p("strong"),g=D(f),y=D("/confirm-password-reset"),F=S(),q=p("div"),q.textContent="Body Parameters",J=S(),W=p("table"),W.innerHTML='<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>token</span></div></td> <td><span class="label">String</span></td> <td>The token from the password reset request email.</td></tr> <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 new password to set.</td></tr> <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>',U=S(),O=p("div"),O.textContent="Responses",A=S(),T=p("div"),C=p("div");for(let l=0;l<R.length;l+=1)R[l].c();j=S(),N=p("div");for(let l=0;l<h.length;l+=1)h[l].c();k(e,"class","label label-primary"),k(d,"class","content"),k(t,"class","alert alert-success"),k(q,"class","section-title"),k(W,"class","table-compact table-border m-b-base"),k(O,"class","section-title"),k(C,"class","tabs-header compact combined left"),k(N,"class","tabs-content"),k(T,"class","tabs")},m(l,s){v(l,t,s),u(t,e),u(t,n),u(t,d),u(d,c),u(c,r),u(c,a),u(a,g),u(c,y),v(l,F,s),v(l,q,s),v(l,J,s),v(l,W,s),v(l,U,s),v(l,O,s),v(l,A,s),v(l,T,s),u(T,C);for(let _=0;_<R.length;_+=1)R[_]&&R[_].m(C,null);u(T,j),u(T,N);for(let _=0;_<h.length;_+=1)h[_]&&h[_].m(N,null);E=!0},p(l,[s]){(!E||s&1)&&f!==(f=l[0].name+"")&&Z(g,f),s&6&&(P=H(l[2]),R=ee(R,s,B,1,l,P,M,C,ye,he,null,_e)),s&6&&(m=H(l[2]),te(),h=ee(h,s,Y,1,l,m,K,N,Te,be,null,me),le())},i(l){if(!E){for(let s=0;s<m.length;s+=1)V(h[s]);E=!0}},o(l){for(let s=0;s<h.length;s+=1)X(h[s]);E=!1},d(l){l&&(b(t),b(F),b(q),b(J),b(W),b(U),b(O),b(A),b(T));for(let s=0;s<R.length;s+=1)R[s].d();for(let s=0;s<h.length;s+=1)h[s].d()}}}function We(o,t,e){let{collection:n}=t,d=204,c=[];const r=a=>e(1,d=a.code);return o.$$set=a=>{"collection"in a&&e(0,n=a.collection)},e(2,c=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as re,i as ae,s as be,V as pe,W as ue,J as P,h as s,d as se,t as ne,a as ie,I as me,l as n,n as y,m as ce,u as p,A as I,v as a,c as le,w as u,p as de}from"./index-ChG9Kpfq.js";function he(o){var B,U,W,A,L,H,T,q,J,M,j,N;let i,m,c=o[0].name+"",b,d,k,h,D,f,_,l,S,$,w,g,C,v,E,r,R;return l=new pe({props:{js:`
|
||||
import{S as re,i as ae,s as be,V as pe,W as ue,J as P,h as s,d as se,t as ne,a as ie,I as me,l as n,n as y,m as ce,u as p,A as I,v as a,c as le,w as u,p as de}from"./index-rRnVk674.js";function he(o){var B,U,W,A,L,H,T,q,J,M,j,N;let i,m,c=o[0].name+"",b,d,k,h,D,f,_,l,S,$,w,g,C,v,E,r,R;return l=new pe({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[1]}');
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
import{S as Ot,i as St,s as $t,V as Mt,J as x,X as ie,W as Tt,h as o,d as ve,t as he,a as ye,I as te,Z as Je,_ as bt,C as qt,$ as Rt,D as Dt,l as r,n,m as we,u as i,A as h,v as f,c as Ce,w as k,p as Ht,k as Te,o as Lt,H as de}from"./index-ChG9Kpfq.js";import{F as Pt}from"./FieldsQueryParam-DkRoNbFY.js";function mt(d,e,t){const a=d.slice();return a[10]=e[t],a}function _t(d,e,t){const a=d.slice();return a[10]=e[t],a}function ht(d,e,t){const a=d.slice();return a[15]=e[t],a}function yt(d){let e;return{c(){e=i("p"),e.innerHTML=`<em>Note that in case of a password change all previously issued tokens for the current record
|
||||
will be automatically invalidated and if you want your user to remain signed in you need to
|
||||
reauthenticate manually after the update call.</em>`},m(t,a){r(t,e,a)},d(t){t&&o(e)}}}function kt(d){let e;return{c(){e=i("p"),e.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",k(e,"class","txt-hint txt-sm txt-right")},m(t,a){r(t,e,a)},d(t){t&&o(e)}}}function gt(d){let e,t,a,m,p,c,u,b,O,T,$,D,S,E,q,H,J,I,M,R,L,g,v,w;function Q(_,C){var le,z,ne;return C&1&&(b=null),b==null&&(b=!!((ne=(z=(le=_[0])==null?void 0:le.fields)==null?void 0:z.find(Wt))!=null&&ne.required)),b?Bt:Ft}let W=Q(d,-1),F=W(d);return{c(){e=i("tr"),e.innerHTML='<td colspan="3" class="txt-hint txt-bold">Auth specific fields</td>',t=f(),a=i("tr"),a.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>email</span></div></td> <td><span class="label">String</span></td> <td>The auth record email address.
|
||||
<br/>
|
||||
This field can be updated only by superusers or auth records with "Manage" access.
|
||||
<br/>
|
||||
Regular accounts can update their email by calling "Request email change".</td>`,m=f(),p=i("tr"),c=i("td"),u=i("div"),F.c(),O=f(),T=i("span"),T.textContent="emailVisibility",$=f(),D=i("td"),D.innerHTML='<span class="label">Boolean</span>',S=f(),E=i("td"),E.textContent="Whether to show/hide the auth record email when fetching the record data.",q=f(),H=i("tr"),H.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>oldPassword</span></div></td> <td><span class="label">String</span></td> <td>Old auth record password.
|
||||
<br/>
|
||||
This field is required only when changing the record password. Superusers and auth records
|
||||
with "Manage" access can skip this field.</td>`,J=f(),I=i("tr"),I.innerHTML='<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>New auth record password.</td>',M=f(),R=i("tr"),R.innerHTML='<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>passwordConfirm</span></div></td> <td><span class="label">String</span></td> <td>New auth record password confirmation.</td>',L=f(),g=i("tr"),g.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>verified</span></div></td> <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 superusers or auth records with "Manage" access.</td>`,v=f(),w=i("tr"),w.innerHTML='<td colspan="3" class="txt-hint txt-bold">Other fields</td>',k(u,"class","inline-flex")},m(_,C){r(_,e,C),r(_,t,C),r(_,a,C),r(_,m,C),r(_,p,C),n(p,c),n(c,u),F.m(u,null),n(u,O),n(u,T),n(p,$),n(p,D),n(p,S),n(p,E),r(_,q,C),r(_,H,C),r(_,J,C),r(_,I,C),r(_,M,C),r(_,R,C),r(_,L,C),r(_,g,C),r(_,v,C),r(_,w,C)},p(_,C){W!==(W=Q(_,C))&&(F.d(1),F=W(_),F&&(F.c(),F.m(u,O)))},d(_){_&&(o(e),o(t),o(a),o(m),o(p),o(q),o(H),o(J),o(I),o(M),o(R),o(L),o(g),o(v),o(w)),F.d()}}}function Ft(d){let e;return{c(){e=i("span"),e.textContent="Optional",k(e,"class","label label-warning")},m(t,a){r(t,e,a)},d(t){t&&o(e)}}}function Bt(d){let e;return{c(){e=i("span"),e.textContent="Required",k(e,"class","label label-success")},m(t,a){r(t,e,a)},d(t){t&&o(e)}}}function Nt(d){let e;return{c(){e=i("span"),e.textContent="Optional",k(e,"class","label label-warning")},m(t,a){r(t,e,a)},d(t){t&&o(e)}}}function jt(d){let e;return{c(){e=i("span"),e.textContent="Required",k(e,"class","label label-success")},m(t,a){r(t,e,a)},d(t){t&&o(e)}}}function At(d){let e,t=d[15].maxSelect==1?"id":"ids",a,m;return{c(){e=h("Relation record "),a=h(t),m=h(".")},m(p,c){r(p,e,c),r(p,a,c),r(p,m,c)},p(p,c){c&64&&t!==(t=p[15].maxSelect==1?"id":"ids")&&te(a,t)},d(p){p&&(o(e),o(a),o(m))}}}function Et(d){let e,t,a,m,p;return{c(){e=h("File object."),t=i("br"),a=h(`
|
||||
Set to `),m=i("code"),m.textContent="null",p=h(" to delete already uploaded file(s).")},m(c,u){r(c,e,u),r(c,t,u),r(c,a,u),r(c,m,u),r(c,p,u)},p:de,d(c){c&&(o(e),o(t),o(a),o(m),o(p))}}}function It(d){let e;return{c(){e=h("URL address.")},m(t,a){r(t,e,a)},p:de,d(t){t&&o(e)}}}function Jt(d){let e;return{c(){e=h("Email address.")},m(t,a){r(t,e,a)},p:de,d(t){t&&o(e)}}}function Ut(d){let e;return{c(){e=h("JSON array or object.")},m(t,a){r(t,e,a)},p:de,d(t){t&&o(e)}}}function Vt(d){let e;return{c(){e=h("Number value.")},m(t,a){r(t,e,a)},p:de,d(t){t&&o(e)}}}function xt(d){let e;return{c(){e=h("Plain text value.")},m(t,a){r(t,e,a)},p:de,d(t){t&&o(e)}}}function vt(d,e){let t,a,m,p,c,u=e[15].name+"",b,O,T,$,D=x.getFieldValueType(e[15])+"",S,E,q,H;function J(v,w){return v[15].required?jt:Nt}let I=J(e),M=I(e);function R(v,w){if(v[15].type==="text")return xt;if(v[15].type==="number")return Vt;if(v[15].type==="json")return Ut;if(v[15].type==="email")return Jt;if(v[15].type==="url")return It;if(v[15].type==="file")return Et;if(v[15].type==="relation")return At}let L=R(e),g=L&&L(e);return{key:d,first:null,c(){t=i("tr"),a=i("td"),m=i("div"),M.c(),p=f(),c=i("span"),b=h(u),O=f(),T=i("td"),$=i("span"),S=h(D),E=f(),q=i("td"),g&&g.c(),H=f(),k(m,"class","inline-flex"),k($,"class","label"),this.first=t},m(v,w){r(v,t,w),n(t,a),n(a,m),M.m(m,null),n(m,p),n(m,c),n(c,b),n(t,O),n(t,T),n(T,$),n($,S),n(t,E),n(t,q),g&&g.m(q,null),n(t,H)},p(v,w){e=v,I!==(I=J(e))&&(M.d(1),M=I(e),M&&(M.c(),M.m(m,p))),w&64&&u!==(u=e[15].name+"")&&te(b,u),w&64&&D!==(D=x.getFieldValueType(e[15])+"")&&te(S,D),L===(L=R(e))&&g?g.p(e,w):(g&&g.d(1),g=L&&L(e),g&&(g.c(),g.m(q,null)))},d(v){v&&o(t),M.d(),g&&g.d()}}}function wt(d,e){let t,a=e[10].code+"",m,p,c,u;function b(){return e[9](e[10])}return{key:d,first:null,c(){t=i("button"),m=h(a),p=f(),k(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m(O,T){r(O,t,T),n(t,m),n(t,p),c||(u=Lt(t,"click",b),c=!0)},p(O,T){e=O,T&8&&a!==(a=e[10].code+"")&&te(m,a),T&12&&Te(t,"active",e[2]===e[10].code)},d(O){O&&o(t),c=!1,u()}}}function Ct(d,e){let t,a,m,p;return a=new Tt({props:{content:e[10].body}}),{key:d,first:null,c(){t=i("div"),Ce(a.$$.fragment),m=f(),k(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m(c,u){r(c,t,u),we(a,t,null),n(t,m),p=!0},p(c,u){e=c;const b={};u&8&&(b.content=e[10].body),a.$set(b),(!p||u&12)&&Te(t,"active",e[2]===e[10].code)},i(c){p||(ye(a.$$.fragment,c),p=!0)},o(c){he(a.$$.fragment,c),p=!1},d(c){c&&o(t),ve(a)}}}function Qt(d){var ct,ut;let e,t,a=d[0].name+"",m,p,c,u,b,O,T,$=d[0].name+"",D,S,E,q,H,J,I,M,R,L,g,v,w,Q,W,F,_,C,le,z=d[0].name+"",ne,Ue,Oe,Ve,Se,oe,$e,re,Me,ce,qe,K,Re,xe,X,De,U=[],Qe=new Map,He,ue,Le,Y,Pe,We,pe,Z,Fe,ze,Be,Ke,B,Xe,ae,Ye,Ze,Ge,Ne,et,je,tt,Ae,lt,nt,se,Ee,fe,Ie,G,be,V=[],at=new Map,st,me,N=[],it=new Map,ee,j=d[1]&&yt();R=new Mt({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${d[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example update data
|
||||
const data = ${JSON.stringify(Object.assign({},d[4],x.dummyCollectionSchemaData(d[0],!0)),null,4)};
|
||||
|
||||
const record = await pb.collection('${(ct=d[0])==null?void 0:ct.name}').update('RECORD_ID', data);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${d[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example update body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},d[4],x.dummyCollectionSchemaData(d[0],!0)),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ut=d[0])==null?void 0:ut.name}').update('RECORD_ID', body: body);
|
||||
`}});let A=d[7]&&kt(),P=d[1]&>(d),ke=ie(d[6]);const dt=l=>l[15].name;for(let l=0;l<ke.length;l+=1){let s=ht(d,ke,l),y=dt(s);Qe.set(y,U[l]=vt(y,s))}ae=new Tt({props:{content:"?expand=relField1,relField2.subRelField21"}}),se=new Pt({});let ge=ie(d[3]);const ot=l=>l[10].code;for(let l=0;l<ge.length;l+=1){let s=_t(d,ge,l),y=ot(s);at.set(y,V[l]=wt(y,s))}let _e=ie(d[3]);const rt=l=>l[10].code;for(let l=0;l<_e.length;l+=1){let s=mt(d,_e,l),y=rt(s);it.set(y,N[l]=Ct(y,s))}return{c(){e=i("h3"),t=h("Update ("),m=h(a),p=h(")"),c=f(),u=i("div"),b=i("p"),O=h("Update a single "),T=i("strong"),D=h($),S=h(" record."),E=f(),q=i("p"),q.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,H=f(),J=i("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>.`,I=f(),j&&j.c(),M=f(),Ce(R.$$.fragment),L=f(),g=i("h6"),g.textContent="API details",v=f(),w=i("div"),Q=i("strong"),Q.textContent="PATCH",W=f(),F=i("div"),_=i("p"),C=h("/api/collections/"),le=i("strong"),ne=h(z),Ue=h("/records/"),Oe=i("strong"),Oe.textContent=":id",Ve=f(),A&&A.c(),Se=f(),oe=i("div"),oe.textContent="Path parameters",$e=f(),re=i("table"),re.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 record to update.</td></tr></tbody>',Me=f(),ce=i("div"),ce.textContent="Body Parameters",qe=f(),K=i("table"),Re=i("thead"),Re.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',xe=f(),X=i("tbody"),P&&P.c(),De=f();for(let l=0;l<U.length;l+=1)U[l].c();He=f(),ue=i("div"),ue.textContent="Query parameters",Le=f(),Y=i("table"),Pe=i("thead"),Pe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',We=f(),pe=i("tbody"),Z=i("tr"),Fe=i("td"),Fe.textContent="expand",ze=f(),Be=i("td"),Be.innerHTML='<span class="label">String</span>',Ke=f(),B=i("td"),Xe=h(`Auto expand relations when returning the updated record. Ex.:
|
||||
`),Ce(ae.$$.fragment),Ye=h(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Ze=i("br"),Ge=h(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),Ne=i("code"),Ne.textContent="expand",et=h(" property (eg. "),je=i("code"),je.textContent='"expand": {"relField1": {...}, ...}',tt=h(`). Only
|
||||
the relations that the user has permissions to `),Ae=i("strong"),Ae.textContent="view",lt=h(" will be expanded."),nt=f(),Ce(se.$$.fragment),Ee=f(),fe=i("div"),fe.textContent="Responses",Ie=f(),G=i("div"),be=i("div");for(let l=0;l<V.length;l+=1)V[l].c();st=f(),me=i("div");for(let l=0;l<N.length;l+=1)N[l].c();k(e,"class","m-b-sm"),k(u,"class","content txt-lg m-b-sm"),k(g,"class","m-b-xs"),k(Q,"class","label label-primary"),k(F,"class","content"),k(w,"class","alert alert-warning"),k(oe,"class","section-title"),k(re,"class","table-compact table-border m-b-base"),k(ce,"class","section-title"),k(K,"class","table-compact table-border m-b-base"),k(ue,"class","section-title"),k(Y,"class","table-compact table-border m-b-lg"),k(fe,"class","section-title"),k(be,"class","tabs-header compact combined left"),k(me,"class","tabs-content"),k(G,"class","tabs")},m(l,s){r(l,e,s),n(e,t),n(e,m),n(e,p),r(l,c,s),r(l,u,s),n(u,b),n(b,O),n(b,T),n(T,D),n(b,S),n(u,E),n(u,q),n(u,H),n(u,J),n(u,I),j&&j.m(u,null),r(l,M,s),we(R,l,s),r(l,L,s),r(l,g,s),r(l,v,s),r(l,w,s),n(w,Q),n(w,W),n(w,F),n(F,_),n(_,C),n(_,le),n(le,ne),n(_,Ue),n(_,Oe),n(w,Ve),A&&A.m(w,null),r(l,Se,s),r(l,oe,s),r(l,$e,s),r(l,re,s),r(l,Me,s),r(l,ce,s),r(l,qe,s),r(l,K,s),n(K,Re),n(K,xe),n(K,X),P&&P.m(X,null),n(X,De);for(let y=0;y<U.length;y+=1)U[y]&&U[y].m(X,null);r(l,He,s),r(l,ue,s),r(l,Le,s),r(l,Y,s),n(Y,Pe),n(Y,We),n(Y,pe),n(pe,Z),n(Z,Fe),n(Z,ze),n(Z,Be),n(Z,Ke),n(Z,B),n(B,Xe),we(ae,B,null),n(B,Ye),n(B,Ze),n(B,Ge),n(B,Ne),n(B,et),n(B,je),n(B,tt),n(B,Ae),n(B,lt),n(pe,nt),we(se,pe,null),r(l,Ee,s),r(l,fe,s),r(l,Ie,s),r(l,G,s),n(G,be);for(let y=0;y<V.length;y+=1)V[y]&&V[y].m(be,null);n(G,st),n(G,me);for(let y=0;y<N.length;y+=1)N[y]&&N[y].m(me,null);ee=!0},p(l,[s]){var pt,ft;(!ee||s&1)&&a!==(a=l[0].name+"")&&te(m,a),(!ee||s&1)&&$!==($=l[0].name+"")&&te(D,$),l[1]?j||(j=yt(),j.c(),j.m(u,null)):j&&(j.d(1),j=null);const y={};s&49&&(y.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${l[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example update data
|
||||
const data = ${JSON.stringify(Object.assign({},l[4],x.dummyCollectionSchemaData(l[0],!0)),null,4)};
|
||||
|
||||
const record = await pb.collection('${(pt=l[0])==null?void 0:pt.name}').update('RECORD_ID', data);
|
||||
`),s&49&&(y.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${l[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example update body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},l[4],x.dummyCollectionSchemaData(l[0],!0)),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ft=l[0])==null?void 0:ft.name}').update('RECORD_ID', body: body);
|
||||
`),R.$set(y),(!ee||s&1)&&z!==(z=l[0].name+"")&&te(ne,z),l[7]?A||(A=kt(),A.c(),A.m(w,null)):A&&(A.d(1),A=null),l[1]?P?P.p(l,s):(P=gt(l),P.c(),P.m(X,De)):P&&(P.d(1),P=null),s&64&&(ke=ie(l[6]),U=Je(U,s,dt,1,l,ke,Qe,X,bt,vt,null,ht)),s&12&&(ge=ie(l[3]),V=Je(V,s,ot,1,l,ge,at,be,bt,wt,null,_t)),s&12&&(_e=ie(l[3]),qt(),N=Je(N,s,rt,1,l,_e,it,me,Rt,Ct,null,mt),Dt())},i(l){if(!ee){ye(R.$$.fragment,l),ye(ae.$$.fragment,l),ye(se.$$.fragment,l);for(let s=0;s<_e.length;s+=1)ye(N[s]);ee=!0}},o(l){he(R.$$.fragment,l),he(ae.$$.fragment,l),he(se.$$.fragment,l);for(let s=0;s<N.length;s+=1)he(N[s]);ee=!1},d(l){l&&(o(e),o(c),o(u),o(M),o(L),o(g),o(v),o(w),o(Se),o(oe),o($e),o(re),o(Me),o(ce),o(qe),o(K),o(He),o(ue),o(Le),o(Y),o(Ee),o(fe),o(Ie),o(G)),j&&j.d(),ve(R,l),A&&A.d(),P&&P.d();for(let s=0;s<U.length;s+=1)U[s].d();ve(ae),ve(se);for(let s=0;s<V.length;s+=1)V[s].d();for(let s=0;s<N.length;s+=1)N[s].d()}}}const Wt=d=>d.name=="emailVisibility";function zt(d,e,t){let a,m,p,c,u,{collection:b}=e,O=200,T=[],$={};const D=S=>t(2,O=S.code);return d.$$set=S=>{"collection"in S&&t(0,b=S.collection)},d.$$.update=()=>{var S,E,q;d.$$.dirty&1&&t(1,a=(b==null?void 0:b.type)==="auth"),d.$$.dirty&1&&t(7,m=(b==null?void 0:b.updateRule)===null),d.$$.dirty&2&&t(8,p=a?["id","password","verified","email","emailVisibility"]:["id"]),d.$$.dirty&257&&t(6,c=((S=b==null?void 0:b.fields)==null?void 0:S.filter(H=>!H.hidden&&H.type!="autodate"&&!p.includes(H.name)))||[]),d.$$.dirty&1&&t(3,T=[{code:200,body:JSON.stringify(x.dummyCollectionRecord(b),null,2)},{code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Failed to update record.",
|
||||
"data": {
|
||||
"${(q=(E=b==null?void 0:b.fields)==null?void 0:E[0])==null?void 0:q.name}": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"status": 403,
|
||||
"message": "You are not allowed to perform this request.",
|
||||
"data": {}
|
||||
}
|
||||
`},{code:404,body:`
|
||||
{
|
||||
"status": 404,
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}]),d.$$.dirty&2&&(a?t(4,$={password:"87654321",passwordConfirm:"87654321",oldPassword:"12345678"}):t(4,$={}))},t(5,u=x.getApiExampleUrl(Ht.baseURL)),[b,a,O,T,$,u,c,m,p,D]}class Yt extends Ot{constructor(e){super(),St(this,e,zt,Qt,$t,{collection:0})}}export{Yt as default};
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import{S as $t,i as Mt,s as St,V as Ot,X as se,W as Tt,h as d,d as ge,t as _e,a as he,I as ee,Z as Je,_ as bt,C as qt,$ as Rt,D as Ht,l as o,n as a,m as we,u as s,A as _,v as f,c as Ce,w as k,J as ye,p as Pt,k as Te,o as Lt,H as te}from"./index-rRnVk674.js";import{F as Dt}from"./FieldsQueryParam-BEgqkb0I.js";function mt(r,e,t){const n=r.slice();return n[10]=e[t],n}function _t(r,e,t){const n=r.slice();return n[10]=e[t],n}function ht(r,e,t){const n=r.slice();return n[15]=e[t],n}function yt(r){let e;return{c(){e=s("p"),e.innerHTML=`<em>Note that in case of a password change all previously issued tokens for the current record
|
||||
will be automatically invalidated and if you want your user to remain signed in you need to
|
||||
reauthenticate manually after the update call.</em>`},m(t,n){o(t,e,n)},d(t){t&&d(e)}}}function kt(r){let e;return{c(){e=s("p"),e.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",k(e,"class","txt-hint txt-sm txt-right")},m(t,n){o(t,e,n)},d(t){t&&d(e)}}}function vt(r){let e,t,n,b,p,c,u,m,S,T,H,P,$,M,q,L,J,j,O,R,D,v,g,w;function x(h,C){var le,W,ne;return C&1&&(m=null),m==null&&(m=!!((ne=(W=(le=h[0])==null?void 0:le.fields)==null?void 0:W.find(zt))!=null&&ne.required)),m?Bt:Ft}let Q=x(r,-1),B=Q(r);return{c(){e=s("tr"),e.innerHTML='<td colspan="3" class="txt-hint txt-bold">Auth specific fields</td>',t=f(),n=s("tr"),n.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>email</span></div></td> <td><span class="label">String</span></td> <td>The auth record email address.
|
||||
<br/>
|
||||
This field can be updated only by superusers or auth records with "Manage" access.
|
||||
<br/>
|
||||
Regular accounts can update their email by calling "Request email change".</td>`,b=f(),p=s("tr"),c=s("td"),u=s("div"),B.c(),S=f(),T=s("span"),T.textContent="emailVisibility",H=f(),P=s("td"),P.innerHTML='<span class="label">Boolean</span>',$=f(),M=s("td"),M.textContent="Whether to show/hide the auth record email when fetching the record data.",q=f(),L=s("tr"),L.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>oldPassword</span></div></td> <td><span class="label">String</span></td> <td>Old auth record password.
|
||||
<br/>
|
||||
This field is required only when changing the record password. Superusers and auth records
|
||||
with "Manage" access can skip this field.</td>`,J=f(),j=s("tr"),j.innerHTML='<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>New auth record password.</td>',O=f(),R=s("tr"),R.innerHTML='<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>passwordConfirm</span></div></td> <td><span class="label">String</span></td> <td>New auth record password confirmation.</td>',D=f(),v=s("tr"),v.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>verified</span></div></td> <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 superusers or auth records with "Manage" access.</td>`,g=f(),w=s("tr"),w.innerHTML='<td colspan="3" class="txt-hint txt-bold">Other fields</td>',k(u,"class","inline-flex")},m(h,C){o(h,e,C),o(h,t,C),o(h,n,C),o(h,b,C),o(h,p,C),a(p,c),a(c,u),B.m(u,null),a(u,S),a(u,T),a(p,H),a(p,P),a(p,$),a(p,M),o(h,q,C),o(h,L,C),o(h,J,C),o(h,j,C),o(h,O,C),o(h,R,C),o(h,D,C),o(h,v,C),o(h,g,C),o(h,w,C)},p(h,C){Q!==(Q=x(h,C))&&(B.d(1),B=Q(h),B&&(B.c(),B.m(u,S)))},d(h){h&&(d(e),d(t),d(n),d(b),d(p),d(q),d(L),d(J),d(j),d(O),d(R),d(D),d(v),d(g),d(w)),B.d()}}}function Ft(r){let e;return{c(){e=s("span"),e.textContent="Optional",k(e,"class","label label-warning")},m(t,n){o(t,e,n)},d(t){t&&d(e)}}}function Bt(r){let e;return{c(){e=s("span"),e.textContent="Required",k(e,"class","label label-success")},m(t,n){o(t,e,n)},d(t){t&&d(e)}}}function Nt(r){let e;return{c(){e=s("span"),e.textContent="Optional",k(e,"class","label label-warning")},m(t,n){o(t,e,n)},d(t){t&&d(e)}}}function At(r){let e;return{c(){e=s("span"),e.textContent="Required",k(e,"class","label label-success")},m(t,n){o(t,e,n)},d(t){t&&d(e)}}}function Et(r){let e,t=r[15].maxSelect==1?"id":"ids",n,b;return{c(){e=_("Relation record "),n=_(t),b=_(".")},m(p,c){o(p,e,c),o(p,n,c),o(p,b,c)},p(p,c){c&32&&t!==(t=p[15].maxSelect==1?"id":"ids")&&ee(n,t)},d(p){p&&(d(e),d(n),d(b))}}}function It(r){let e,t,n,b,p;return{c(){e=_("File object."),t=s("br"),n=_(`
|
||||
Set to `),b=s("code"),b.textContent="null",p=_(" to delete already uploaded file(s).")},m(c,u){o(c,e,u),o(c,t,u),o(c,n,u),o(c,b,u),o(c,p,u)},p:te,d(c){c&&(d(e),d(t),d(n),d(b),d(p))}}}function jt(r){let e,t;return{c(){e=s("code"),e.textContent='{"lon":x,"lat":y}',t=_(" object.")},m(n,b){o(n,e,b),o(n,t,b)},p:te,d(n){n&&(d(e),d(t))}}}function Jt(r){let e;return{c(){e=_("URL address.")},m(t,n){o(t,e,n)},p:te,d(t){t&&d(e)}}}function Ut(r){let e;return{c(){e=_("Email address.")},m(t,n){o(t,e,n)},p:te,d(t){t&&d(e)}}}function Vt(r){let e;return{c(){e=_("JSON array or object.")},m(t,n){o(t,e,n)},p:te,d(t){t&&d(e)}}}function xt(r){let e;return{c(){e=_("Number value.")},m(t,n){o(t,e,n)},p:te,d(t){t&&d(e)}}}function Qt(r){let e;return{c(){e=_("Plain text value.")},m(t,n){o(t,e,n)},p:te,d(t){t&&d(e)}}}function gt(r,e){let t,n,b,p,c,u=e[15].name+"",m,S,T,H,P=ye.getFieldValueType(e[15])+"",$,M,q,L;function J(g,w){return g[15].required?At:Nt}let j=J(e),O=j(e);function R(g,w){if(g[15].type==="text")return Qt;if(g[15].type==="number")return xt;if(g[15].type==="json")return Vt;if(g[15].type==="email")return Ut;if(g[15].type==="url")return Jt;if(g[15].type==="geoPoint")return jt;if(g[15].type==="file")return It;if(g[15].type==="relation")return Et}let D=R(e),v=D&&D(e);return{key:r,first:null,c(){t=s("tr"),n=s("td"),b=s("div"),O.c(),p=f(),c=s("span"),m=_(u),S=f(),T=s("td"),H=s("span"),$=_(P),M=f(),q=s("td"),v&&v.c(),L=f(),k(b,"class","inline-flex"),k(H,"class","label"),this.first=t},m(g,w){o(g,t,w),a(t,n),a(n,b),O.m(b,null),a(b,p),a(b,c),a(c,m),a(t,S),a(t,T),a(T,H),a(H,$),a(t,M),a(t,q),v&&v.m(q,null),a(t,L)},p(g,w){e=g,j!==(j=J(e))&&(O.d(1),O=j(e),O&&(O.c(),O.m(b,p))),w&32&&u!==(u=e[15].name+"")&&ee(m,u),w&32&&P!==(P=ye.getFieldValueType(e[15])+"")&&ee($,P),D===(D=R(e))&&v?v.p(e,w):(v&&v.d(1),v=D&&D(e),v&&(v.c(),v.m(q,null)))},d(g){g&&d(t),O.d(),v&&v.d()}}}function wt(r,e){let t,n=e[10].code+"",b,p,c,u;function m(){return e[9](e[10])}return{key:r,first:null,c(){t=s("button"),b=_(n),p=f(),k(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m(S,T){o(S,t,T),a(t,b),a(t,p),c||(u=Lt(t,"click",m),c=!0)},p(S,T){e=S,T&8&&n!==(n=e[10].code+"")&&ee(b,n),T&12&&Te(t,"active",e[2]===e[10].code)},d(S){S&&d(t),c=!1,u()}}}function Ct(r,e){let t,n,b,p;return n=new Tt({props:{content:e[10].body}}),{key:r,first:null,c(){t=s("div"),Ce(n.$$.fragment),b=f(),k(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m(c,u){o(c,t,u),we(n,t,null),a(t,b),p=!0},p(c,u){e=c;const m={};u&8&&(m.content=e[10].body),n.$set(m),(!p||u&12)&&Te(t,"active",e[2]===e[10].code)},i(c){p||(he(n.$$.fragment,c),p=!0)},o(c){_e(n.$$.fragment,c),p=!1},d(c){c&&d(t),ge(n)}}}function Wt(r){var ct,ut;let e,t,n=r[0].name+"",b,p,c,u,m,S,T,H=r[0].name+"",P,$,M,q,L,J,j,O,R,D,v,g,w,x,Q,B,h,C,le,W=r[0].name+"",ne,Ue,$e,Ve,Me,de,Se,oe,Oe,re,qe,z,Re,xe,K,He,U=[],Qe=new Map,Pe,ce,Le,X,De,We,ue,Y,Fe,ze,Be,Ke,N,Xe,ae,Ye,Ze,Ge,Ne,et,Ae,tt,Ee,lt,nt,ie,Ie,pe,je,Z,fe,V=[],at=new Map,it,be,A=[],st=new Map,G,E=r[1]&&yt();R=new Ot({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example update data
|
||||
const data = ${JSON.stringify(r[7](r[0]),null,4)};
|
||||
|
||||
const record = await pb.collection('${(ct=r[0])==null?void 0:ct.name}').update('RECORD_ID', data);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${r[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example update body
|
||||
final body = <String, dynamic>${JSON.stringify(r[7](r[0]),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ut=r[0])==null?void 0:ut.name}').update('RECORD_ID', body: body);
|
||||
`}});let I=r[6]&&kt(),F=r[1]&&vt(r),ke=se(r[5]);const dt=l=>l[15].name;for(let l=0;l<ke.length;l+=1){let i=ht(r,ke,l),y=dt(i);Qe.set(y,U[l]=gt(y,i))}ae=new Tt({props:{content:"?expand=relField1,relField2.subRelField21"}}),ie=new Dt({});let ve=se(r[3]);const ot=l=>l[10].code;for(let l=0;l<ve.length;l+=1){let i=_t(r,ve,l),y=ot(i);at.set(y,V[l]=wt(y,i))}let me=se(r[3]);const rt=l=>l[10].code;for(let l=0;l<me.length;l+=1){let i=mt(r,me,l),y=rt(i);st.set(y,A[l]=Ct(y,i))}return{c(){e=s("h3"),t=_("Update ("),b=_(n),p=_(")"),c=f(),u=s("div"),m=s("p"),S=_("Update a single "),T=s("strong"),P=_(H),$=_(" record."),M=f(),q=s("p"),q.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,L=f(),J=s("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>.`,j=f(),E&&E.c(),O=f(),Ce(R.$$.fragment),D=f(),v=s("h6"),v.textContent="API details",g=f(),w=s("div"),x=s("strong"),x.textContent="PATCH",Q=f(),B=s("div"),h=s("p"),C=_("/api/collections/"),le=s("strong"),ne=_(W),Ue=_("/records/"),$e=s("strong"),$e.textContent=":id",Ve=f(),I&&I.c(),Me=f(),de=s("div"),de.textContent="Path parameters",Se=f(),oe=s("table"),oe.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 record to update.</td></tr></tbody>',Oe=f(),re=s("div"),re.textContent="Body Parameters",qe=f(),z=s("table"),Re=s("thead"),Re.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',xe=f(),K=s("tbody"),F&&F.c(),He=f();for(let l=0;l<U.length;l+=1)U[l].c();Pe=f(),ce=s("div"),ce.textContent="Query parameters",Le=f(),X=s("table"),De=s("thead"),De.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',We=f(),ue=s("tbody"),Y=s("tr"),Fe=s("td"),Fe.textContent="expand",ze=f(),Be=s("td"),Be.innerHTML='<span class="label">String</span>',Ke=f(),N=s("td"),Xe=_(`Auto expand relations when returning the updated record. Ex.:
|
||||
`),Ce(ae.$$.fragment),Ye=_(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Ze=s("br"),Ge=_(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),Ne=s("code"),Ne.textContent="expand",et=_(" property (eg. "),Ae=s("code"),Ae.textContent='"expand": {"relField1": {...}, ...}',tt=_(`). Only
|
||||
the relations that the user has permissions to `),Ee=s("strong"),Ee.textContent="view",lt=_(" will be expanded."),nt=f(),Ce(ie.$$.fragment),Ie=f(),pe=s("div"),pe.textContent="Responses",je=f(),Z=s("div"),fe=s("div");for(let l=0;l<V.length;l+=1)V[l].c();it=f(),be=s("div");for(let l=0;l<A.length;l+=1)A[l].c();k(e,"class","m-b-sm"),k(u,"class","content txt-lg m-b-sm"),k(v,"class","m-b-xs"),k(x,"class","label label-primary"),k(B,"class","content"),k(w,"class","alert alert-warning"),k(de,"class","section-title"),k(oe,"class","table-compact table-border m-b-base"),k(re,"class","section-title"),k(z,"class","table-compact table-border m-b-base"),k(ce,"class","section-title"),k(X,"class","table-compact table-border m-b-lg"),k(pe,"class","section-title"),k(fe,"class","tabs-header compact combined left"),k(be,"class","tabs-content"),k(Z,"class","tabs")},m(l,i){o(l,e,i),a(e,t),a(e,b),a(e,p),o(l,c,i),o(l,u,i),a(u,m),a(m,S),a(m,T),a(T,P),a(m,$),a(u,M),a(u,q),a(u,L),a(u,J),a(u,j),E&&E.m(u,null),o(l,O,i),we(R,l,i),o(l,D,i),o(l,v,i),o(l,g,i),o(l,w,i),a(w,x),a(w,Q),a(w,B),a(B,h),a(h,C),a(h,le),a(le,ne),a(h,Ue),a(h,$e),a(w,Ve),I&&I.m(w,null),o(l,Me,i),o(l,de,i),o(l,Se,i),o(l,oe,i),o(l,Oe,i),o(l,re,i),o(l,qe,i),o(l,z,i),a(z,Re),a(z,xe),a(z,K),F&&F.m(K,null),a(K,He);for(let y=0;y<U.length;y+=1)U[y]&&U[y].m(K,null);o(l,Pe,i),o(l,ce,i),o(l,Le,i),o(l,X,i),a(X,De),a(X,We),a(X,ue),a(ue,Y),a(Y,Fe),a(Y,ze),a(Y,Be),a(Y,Ke),a(Y,N),a(N,Xe),we(ae,N,null),a(N,Ye),a(N,Ze),a(N,Ge),a(N,Ne),a(N,et),a(N,Ae),a(N,tt),a(N,Ee),a(N,lt),a(ue,nt),we(ie,ue,null),o(l,Ie,i),o(l,pe,i),o(l,je,i),o(l,Z,i),a(Z,fe);for(let y=0;y<V.length;y+=1)V[y]&&V[y].m(fe,null);a(Z,it),a(Z,be);for(let y=0;y<A.length;y+=1)A[y]&&A[y].m(be,null);G=!0},p(l,[i]){var pt,ft;(!G||i&1)&&n!==(n=l[0].name+"")&&ee(b,n),(!G||i&1)&&H!==(H=l[0].name+"")&&ee(P,H),l[1]?E||(E=yt(),E.c(),E.m(u,null)):E&&(E.d(1),E=null);const y={};i&17&&(y.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${l[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example update data
|
||||
const data = ${JSON.stringify(l[7](l[0]),null,4)};
|
||||
|
||||
const record = await pb.collection('${(pt=l[0])==null?void 0:pt.name}').update('RECORD_ID', data);
|
||||
`),i&17&&(y.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${l[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example update body
|
||||
final body = <String, dynamic>${JSON.stringify(l[7](l[0]),null,2)};
|
||||
|
||||
final record = await pb.collection('${(ft=l[0])==null?void 0:ft.name}').update('RECORD_ID', body: body);
|
||||
`),R.$set(y),(!G||i&1)&&W!==(W=l[0].name+"")&&ee(ne,W),l[6]?I||(I=kt(),I.c(),I.m(w,null)):I&&(I.d(1),I=null),l[1]?F?F.p(l,i):(F=vt(l),F.c(),F.m(K,He)):F&&(F.d(1),F=null),i&32&&(ke=se(l[5]),U=Je(U,i,dt,1,l,ke,Qe,K,bt,gt,null,ht)),i&12&&(ve=se(l[3]),V=Je(V,i,ot,1,l,ve,at,fe,bt,wt,null,_t)),i&12&&(me=se(l[3]),qt(),A=Je(A,i,rt,1,l,me,st,be,Rt,Ct,null,mt),Ht())},i(l){if(!G){he(R.$$.fragment,l),he(ae.$$.fragment,l),he(ie.$$.fragment,l);for(let i=0;i<me.length;i+=1)he(A[i]);G=!0}},o(l){_e(R.$$.fragment,l),_e(ae.$$.fragment,l),_e(ie.$$.fragment,l);for(let i=0;i<A.length;i+=1)_e(A[i]);G=!1},d(l){l&&(d(e),d(c),d(u),d(O),d(D),d(v),d(g),d(w),d(Me),d(de),d(Se),d(oe),d(Oe),d(re),d(qe),d(z),d(Pe),d(ce),d(Le),d(X),d(Ie),d(pe),d(je),d(Z)),E&&E.d(),ge(R,l),I&&I.d(),F&&F.d();for(let i=0;i<U.length;i+=1)U[i].d();ge(ae),ge(ie);for(let i=0;i<V.length;i+=1)V[i].d();for(let i=0;i<A.length;i+=1)A[i].d()}}}const zt=r=>r.name=="emailVisibility";function Kt(r,e,t){let n,b,p,c,u,{collection:m}=e,S=200,T=[];function H($){let M=ye.dummyCollectionSchemaData($,!0);return n&&(M.oldPassword="12345678",M.password="87654321",M.passwordConfirm="87654321",delete M.verified,delete M.email),M}const P=$=>t(2,S=$.code);return r.$$set=$=>{"collection"in $&&t(0,m=$.collection)},r.$$.update=()=>{var $,M,q;r.$$.dirty&1&&t(1,n=(m==null?void 0:m.type)==="auth"),r.$$.dirty&1&&t(6,b=(m==null?void 0:m.updateRule)===null),r.$$.dirty&2&&t(8,p=n?["id","password","verified","email","emailVisibility"]:["id"]),r.$$.dirty&257&&t(5,c=(($=m==null?void 0:m.fields)==null?void 0:$.filter(L=>!L.hidden&&L.type!="autodate"&&!p.includes(L.name)))||[]),r.$$.dirty&1&&t(3,T=[{code:200,body:JSON.stringify(ye.dummyCollectionRecord(m),null,2)},{code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Failed to update record.",
|
||||
"data": {
|
||||
"${(q=(M=m==null?void 0:m.fields)==null?void 0:M[0])==null?void 0:q.name}": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"status": 403,
|
||||
"message": "You are not allowed to perform this request.",
|
||||
"data": {}
|
||||
}
|
||||
`},{code:404,body:`
|
||||
{
|
||||
"status": 404,
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}])},t(4,u=ye.getApiExampleUrl(Pt.baseURL)),[m,n,S,T,u,c,b,H,p,P]}class Zt extends $t{constructor(e){super(),Mt(this,e,Kt,Wt,St,{collection:0})}}export{Zt as default};
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{S as le,i as ne,s as ie,X as F,h as b,t as j,a as U,I as Y,Z as x,_ as Te,C as ee,$ as Ce,D as te,l as h,n as u,u as m,v as y,A as M,w as v,k as K,o as oe,W as qe,d as z,m as G,c as Q,V as Ve,Y as fe,J as Ae,p as Ie,a0 as ue}from"./index-ChG9Kpfq.js";function de(s,t,e){const o=s.slice();return o[4]=t[e],o}function me(s,t,e){const o=s.slice();return o[4]=t[e],o}function pe(s,t){let e,o=t[4].code+"",f,c,r,a;function d(){return t[3](t[4])}return{key:s,first:null,c(){e=m("button"),f=M(o),c=y(),v(e,"class","tab-item"),K(e,"active",t[1]===t[4].code),this.first=e},m(g,C){h(g,e,C),u(e,f),u(e,c),r||(a=oe(e,"click",d),r=!0)},p(g,C){t=g,C&4&&o!==(o=t[4].code+"")&&Y(f,o),C&6&&K(e,"active",t[1]===t[4].code)},d(g){g&&b(e),r=!1,a()}}}function _e(s,t){let e,o,f,c;return o=new qe({props:{content:t[4].body}}),{key:s,first:null,c(){e=m("div"),Q(o.$$.fragment),f=y(),v(e,"class","tab-item"),K(e,"active",t[1]===t[4].code),this.first=e},m(r,a){h(r,e,a),G(o,e,null),u(e,f),c=!0},p(r,a){t=r;const d={};a&4&&(d.content=t[4].body),o.$set(d),(!c||a&6)&&K(e,"active",t[1]===t[4].code)},i(r){c||(U(o.$$.fragment,r),c=!0)},o(r){j(o.$$.fragment,r),c=!1},d(r){r&&b(e),z(o)}}}function Pe(s){let t,e,o,f,c,r,a,d=s[0].name+"",g,C,D,P,L,R,B,O,N,q,V,$=[],J=new Map,H,I,p=[],T=new Map,A,_=F(s[2]);const X=l=>l[4].code;for(let l=0;l<_.length;l+=1){let i=me(s,_,l),n=X(i);J.set(n,$[l]=pe(n,i))}let E=F(s[2]);const W=l=>l[4].code;for(let l=0;l<E.length;l+=1){let i=de(s,E,l),n=W(i);T.set(n,p[l]=_e(n,i))}return{c(){t=m("div"),e=m("strong"),e.textContent="POST",o=y(),f=m("div"),c=m("p"),r=M("/api/collections/"),a=m("strong"),g=M(d),C=M("/confirm-verification"),D=y(),P=m("div"),P.textContent="Body Parameters",L=y(),R=m("table"),R.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>',B=y(),O=m("div"),O.textContent="Responses",N=y(),q=m("div"),V=m("div");for(let l=0;l<$.length;l+=1)$[l].c();H=y(),I=m("div");for(let l=0;l<p.length;l+=1)p[l].c();v(e,"class","label label-primary"),v(f,"class","content"),v(t,"class","alert alert-success"),v(P,"class","section-title"),v(R,"class","table-compact table-border m-b-base"),v(O,"class","section-title"),v(V,"class","tabs-header compact combined left"),v(I,"class","tabs-content"),v(q,"class","tabs")},m(l,i){h(l,t,i),u(t,e),u(t,o),u(t,f),u(f,c),u(c,r),u(c,a),u(a,g),u(c,C),h(l,D,i),h(l,P,i),h(l,L,i),h(l,R,i),h(l,B,i),h(l,O,i),h(l,N,i),h(l,q,i),u(q,V);for(let n=0;n<$.length;n+=1)$[n]&&$[n].m(V,null);u(q,H),u(q,I);for(let n=0;n<p.length;n+=1)p[n]&&p[n].m(I,null);A=!0},p(l,[i]){(!A||i&1)&&d!==(d=l[0].name+"")&&Y(g,d),i&6&&(_=F(l[2]),$=x($,i,X,1,l,_,J,V,Te,pe,null,me)),i&6&&(E=F(l[2]),ee(),p=x(p,i,W,1,l,E,T,I,Ce,_e,null,de),te())},i(l){if(!A){for(let i=0;i<E.length;i+=1)U(p[i]);A=!0}},o(l){for(let i=0;i<p.length;i+=1)j(p[i]);A=!1},d(l){l&&(b(t),b(D),b(P),b(L),b(R),b(B),b(O),b(N),b(q));for(let i=0;i<$.length;i+=1)$[i].d();for(let i=0;i<p.length;i+=1)p[i].d()}}}function Re(s,t,e){let{collection:o}=t,f=204,c=[];const r=a=>e(1,f=a.code);return s.$$set=a=>{"collection"in a&&e(0,o=a.collection)},e(2,c=[{code:204,body:"null"},{code:400,body:`
|
||||
import{S as le,i as ne,s as ie,X as F,h as b,t as j,a as U,I as Y,Z as x,_ as Te,C as ee,$ as Ce,D as te,l as h,n as u,u as m,v as y,A as M,w as v,k as K,o as oe,W as qe,d as z,m as G,c as Q,V as Ve,Y as fe,J as Ae,p as Ie,a0 as ue}from"./index-rRnVk674.js";function de(s,t,e){const o=s.slice();return o[4]=t[e],o}function me(s,t,e){const o=s.slice();return o[4]=t[e],o}function pe(s,t){let e,o=t[4].code+"",f,c,r,a;function d(){return t[3](t[4])}return{key:s,first:null,c(){e=m("button"),f=M(o),c=y(),v(e,"class","tab-item"),K(e,"active",t[1]===t[4].code),this.first=e},m(g,C){h(g,e,C),u(e,f),u(e,c),r||(a=oe(e,"click",d),r=!0)},p(g,C){t=g,C&4&&o!==(o=t[4].code+"")&&Y(f,o),C&6&&K(e,"active",t[1]===t[4].code)},d(g){g&&b(e),r=!1,a()}}}function _e(s,t){let e,o,f,c;return o=new qe({props:{content:t[4].body}}),{key:s,first:null,c(){e=m("div"),Q(o.$$.fragment),f=y(),v(e,"class","tab-item"),K(e,"active",t[1]===t[4].code),this.first=e},m(r,a){h(r,e,a),G(o,e,null),u(e,f),c=!0},p(r,a){t=r;const d={};a&4&&(d.content=t[4].body),o.$set(d),(!c||a&6)&&K(e,"active",t[1]===t[4].code)},i(r){c||(U(o.$$.fragment,r),c=!0)},o(r){j(o.$$.fragment,r),c=!1},d(r){r&&b(e),z(o)}}}function Pe(s){let t,e,o,f,c,r,a,d=s[0].name+"",g,C,D,P,L,R,B,O,N,q,V,$=[],J=new Map,H,I,p=[],T=new Map,A,_=F(s[2]);const X=l=>l[4].code;for(let l=0;l<_.length;l+=1){let i=me(s,_,l),n=X(i);J.set(n,$[l]=pe(n,i))}let E=F(s[2]);const W=l=>l[4].code;for(let l=0;l<E.length;l+=1){let i=de(s,E,l),n=W(i);T.set(n,p[l]=_e(n,i))}return{c(){t=m("div"),e=m("strong"),e.textContent="POST",o=y(),f=m("div"),c=m("p"),r=M("/api/collections/"),a=m("strong"),g=M(d),C=M("/confirm-verification"),D=y(),P=m("div"),P.textContent="Body Parameters",L=y(),R=m("table"),R.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>',B=y(),O=m("div"),O.textContent="Responses",N=y(),q=m("div"),V=m("div");for(let l=0;l<$.length;l+=1)$[l].c();H=y(),I=m("div");for(let l=0;l<p.length;l+=1)p[l].c();v(e,"class","label label-primary"),v(f,"class","content"),v(t,"class","alert alert-success"),v(P,"class","section-title"),v(R,"class","table-compact table-border m-b-base"),v(O,"class","section-title"),v(V,"class","tabs-header compact combined left"),v(I,"class","tabs-content"),v(q,"class","tabs")},m(l,i){h(l,t,i),u(t,e),u(t,o),u(t,f),u(f,c),u(c,r),u(c,a),u(a,g),u(c,C),h(l,D,i),h(l,P,i),h(l,L,i),h(l,R,i),h(l,B,i),h(l,O,i),h(l,N,i),h(l,q,i),u(q,V);for(let n=0;n<$.length;n+=1)$[n]&&$[n].m(V,null);u(q,H),u(q,I);for(let n=0;n<p.length;n+=1)p[n]&&p[n].m(I,null);A=!0},p(l,[i]){(!A||i&1)&&d!==(d=l[0].name+"")&&Y(g,d),i&6&&(_=F(l[2]),$=x($,i,X,1,l,_,J,V,Te,pe,null,me)),i&6&&(E=F(l[2]),ee(),p=x(p,i,W,1,l,E,T,I,Ce,_e,null,de),te())},i(l){if(!A){for(let i=0;i<E.length;i+=1)U(p[i]);A=!0}},o(l){for(let i=0;i<p.length;i+=1)j(p[i]);A=!1},d(l){l&&(b(t),b(D),b(P),b(L),b(R),b(B),b(O),b(N),b(q));for(let i=0;i<$.length;i+=1)$[i].d();for(let i=0;i<p.length;i+=1)p[i].d()}}}function Re(s,t,e){let{collection:o}=t,f=204,c=[];const r=a=>e(1,f=a.code);return s.$$set=a=>{"collection"in a&&e(0,o=a.collection)},e(2,c=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user