mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-09-20 13:30:50 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb2aa1cfc6 | ||
|
|
e1528aedac | ||
|
|
22b0a2b586 | ||
|
|
0ca86a0c87 | ||
|
|
b2c8f394af | ||
|
|
56b2641469 | ||
|
|
f266621a0f | ||
|
|
ca136c5dc1 | ||
|
|
abfe18bcce | ||
|
|
f3fc7f78d7 | ||
|
|
26fd069d11 | ||
|
|
62bde9e1f3 | ||
|
|
b945cd2fdf | ||
|
|
89a0520f7d | ||
|
|
a88b3c5db3 | ||
|
|
a37d9cfb84 | ||
|
|
5b084bbbfd | ||
|
|
322508f6d1 | ||
|
|
78e70bd52b | ||
|
|
58401459bf | ||
|
|
f172754775 | ||
|
|
31670ab3e1 | ||
|
|
baacf4913b | ||
|
|
8a94ccea42 | ||
|
|
b2bab9573a | ||
|
|
e5b5c1f76f | ||
|
|
ccb1c42220 | ||
|
|
a394777264 | ||
|
|
7d10b3c502 | ||
|
|
64ffb308bb | ||
|
|
916c74c218 | ||
|
|
17974d534e | ||
|
|
bde7a86b30 | ||
|
|
f7f8f09336 | ||
|
|
2a6b891a9b | ||
|
|
b4cb35483b | ||
|
|
1606cfd6e2 | ||
|
|
08f97ef0bb | ||
|
|
0dc263a40c | ||
|
|
824031e1a4 | ||
|
|
311bc74b7e | ||
|
|
4f3d1682de | ||
|
|
18728732b9 | ||
|
|
bb4f27cfb5 | ||
|
|
d423acad3b | ||
|
|
ef73052546 | ||
|
|
c89c68a4dc | ||
|
|
02495554cf | ||
|
|
cdbe6d78d3 | ||
|
|
ff6904f1f8 | ||
|
|
bc0222dcb4 | ||
|
|
04826ba588 | ||
|
|
6ca1f5c431 | ||
|
|
2863763a27 | ||
|
|
9c0d952543 | ||
|
|
ed4f7c7358 | ||
|
|
49f1c869c0 | ||
|
|
5e6949062f | ||
|
|
dc063b20fa | ||
|
|
f42bbfd927 | ||
|
|
f0af24d78f | ||
|
|
26fd3d48df | ||
|
|
864bbe7e12 | ||
|
|
1e995552c8 | ||
|
|
6baae97b5d | ||
|
|
bcfbbc53f8 | ||
|
|
8a916cd636 | ||
|
|
75f58a28ac | ||
|
|
e87ef431c5 | ||
|
|
b2ac538580 | ||
|
|
53b20ec104 | ||
|
|
c8ef3c4050 | ||
|
|
9113d30103 | ||
|
|
fef6e584b7 | ||
|
|
67fa47b1bb | ||
|
|
5f21c4119f | ||
|
|
734f35c504 | ||
|
|
038ae8f803 | ||
|
|
2236288e57 | ||
|
|
8f10c66160 | ||
|
|
5960dc5f2d | ||
|
|
cbf1215bb1 | ||
|
|
1b633720be | ||
|
|
adb5d6e998 | ||
|
|
3841946b61 | ||
|
|
369e2703c2 | ||
|
|
4a45ad91fa | ||
|
|
265dac45ce | ||
|
|
f152787578 | ||
|
|
640623f4fd | ||
|
|
1aff89f377 | ||
|
|
7d6b12a4ef | ||
|
|
7a3223e415 | ||
|
|
bd18688f35 | ||
|
|
f90da96820 | ||
|
|
6c8f2d2cd6 | ||
|
|
5e84305922 |
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: '>=1.20.3'
|
||||
go-version: '>=1.21.1'
|
||||
|
||||
# This step usually is not needed because the /ui/dist is pregenerated locally
|
||||
# but its here to ensure that each release embeds the latest admin ui artifacts.
|
||||
|
||||
+1
-8
@@ -7,6 +7,7 @@ before:
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
# used only for tests
|
||||
- id: build_cgo
|
||||
main: ./examples/base
|
||||
binary: pocketbase
|
||||
@@ -46,14 +47,6 @@ release:
|
||||
draft: true
|
||||
|
||||
archives:
|
||||
- id: archive_cgo
|
||||
builds: [build_cgo]
|
||||
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}_cgo'
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE*
|
||||
- CHANGELOG*
|
||||
|
||||
- id: archive_noncgo
|
||||
builds: [build_noncgo]
|
||||
format: zip
|
||||
|
||||
+209
-6
@@ -1,8 +1,211 @@
|
||||
## v0.18.4
|
||||
|
||||
- Added escape character (`\`) support in the Admin UI to allow using `select` field values with comma ([#2197](https://github.com/pocketbase/pocketbase/discussions/2197)).
|
||||
|
||||
|
||||
## v0.18.3
|
||||
|
||||
- Exposed a global JSVM `readerToString(reader)` helper function to allow reading Go `io.Reader` values ([#3273](https://github.com/pocketbase/pocketbase/discussions/3273)).
|
||||
|
||||
- Bumped the GitHub action to use [min Go 1.21.1](https://github.com/golang/go/issues?q=milestone%3AGo1.21.1+label%3ACherryPickApproved) for the prebuilt executable since it contains some minor `html/template` and `net/http` security fixes.
|
||||
|
||||
|
||||
## v0.18.2
|
||||
|
||||
- Prevent breaking the record form in the Admin UI in case the browser's localStorage quota has been exceeded when uploading or storing large `editor` values ([#3265](https://github.com/pocketbase/pocketbase/issues/3265)).
|
||||
|
||||
- Updated docs and missing JSVM typings.
|
||||
|
||||
- Exposed additional crypto primitives under the `$security.*` JSVM namespace ([#3273](https://github.com/pocketbase/pocketbase/discussions/3273)):
|
||||
```js
|
||||
// HMAC with SHA256
|
||||
$security.hs256("hello", "secret")
|
||||
|
||||
// HMAC with SHA512
|
||||
$security.hs512("hello", "secret")
|
||||
|
||||
// compare 2 strings with a constant time
|
||||
$security.equal(hash1, hash2)
|
||||
```
|
||||
|
||||
|
||||
## v0.18.1
|
||||
|
||||
- Excluded the local temp dir from the backups ([#3261](https://github.com/pocketbase/pocketbase/issues/3261)).
|
||||
|
||||
|
||||
## v0.18.0
|
||||
|
||||
- Simplified the `serve` command to accept domain name(s) as argument to reduce any additional manual hosts setup that sometimes previously was needed when deploying on production ([#3190](https://github.com/pocketbase/pocketbase/discussions/3190)).
|
||||
```sh
|
||||
./pocketbase serve yourdomain.com
|
||||
```
|
||||
|
||||
- Added `fields` wildcard (`*`) support.
|
||||
|
||||
- Added option to upload a backup file from the Admin UI ([#2599](https://github.com/pocketbase/pocketbase/issues/2599)).
|
||||
|
||||
- Registered a custom Deflate compressor to speedup (_nearly 2-3x_) the backups generation for the sake of a small zip size increase.
|
||||
_Based on several local tests, `pb_data` of ~500MB (from which ~350MB+ are several hundred small files) results in a ~280MB zip generated for ~11s (previously it resulted in ~250MB zip but for ~35s)._
|
||||
|
||||
- Added the application name as part of the autogenerated backup name for easier identification ([#3066](https://github.com/pocketbase/pocketbase/issues/3066)).
|
||||
|
||||
- Added new `SmtpConfig.LocalName` option to specify a custom domain name (or IP address) for the initial EHLO/HELO exchange ([#3097](https://github.com/pocketbase/pocketbase/discussions/3097)).
|
||||
_This is usually required for verification purposes only by some SMTP providers, such as on-premise [Gmail SMTP-relay](https://support.google.com/a/answer/2956491)._
|
||||
|
||||
- Added `NoDecimal` `number` field option.
|
||||
|
||||
- `editor` field improvements:
|
||||
- Added new "Strip urls domain" option to allow controlling the default TinyMCE urls behavior (_default to `false` for new content_).
|
||||
- Normalized pasted text while still preserving links, lists, tables, etc. formatting ([#3257](https://github.com/pocketbase/pocketbase/issues/3257)).
|
||||
|
||||
- Added option to auto generate admin and auth record passwords from the Admin UI.
|
||||
|
||||
- Added JSON validation and syntax highlight for the `json` field in the Admin UI ([#3191](https://github.com/pocketbase/pocketbase/issues/3191)).
|
||||
|
||||
- Added datetime filter macros:
|
||||
```
|
||||
// all macros are UTC based
|
||||
@second - @now second number (0-59)
|
||||
@minute - @now minute number (0-59)
|
||||
@hour - @now hour number (0-23)
|
||||
@weekday - @now weekday number (0-6)
|
||||
@day - @now day number
|
||||
@month - @now month number
|
||||
@year - @now year number
|
||||
@todayStart - beginning of the current day as datetime string
|
||||
@todayEnd - end of the current day as datetime string
|
||||
@monthStart - beginning of the current month as datetime string
|
||||
@monthEnd - end of the current month as datetime string
|
||||
@yearStart - beginning of the current year as datetime string
|
||||
@yearEnd - end of the current year as datetime string
|
||||
```
|
||||
|
||||
- Added cron expression macros ([#3132](https://github.com/pocketbase/pocketbase/issues/3132)):
|
||||
```
|
||||
@yearly - "0 0 1 1 *"
|
||||
@annually - "0 0 1 1 *"
|
||||
@monthly - "0 0 1 * *"
|
||||
@weekly - "0 0 * * 0"
|
||||
@daily - "0 0 * * *"
|
||||
@midnight - "0 0 * * *"
|
||||
@hourly - "0 * * * *"
|
||||
```
|
||||
|
||||
- ⚠️ Added offset argument `Dao.FindRecordsByFilter(collection, filter, sort, limit, offset, [params...])`.
|
||||
_If you don't need an offset, you can set it to `0`._
|
||||
|
||||
- To minimize the footguns with `Dao.FindFirstRecordByFilter()` and `Dao.FindRecordsByFilter()`, the functions now supports an optional placeholder params argument that is safe to be populated with untrusted user input.
|
||||
The placeholders are in the same format as when binding regular SQL parameters.
|
||||
```go
|
||||
// unsanitized and untrusted filter variables
|
||||
status := "..."
|
||||
author := "..."
|
||||
|
||||
app.Dao().FindFirstRecordByFilter("articles", "status={:status} && author={:author}", dbx.Params{
|
||||
"status": status,
|
||||
"author": author,
|
||||
})
|
||||
|
||||
app.Dao().FindRecordsByFilter("articles", "status={:status} && author={:author}", "-created", 10, 0, dbx.Params{
|
||||
"status": status,
|
||||
"author": author,
|
||||
})
|
||||
```
|
||||
|
||||
- Added JSVM `$mails.*` binds for the corresponding Go [mails package](https://pkg.go.dev/github.com/pocketbase/pocketbase/mails) functions.
|
||||
|
||||
- Added JSVM helper crypto primitives under the `$security.*` namespace:
|
||||
```js
|
||||
$security.md5(text)
|
||||
$security.sha256(text)
|
||||
$security.sha512(text)
|
||||
```
|
||||
|
||||
- ⚠️ Deprecated `RelationOptions.DisplayFields` in favor of the new `SchemaField.Presentable` option to avoid the duplication when a single collection is referenced more than once and/or by multiple other collections.
|
||||
|
||||
- ⚠️ Fill the `LastVerificationSentAt` and `LastResetSentAt` fields only after a successfull email send ([#3121](https://github.com/pocketbase/pocketbase/issues/3121)).
|
||||
|
||||
- ⚠️ Skip API `fields` json transformations for non 20x responses ([#3176](https://github.com/pocketbase/pocketbase/issues/3176)).
|
||||
|
||||
- ⚠️ Changes to `tests.ApiScenario` struct:
|
||||
|
||||
- The `ApiScenario.AfterTestFunc` now receive as 3rd argument `*http.Response` pointer instead of `*echo.Echo` as the latter is not really useful in this context.
|
||||
```go
|
||||
// old
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo)
|
||||
|
||||
// new
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response)
|
||||
```
|
||||
|
||||
- The `ApiScenario.TestAppFactory` now accept the test instance as argument and no longer expect an error as return result ([#3025](https://github.com/pocketbase/pocketbase/discussions/3025#discussioncomment-6592272)).
|
||||
```go
|
||||
// old
|
||||
TestAppFactory: func() (*tests.TestApp, error)
|
||||
|
||||
// new
|
||||
TestAppFactory: func(t *testing.T) *tests.TestApp
|
||||
```
|
||||
_Returning a `nil` app instance from the factory results in test failure. You can enforce a custom test failure by calling `t.Fatal(err)` inside the factory._
|
||||
|
||||
- Bumped the min required TLS version to 1.2 in order to improve the cert reputation score.
|
||||
|
||||
- Reduced the default JSVM prewarmed pool size to 25 to reduce the initial memory consumptions (_you can manually adjust the pool size with `--hooksPool=50` if you need to, but the default should suffice for most cases_).
|
||||
|
||||
- Update `gocloud.dev` dependency to v0.34 and explicitly set the new `NoTempDir` fileblob option to prevent the cross-device link error introduced with v0.33.
|
||||
|
||||
- Other minor Admin UI and docs improvements.
|
||||
|
||||
|
||||
## v0.17.7
|
||||
|
||||
- Fixed the autogenerated `down` migrations to properly revert the old collection rules in case a change was made in `up` ([#3192](https://github.com/pocketbase/pocketbase/pull/3192); thanks @impact-merlinmarek).
|
||||
_Existing `down` migrations can't be fixed but that should be ok as usually the `down` migrations are rarely used against prod environments since they can cause data loss and, while not ideal, the previous old behavior of always setting the rules to `null/nil` is safer than not updating the rules at all._
|
||||
|
||||
- Updated some Go deps.
|
||||
|
||||
|
||||
## v0.17.6
|
||||
|
||||
- Fixed JSVM `require()` file path error when using Windows-style path delimiters ([#3163](https://github.com/pocketbase/pocketbase/issues/3163#issuecomment-1685034438)).
|
||||
|
||||
|
||||
## v0.17.5
|
||||
|
||||
- Added quotes around the wrapped view query columns introduced with v0.17.4.
|
||||
|
||||
|
||||
## v0.17.4
|
||||
|
||||
- Fixed Views record retrieval when numeric id is used ([#3110](https://github.com/pocketbase/pocketbase/issues/3110)).
|
||||
_With this fix we also now properly recognize `CAST(... as TEXT)` and `CAST(... as BOOLEAN)` as `text` and `bool` fields._
|
||||
|
||||
- Fixed `relation` "Cascade delete" tooltip message ([#3098](https://github.com/pocketbase/pocketbase/issues/3098)).
|
||||
|
||||
- Fixed jsvm error message prefix on failed migrations ([#3103](https://github.com/pocketbase/pocketbase/pull/3103); thanks @nzhenev).
|
||||
|
||||
- Disabled the initial Admin UI admins counter cache when there are no initial admins to allow detecting externally created accounts (eg. with the `admin` command) ([#3106](https://github.com/pocketbase/pocketbase/issues/3106)).
|
||||
|
||||
- Downgraded `google/go-cloud` dependency to v0.32.0 until v0.34.0 is released to prevent the `os.TempDir` `cross-device link` errors as too many users complained about it.
|
||||
|
||||
|
||||
## v0.17.3
|
||||
|
||||
- Fixed Docker `cross-device link` error when creating `pb_data` backups on a local mounted volume ([#3089](https://github.com/pocketbase/pocketbase/issues/3089)).
|
||||
|
||||
- Fixed the error messages for relation to views ([#3090](https://github.com/pocketbase/pocketbase/issues/3090)).
|
||||
|
||||
- Always reserve space for the scrollbar to reduce the layout shifts in the Admin UI records listing due to the deprecated `overflow: overlay`.
|
||||
|
||||
- Enabled lazy loading for the Admin UI thumb images.
|
||||
|
||||
|
||||
## v0.17.2
|
||||
|
||||
- Soft-deprecated `$http.send({ data: object, ... })` in favour of `$http.send({ body: rawString, ... })`
|
||||
to allow sending non-JSON body with the request ([#3058](https://github.com/pocketbase/pocketbase/discussions/3058)).
|
||||
The existing `data` prop will still work, but it will be recommended to use `body` instead (_to send JSON you can use `JSON.stringify(...)` as body value_).
|
||||
The existing `data` prop will still work, but it is recommended to use `body` instead (_to send JSON you can use `JSON.stringify(...)` as body value_).
|
||||
|
||||
- Added `core.RealtimeConnectEvent.IdleTimeout` field to allow specifying a different realtime idle timeout duration per client basis ([#3054](https://github.com/pocketbase/pocketbase/discussions/3054)).
|
||||
|
||||
@@ -696,7 +899,7 @@
|
||||
|
||||
_Note2: The old index (`"field.0":null`) and filename (`"field.filename.png":null`) based suffixed syntax for deleting files is still supported._
|
||||
|
||||
- ! Added support for multi-match/match-all request data and collection multi-valued fields (`select`, `relation`) conditions.
|
||||
- ⚠️ Added support for multi-match/match-all request data and collection multi-valued fields (`select`, `relation`) conditions.
|
||||
If you want a "at least one of" type of condition, you can prefix the operator with `?`.
|
||||
```js
|
||||
// for each someRelA.someRelB record require the "status" field to be "active"
|
||||
@@ -776,7 +979,7 @@
|
||||
|
||||
## v0.10.3
|
||||
|
||||
- ! Renamed the metadata key `original_filename` to `original-filename` due to an S3 file upload error caused by the underscore character ([#1343](https://github.com/pocketbase/pocketbase/pull/1343); thanks @yuxiang-gao).
|
||||
- ⚠️ Renamed the metadata key `original_filename` to `original-filename` due to an S3 file upload error caused by the underscore character ([#1343](https://github.com/pocketbase/pocketbase/pull/1343); thanks @yuxiang-gao).
|
||||
|
||||
- Fixed request verification docs api url ([#1332](https://github.com/pocketbase/pocketbase/pull/1332); thanks @JoyMajumdar2001)
|
||||
|
||||
@@ -812,7 +1015,7 @@
|
||||
|
||||
- Refactored the `core.app.Bootstrap()` to be called before starting the cobra commands ([#1267](https://github.com/pocketbase/pocketbase/discussions/1267)).
|
||||
|
||||
- ! Changed `pocketbase.NewWithConfig(config Config)` to `pocketbase.NewWithConfig(config *Config)` and added 4 new config settings:
|
||||
- ⚠️ Changed `pocketbase.NewWithConfig(config Config)` to `pocketbase.NewWithConfig(config *Config)` and added 4 new config settings:
|
||||
```go
|
||||
DataMaxOpenConns int // default to core.DefaultDataMaxOpenConns
|
||||
DataMaxIdleConns int // default to core.DefaultDataMaxIdleConns
|
||||
@@ -822,9 +1025,9 @@
|
||||
|
||||
- Added new helper method `core.App.IsBootstrapped()` to check the current app bootstrap state.
|
||||
|
||||
- ! Changed `core.NewBaseApp(dir, encryptionEnv, isDebug)` to `NewBaseApp(config *BaseAppConfig)`.
|
||||
- ⚠️ Changed `core.NewBaseApp(dir, encryptionEnv, isDebug)` to `NewBaseApp(config *BaseAppConfig)`.
|
||||
|
||||
- ! Removed `rest.UploadedFile` struct (see below `filesystem.File`).
|
||||
- ⚠️ Removed `rest.UploadedFile` struct (see below `filesystem.File`).
|
||||
|
||||
- Added generic file resource struct that allows loading and uploading file content from
|
||||
different sources (at the moment multipart/form-data requests and from the local filesystem).
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/forms"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
"github.com/pocketbase/pocketbase/tools/rest"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -24,6 +26,7 @@ func bindBackupApi(app core.App, rg *echo.Group) {
|
||||
subGroup := rg.Group("/backups", ActivityLogger(app))
|
||||
subGroup.GET("", api.list, RequireAdminAuth())
|
||||
subGroup.POST("", api.create, RequireAdminAuth())
|
||||
subGroup.POST("/upload", api.upload, RequireAdminAuth())
|
||||
subGroup.GET("/:key", api.download)
|
||||
subGroup.DELETE("/:key", api.delete, RequireAdminAuth())
|
||||
subGroup.POST("/:key/restore", api.restore, RequireAdminAuth())
|
||||
@@ -88,6 +91,28 @@ func (api *backupApi) create(c echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (api *backupApi) upload(c echo.Context) error {
|
||||
files, err := rest.FindUploadedFiles(c.Request(), "file")
|
||||
if err != nil {
|
||||
return NewBadRequestError("Missing or invalid uploaded file.", err)
|
||||
}
|
||||
|
||||
form := forms.NewBackupUpload(api.app)
|
||||
form.File = files[0]
|
||||
|
||||
return form.Submit(func(next forms.InterceptorNextFunc[*filesystem.File]) forms.InterceptorNextFunc[*filesystem.File] {
|
||||
return func(file *filesystem.File) error {
|
||||
if err := next(file); err != nil {
|
||||
return NewBadRequestError("Failed to upload backup.", err)
|
||||
}
|
||||
|
||||
// we don't retrieve the generated backup file because it may not be
|
||||
// available yet due to the eventually consistent nature of some S3 providers
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (api *backupApi) download(c echo.Context) error {
|
||||
fileToken := c.QueryParam("token")
|
||||
|
||||
|
||||
+148
-12
@@ -1,7 +1,11 @@
|
||||
package apis_test
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -85,7 +89,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
Name: "unauthorized",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/backups",
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
@@ -98,7 +102,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
@@ -114,7 +118,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
BeforeTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
app.Cache().Set(core.CacheKeyActiveBackup, "")
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
@@ -127,7 +131,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
files, err := getBackupFiles(app)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -152,7 +156,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
@@ -169,7 +173,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
files, err := getBackupFiles(app)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -193,6 +197,138 @@ func TestBackupsCreate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupsUpload(t *testing.T) {
|
||||
// create dummy form data bodies
|
||||
type body struct {
|
||||
buffer io.Reader
|
||||
contentType string
|
||||
}
|
||||
bodies := make([]body, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
func() {
|
||||
zb := new(bytes.Buffer)
|
||||
zw := zip.NewWriter(zb)
|
||||
if err := zw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
mw := multipart.NewWriter(b)
|
||||
|
||||
mfw, err := mw.CreateFormFile("file", "test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := io.Copy(mfw, zb); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mw.Close()
|
||||
|
||||
bodies[i] = body{
|
||||
buffer: b,
|
||||
contentType: mw.FormDataContentType(),
|
||||
}
|
||||
}()
|
||||
}
|
||||
// ---
|
||||
|
||||
scenarios := []tests.ApiScenario{
|
||||
{
|
||||
Name: "unauthorized",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/backups/upload",
|
||||
Body: bodies[0].buffer,
|
||||
RequestHeaders: map[string]string{
|
||||
"Content-Type": bodies[0].contentType,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
},
|
||||
{
|
||||
Name: "authorized as auth record",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/backups/upload",
|
||||
Body: bodies[1].buffer,
|
||||
RequestHeaders: map[string]string{
|
||||
"Content-Type": bodies[1].contentType,
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoUmVjb3JkIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyMjA4OTg1MjYxfQ.UwD8JvkbQtXpymT09d7J6fdA0aP9g4FJ1GPh_ggEkzc",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
},
|
||||
{
|
||||
Name: "authorized as admin (missing file)",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/backups/upload",
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{`},
|
||||
},
|
||||
{
|
||||
Name: "authorized as admin (existing backup name)",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/backups/upload",
|
||||
Body: bodies[3].buffer,
|
||||
RequestHeaders: map[string]string{
|
||||
"Content-Type": bodies[3].contentType,
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
BeforeTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
fsys, err := app.NewBackupsFilesystem()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fsys.Close()
|
||||
// create a dummy backup file to simulate existing backups
|
||||
if err := fsys.Upload([]byte("123"), "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
files, _ := getBackupFiles(app)
|
||||
if total := len(files); total != 1 {
|
||||
t.Fatalf("Expected %d backup file, got %d", 1, total)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{"file":{`},
|
||||
},
|
||||
{
|
||||
Name: "authorized as admin (valid file)",
|
||||
Method: http.MethodPost,
|
||||
Url: "/api/backups/upload",
|
||||
Body: bodies[4].buffer,
|
||||
RequestHeaders: map[string]string{
|
||||
"Content-Type": bodies[4].contentType,
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
files, _ := getBackupFiles(app)
|
||||
if total := len(files); total != 1 {
|
||||
t.Fatalf("Expected %d backup file, got %d", 1, total)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario.Test(t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupsDownload(t *testing.T) {
|
||||
scenarios := []tests.ApiScenario{
|
||||
{
|
||||
@@ -371,7 +507,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
noTestBackupFilesChanges(t, app)
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
@@ -389,7 +525,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
noTestBackupFilesChanges(t, app)
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
@@ -407,7 +543,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
noTestBackupFilesChanges(t, app)
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
@@ -428,7 +564,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
// mock active backup with the same name to delete
|
||||
app.Cache().Set(core.CacheKeyActiveBackup, "test1.zip")
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
noTestBackupFilesChanges(t, app)
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
@@ -449,7 +585,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
// mock active backup with different name
|
||||
app.Cache().Set(core.CacheKeyActiveBackup, "new.zip")
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
files, err := getBackupFiles(app)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -481,7 +617,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
files, err := getBackupFiles(app)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
+15
-13
@@ -207,15 +207,15 @@ func uiCacheControl() echo.MiddlewareFunc {
|
||||
}
|
||||
}
|
||||
|
||||
const totalAdminsCacheKey = "@totalAdmins"
|
||||
const hasAdminsCacheKey = "@hasAdmins"
|
||||
|
||||
func updateTotalAdminsCache(app core.App) error {
|
||||
func updateHasAdminsCache(app core.App) error {
|
||||
total, err := app.Dao().TotalAdmins()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app.Cache().Set(totalAdminsCacheKey, total)
|
||||
app.Cache().Set(hasAdminsCacheKey, total > 0)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -223,12 +223,13 @@ func updateTotalAdminsCache(app core.App) error {
|
||||
// installerRedirect redirects the user to the installer admin UI page
|
||||
// when the application needs some preliminary configurations to be done.
|
||||
func installerRedirect(app core.App) echo.MiddlewareFunc {
|
||||
// keep totalAdminsCacheKey value up-to-date
|
||||
// keep hasAdminsCacheKey value up-to-date
|
||||
app.OnAdminAfterCreateRequest().Add(func(data *core.AdminCreateEvent) error {
|
||||
return updateTotalAdminsCache(app)
|
||||
return updateHasAdminsCache(app)
|
||||
})
|
||||
|
||||
app.OnAdminAfterDeleteRequest().Add(func(data *core.AdminDeleteEvent) error {
|
||||
return updateTotalAdminsCache(app)
|
||||
return updateHasAdminsCache(app)
|
||||
})
|
||||
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
@@ -239,23 +240,24 @@ func installerRedirect(app core.App) echo.MiddlewareFunc {
|
||||
return next(c)
|
||||
}
|
||||
|
||||
// load into cache (if not already)
|
||||
if !app.Cache().Has(totalAdminsCacheKey) {
|
||||
if err := updateTotalAdminsCache(app); err != nil {
|
||||
hasAdmins := cast.ToBool(app.Cache().Get(hasAdminsCacheKey))
|
||||
|
||||
if !hasAdmins {
|
||||
// update the cache to make sure that the admin wasn't created by another process
|
||||
if err := updateHasAdminsCache(app); err != nil {
|
||||
return err
|
||||
}
|
||||
hasAdmins = cast.ToBool(app.Cache().Get(hasAdminsCacheKey))
|
||||
}
|
||||
|
||||
totalAdmins := cast.ToInt(app.Cache().Get(totalAdminsCacheKey))
|
||||
|
||||
_, hasInstallerParam := c.Request().URL.Query()["installer"]
|
||||
|
||||
if totalAdmins == 0 && !hasInstallerParam {
|
||||
if !hasAdmins && !hasInstallerParam {
|
||||
// redirect to the installer page
|
||||
return c.Redirect(http.StatusTemporaryRedirect, "?installer#")
|
||||
}
|
||||
|
||||
if totalAdmins != 0 && hasInstallerParam {
|
||||
if hasAdmins && hasInstallerParam {
|
||||
// clear the installer param
|
||||
return c.Redirect(http.StatusTemporaryRedirect, "?")
|
||||
}
|
||||
|
||||
+19
-101
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
)
|
||||
@@ -47,7 +46,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
ExpectedContent: []string{
|
||||
`"page":1`,
|
||||
`"perPage":30`,
|
||||
`"totalItems":10`,
|
||||
`"totalItems":11`,
|
||||
`"items":[{`,
|
||||
`"id":"_pb_users_auth_"`,
|
||||
`"id":"v851q4r790rhknl"`,
|
||||
@@ -57,6 +56,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
`"id":"wzlqyes4orhoygb"`,
|
||||
`"id":"4d1blo5cuycfaca"`,
|
||||
`"id":"9n89pl5vkct6330"`,
|
||||
`"id":"ib3m2700k5hlsjz"`,
|
||||
`"type":"auth"`,
|
||||
`"type":"base"`,
|
||||
},
|
||||
@@ -75,9 +75,9 @@ func TestCollectionsList(t *testing.T) {
|
||||
ExpectedContent: []string{
|
||||
`"page":2`,
|
||||
`"perPage":2`,
|
||||
`"totalItems":10`,
|
||||
`"totalItems":11`,
|
||||
`"items":[{`,
|
||||
`"id":"kpv709sk2lqbqk8"`,
|
||||
`"id":"v9gwnfh02gjq1q0"`,
|
||||
`"id":"9n89pl5vkct6330"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -245,7 +245,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
"OnCollectionBeforeDeleteRequest": 1,
|
||||
"OnCollectionAfterDeleteRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureDeletedFiles(app, "9n89pl5vkct6330")
|
||||
},
|
||||
},
|
||||
@@ -264,7 +264,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
"OnCollectionBeforeDeleteRequest": 1,
|
||||
"OnCollectionAfterDeleteRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureDeletedFiles(app, "9n89pl5vkct6330")
|
||||
},
|
||||
},
|
||||
@@ -400,7 +400,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
`"name":"new"`,
|
||||
`"type":"base"`,
|
||||
`"system":false`,
|
||||
`"schema":[{"system":false,"id":"12345789","name":"test","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
|
||||
`"schema":[{"system":false,"id":"12345789","name":"test","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
|
||||
`"options":{}`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -424,7 +424,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
`"name":"new"`,
|
||||
`"type":"auth"`,
|
||||
`"system":false`,
|
||||
`"schema":[{"system":false,"id":"12345789","name":"test","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
|
||||
`"schema":[{"system":false,"id":"12345789","name":"test","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
|
||||
`"options":{"allowEmailAuth":false,"allowOAuth2Auth":false,"allowUsernameAuth":false,"exceptEmailDomains":null,"manageRule":null,"minPasswordLength":0,"onlyEmailDomains":null,"requireEmail":false}`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -693,7 +693,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
"OnCollectionBeforeCreateRequest": 1,
|
||||
"OnCollectionAfterCreateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
indexes, err := app.Dao().TableIndexes("new")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -823,7 +823,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
"OnCollectionBeforeUpdateRequest": 1,
|
||||
"OnCollectionAfterUpdateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
// check if the record table was renamed
|
||||
if !app.Dao().HasTable("new") {
|
||||
t.Fatal("Couldn't find record table 'new'.")
|
||||
@@ -959,88 +959,6 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
|
||||
// rel field change displayFields propagation
|
||||
// -----------------------------------------------------------
|
||||
{
|
||||
Name: "renaming a display field should also update the referenced displayFields value",
|
||||
Method: http.MethodPatch,
|
||||
Url: "/api/collections/demo3",
|
||||
Body: strings.NewReader(`{
|
||||
"schema":[
|
||||
{
|
||||
"id": "w5z2x0nq",
|
||||
"type": "text",
|
||||
"name": "title_change"
|
||||
}
|
||||
]
|
||||
}`),
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"name":"title_change"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelBeforeUpdate": 2,
|
||||
"OnModelAfterUpdate": 2,
|
||||
"OnCollectionBeforeUpdateRequest": 1,
|
||||
"OnCollectionAfterUpdateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
collection, err := app.Dao().FindCollectionByNameOrId("demo4")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
relField := collection.Schema.GetFieldByName("rel_many_no_cascade_required")
|
||||
options := relField.Options.(*schema.RelationOptions)
|
||||
expectedDisplayFields := []string{"title_change", "id"}
|
||||
if len(list.SubtractSlice(options.DisplayFields, expectedDisplayFields)) != 0 {
|
||||
t.Fatalf("Expected displayFields %v, got %v", expectedDisplayFields, options.DisplayFields)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "deleting a display field should also update the referenced displayFields value",
|
||||
Method: http.MethodPatch,
|
||||
Url: "/api/collections/demo3",
|
||||
Body: strings.NewReader(`{
|
||||
"schema":[
|
||||
{
|
||||
"type": "text",
|
||||
"name": "new_field"
|
||||
}
|
||||
]
|
||||
}`),
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"name":"new_field"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnModelBeforeUpdate": 2,
|
||||
"OnModelAfterUpdate": 2,
|
||||
"OnCollectionBeforeUpdateRequest": 1,
|
||||
"OnCollectionAfterUpdateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
collection, err := app.Dao().FindCollectionByNameOrId("demo4")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
relField := collection.Schema.GetFieldByName("rel_many_no_cascade_required")
|
||||
options := relField.Options.(*schema.RelationOptions)
|
||||
expectedDisplayFields := []string{"id"}
|
||||
if len(list.SubtractSlice(options.DisplayFields, expectedDisplayFields)) != 0 {
|
||||
t.Fatalf("Expected displayFields %v, got %v", expectedDisplayFields, options.DisplayFields)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// view
|
||||
// -----------------------------------------------------------
|
||||
{
|
||||
@@ -1142,7 +1060,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
"OnCollectionBeforeUpdateRequest": 1,
|
||||
"OnCollectionAfterUpdateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
indexes, err := app.Dao().TableIndexes("new")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1164,7 +1082,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCollectionsImport(t *testing.T) {
|
||||
totalCollections := 10
|
||||
totalCollections := 11
|
||||
|
||||
scenarios := []tests.ApiScenario{
|
||||
{
|
||||
@@ -1197,7 +1115,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
`"data":{`,
|
||||
`"collections":{"code":"validation_required"`,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
collections := []*models.Collection{}
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1225,7 +1143,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
"OnCollectionsBeforeImportRequest": 1,
|
||||
"OnModelBeforeDelete": 4,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
collections := []*models.Collection{}
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1267,7 +1185,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
"OnCollectionsBeforeImportRequest": 1,
|
||||
"OnModelBeforeCreate": 2,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
collections := []*models.Collection{}
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1323,7 +1241,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
"OnModelBeforeCreate": 3,
|
||||
"OnModelAfterCreate": 3,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
collections := []*models.Collection{}
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1421,14 +1339,14 @@ func TestCollectionsImport(t *testing.T) {
|
||||
ExpectedEvents: map[string]int{
|
||||
"OnCollectionsAfterImportRequest": 1,
|
||||
"OnCollectionsBeforeImportRequest": 1,
|
||||
"OnModelBeforeDelete": 8,
|
||||
"OnModelAfterDelete": 8,
|
||||
"OnModelBeforeDelete": 9,
|
||||
"OnModelAfterDelete": 9,
|
||||
"OnModelBeforeUpdate": 2,
|
||||
"OnModelAfterUpdate": 2,
|
||||
"OnModelBeforeCreate": 1,
|
||||
"OnModelAfterCreate": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
collections := []*models.Collection{}
|
||||
if err := app.Dao().CollectionQuery().All(&collections); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
+5
-3
@@ -287,8 +287,10 @@ func ActivityLogger(app core.App) echo.MiddlewareFunc {
|
||||
return func(c echo.Context) error {
|
||||
err := next(c)
|
||||
|
||||
logsMaxDays := app.Settings().Logs.MaxDays
|
||||
|
||||
// no logs retention
|
||||
if app.Settings().Logs.MaxDays == 0 {
|
||||
if logsMaxDays == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -348,8 +350,8 @@ func ActivityLogger(app core.App) echo.MiddlewareFunc {
|
||||
lastLogsDeletedAt := cast.ToTime(app.Cache().Get("lastLogsDeletedAt"))
|
||||
daysDiff := now.Sub(lastLogsDeletedAt).Hours() * 24
|
||||
|
||||
if daysDiff > float64(app.Settings().Logs.MaxDays) {
|
||||
deleteErr := app.LogsDao().DeleteOldRequests(now.AddDate(0, 0, -1*app.Settings().Logs.MaxDays))
|
||||
if daysDiff > float64(logsMaxDays) {
|
||||
deleteErr := app.LogsDao().DeleteOldRequests(now.AddDate(0, 0, -1*logsMaxDays))
|
||||
if deleteErr == nil {
|
||||
app.Cache().Set("lastLogsDeletedAt", now)
|
||||
} else if app.IsDebug() {
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestRealtimeConnect(t *testing.T) {
|
||||
"OnRealtimeAfterMessageSend": 1,
|
||||
"OnRealtimeDisconnectRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
if len(app.SubscriptionsBroker().Clients()) != 0 {
|
||||
t.Errorf("Expected the subscribers to be removed after connection close, found %d", len(app.SubscriptionsBroker().Clients()))
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func TestRealtimeConnect(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
if len(app.SubscriptionsBroker().Clients()) != 0 {
|
||||
t.Errorf("Expected the subscribers to be removed after connection close, found %d", len(app.SubscriptionsBroker().Clients()))
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func TestRealtimeConnect(t *testing.T) {
|
||||
return hook.StopPropagation
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
if len(app.SubscriptionsBroker().Clients()) != 0 {
|
||||
t.Errorf("Expected the subscribers to be removed after connection close, found %d", len(app.SubscriptionsBroker().Clients()))
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
client.Subscribe("test0")
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
if len(client.Subscriptions()) != 0 {
|
||||
t.Errorf("Expected no subscriptions, got %v", client.Subscriptions())
|
||||
}
|
||||
@@ -145,7 +145,7 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
client.Subscribe("test0")
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
expectedSubs := []string{"test1", "test2"}
|
||||
if len(expectedSubs) != len(client.Subscriptions()) {
|
||||
t.Errorf("Expected subscriptions %v, got %v", expectedSubs, client.Subscriptions())
|
||||
@@ -175,7 +175,7 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
BeforeTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
admin, _ := client.Get(apis.ContextAdminKey).(*models.Admin)
|
||||
if admin == nil {
|
||||
t.Errorf("Expected admin auth model, got nil")
|
||||
@@ -199,7 +199,7 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
BeforeTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
authRecord, _ := client.Get(apis.ContextAuthRecordKey).(*models.Record)
|
||||
if authRecord == nil {
|
||||
t.Errorf("Expected auth record model, got nil")
|
||||
@@ -224,7 +224,7 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
authRecord, _ := client.Get(apis.ContextAuthRecordKey).(*models.Record)
|
||||
if authRecord == nil {
|
||||
t.Errorf("Expected auth record model, got nil")
|
||||
|
||||
@@ -1188,7 +1188,7 @@ func TestRecordAuthUnlinkExternalsAuth(t *testing.T) {
|
||||
"OnRecordAfterUnlinkExternalAuthRequest": 1,
|
||||
"OnRecordBeforeUnlinkExternalAuthRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
record, err := app.Dao().FindRecordById("users", "4q1xlclmfloku33")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1234,7 +1234,7 @@ func TestRecordAuthUnlinkExternalsAuth(t *testing.T) {
|
||||
"OnRecordAfterUnlinkExternalAuthRequest": 1,
|
||||
"OnRecordBeforeUnlinkExternalAuthRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
record, err := app.Dao().FindRecordById("users", "4q1xlclmfloku33")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -462,6 +462,22 @@ func TestRecordCrudList(t *testing.T) {
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: "view collection with numeric ids",
|
||||
Method: http.MethodGet,
|
||||
Url: "/api/collections/numeric_id_view/records",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"page":1`,
|
||||
`"perPage":30`,
|
||||
`"totalPages":1`,
|
||||
`"totalItems":2`,
|
||||
`"items":[{`,
|
||||
`"id":"1"`,
|
||||
`"id":"2"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordsListRequest": 1},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
@@ -731,6 +747,16 @@ func TestRecordCrudView(t *testing.T) {
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordViewRequest": 1},
|
||||
},
|
||||
{
|
||||
Name: "view record with numeric id",
|
||||
Method: http.MethodGet,
|
||||
Url: "/api/collections/numeric_id_view/records/1",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"id":"1"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"OnRecordViewRequest": 1},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
@@ -877,7 +903,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
"OnRecordAfterDeleteRequest": 1,
|
||||
"OnRecordBeforeDeleteRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
ensureDeletedFiles(app, "_pb_users_auth_", "4q1xlclmfloku33")
|
||||
|
||||
// check if all the external auths records were deleted
|
||||
@@ -964,7 +990,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
"OnRecordBeforeDeleteRequest": 1,
|
||||
"OnRecordAfterDeleteRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
recId := "84nmscqy84lsi1t"
|
||||
rec, _ := app.Dao().FindRecordById("demo1", recId, nil)
|
||||
if rec != nil {
|
||||
@@ -2006,7 +2032,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"OnRecordAfterUpdateRequest": 1,
|
||||
"OnRecordBeforeUpdateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
record, _ := app.Dao().FindRecordById("nologin", "phhq3wr65cap535")
|
||||
if !record.ValidatePassword("12345678") {
|
||||
t.Fatal("Password update failed.")
|
||||
@@ -2049,7 +2075,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"OnRecordAfterUpdateRequest": 1,
|
||||
"OnRecordBeforeUpdateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
record, _ := app.Dao().FindRecordById("users", "oap640cot4yru2s")
|
||||
if !record.ValidatePassword("12345678") {
|
||||
t.Fatal("Password update failed.")
|
||||
@@ -2111,7 +2137,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"OnRecordAfterUpdateRequest": 1,
|
||||
"OnRecordBeforeUpdateRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
record, _ := app.Dao().FindRecordById("nologin", "dc49k6jgejn40h3")
|
||||
if !record.ValidatePassword("123456789") {
|
||||
t.Fatal("Password update failed.")
|
||||
|
||||
+70
-7
@@ -11,11 +11,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/labstack/echo/v5/middleware"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/migrations"
|
||||
"github.com/pocketbase/pocketbase/migrations/logs"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/migrate"
|
||||
"golang.org/x/crypto/acme"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
@@ -26,17 +28,35 @@ type ServeConfig struct {
|
||||
// ShowStartBanner indicates whether to show or hide the server start console message.
|
||||
ShowStartBanner bool
|
||||
|
||||
// HttpAddr is the HTTP server address to bind (eg. `127.0.0.1:80`).
|
||||
// HttpAddr is the TCP address to listen for the HTTP server (eg. `127.0.0.1:80`).
|
||||
HttpAddr string
|
||||
|
||||
// HttpsAddr is the HTTPS server address to bind (eg. `127.0.0.1:443`).
|
||||
// HttpsAddr is the TCP address to listen for the HTTPS server (eg. `127.0.0.1:443`).
|
||||
HttpsAddr string
|
||||
|
||||
// Optional domains list to use when issuing the TLS certificate.
|
||||
//
|
||||
// If not set, the host from the bound server address will be used.
|
||||
//
|
||||
// For convenience, for each "non-www" domain a "www" entry and
|
||||
// redirect will be automatically added.
|
||||
CertificateDomains []string
|
||||
|
||||
// AllowedOrigins is an optional list of CORS origins (default to "*").
|
||||
AllowedOrigins []string
|
||||
}
|
||||
|
||||
// Serve starts a new app web server.
|
||||
//
|
||||
// NB! The app should be bootstrapped before starting the web server.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// app.Bootstrap()
|
||||
// apis.Serve(app, apis.ServeConfig{
|
||||
// HttpAddr: "127.0.0.1:8080",
|
||||
// ShowStartBanner: false,
|
||||
// })
|
||||
func Serve(app core.App, config ServeConfig) (*http.Server, error) {
|
||||
if len(config.AllowedOrigins) == 0 {
|
||||
config.AllowedOrigins = []string{"*"}
|
||||
@@ -75,16 +95,53 @@ func Serve(app core.App, config ServeConfig) (*http.Server, error) {
|
||||
mainAddr = config.HttpsAddr
|
||||
}
|
||||
|
||||
mainHost, _, _ := net.SplitHostPort(mainAddr)
|
||||
var wwwRedirects []string
|
||||
|
||||
// extract the host names for the certificate host policy
|
||||
hostNames := config.CertificateDomains
|
||||
if len(hostNames) == 0 {
|
||||
host, _, _ := net.SplitHostPort(mainAddr)
|
||||
hostNames = append(hostNames, host)
|
||||
}
|
||||
for _, host := range hostNames {
|
||||
if strings.HasPrefix(host, "www.") {
|
||||
continue // explicitly set www host
|
||||
}
|
||||
|
||||
wwwHost := "www." + host
|
||||
if !list.ExistInSlice(wwwHost, hostNames) {
|
||||
hostNames = append(hostNames, wwwHost)
|
||||
wwwRedirects = append(wwwRedirects, wwwHost)
|
||||
}
|
||||
}
|
||||
|
||||
// implicit www->non-www redirect(s)
|
||||
if len(wwwRedirects) > 0 {
|
||||
router.Pre(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
host := c.Request().Host
|
||||
|
||||
if strings.HasPrefix(host, "www.") && list.ExistInSlice(host, wwwRedirects) {
|
||||
return c.Redirect(
|
||||
http.StatusTemporaryRedirect,
|
||||
(c.Scheme() + "://" + host[4:] + c.Request().RequestURI),
|
||||
)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
certManager := &autocert.Manager{
|
||||
Prompt: autocert.AcceptTOS,
|
||||
Cache: autocert.DirCache(filepath.Join(app.DataDir(), ".autocert_cache")),
|
||||
HostPolicy: autocert.HostWhitelist(mainHost, "www."+mainHost),
|
||||
HostPolicy: autocert.HostWhitelist(hostNames...),
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
TLSConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
GetCertificate: certManager.GetCertificate,
|
||||
NextProtos: []string{acme.ALPNProto},
|
||||
},
|
||||
@@ -107,8 +164,14 @@ func Serve(app core.App, config ServeConfig) (*http.Server, error) {
|
||||
|
||||
if config.ShowStartBanner {
|
||||
schema := "http"
|
||||
addr := server.Addr
|
||||
|
||||
if config.HttpsAddr != "" {
|
||||
schema = "https"
|
||||
|
||||
if len(config.CertificateDomains) > 0 {
|
||||
addr = config.CertificateDomains[0]
|
||||
}
|
||||
}
|
||||
|
||||
date := new(strings.Builder)
|
||||
@@ -118,12 +181,12 @@ func Serve(app core.App, config ServeConfig) (*http.Server, error) {
|
||||
bold.Printf(
|
||||
"%s Server started at %s\n",
|
||||
strings.TrimSpace(date.String()),
|
||||
color.CyanString("%s://%s", schema, server.Addr),
|
||||
color.CyanString("%s://%s", schema, addr),
|
||||
)
|
||||
|
||||
regular := color.New()
|
||||
regular.Printf("├─ REST API: %s\n", color.CyanString("%s://%s/api/", schema, server.Addr))
|
||||
regular.Printf("└─ Admin UI: %s\n", color.CyanString("%s://%s/_/", schema, server.Addr))
|
||||
regular.Printf("├─ REST API: %s\n", color.CyanString("%s://%s/api/", schema, addr))
|
||||
regular.Printf("└─ Admin UI: %s\n", color.CyanString("%s://%s/_/", schema, addr))
|
||||
}
|
||||
|
||||
// try to gracefully shutdown the server on app termination
|
||||
|
||||
@@ -400,7 +400,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend != 1 {
|
||||
t.Fatalf("[verification] Expected 1 sent email, got %d", app.TestMailer.TotalSend)
|
||||
}
|
||||
@@ -435,7 +435,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend != 1 {
|
||||
t.Fatalf("[password-reset] Expected 1 sent email, got %d", app.TestMailer.TotalSend)
|
||||
}
|
||||
@@ -470,7 +470,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
RequestHeaders: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhZG1pbiIsImV4cCI6MjIwODk4NTI2MX0.M1m--VOqGyv0d23eeUc0r9xE8ZzHaYVmVFw1VZW6gT8",
|
||||
},
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, e *echo.Echo) {
|
||||
AfterTestFunc: func(t *testing.T, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend != 1 {
|
||||
t.Fatalf("[email-change] Expected 1 sent email, got %d", app.TestMailer.TotalSend)
|
||||
}
|
||||
|
||||
+25
-9
@@ -17,14 +17,30 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||
var httpsAddr string
|
||||
|
||||
command := &cobra.Command{
|
||||
Use: "serve",
|
||||
Short: "Starts the web server (default to 127.0.0.1:8090)",
|
||||
Use: "serve [domain(s)]",
|
||||
Args: cobra.ArbitraryArgs,
|
||||
Short: "Starts the web server (default to 127.0.0.1:8090 if no domain is specified)",
|
||||
Run: func(command *cobra.Command, args []string) {
|
||||
// set default listener addresses if at least one domain is specified
|
||||
if len(args) > 0 {
|
||||
if httpAddr == "" {
|
||||
httpAddr = "0.0.0.0:80"
|
||||
}
|
||||
if httpsAddr == "" {
|
||||
httpsAddr = "0.0.0.0:443"
|
||||
}
|
||||
} else {
|
||||
if httpAddr == "" {
|
||||
httpAddr = "127.0.0.1:8090"
|
||||
}
|
||||
}
|
||||
|
||||
_, err := apis.Serve(app, apis.ServeConfig{
|
||||
HttpAddr: httpAddr,
|
||||
HttpsAddr: httpsAddr,
|
||||
ShowStartBanner: showStartBanner,
|
||||
AllowedOrigins: allowedOrigins,
|
||||
HttpAddr: httpAddr,
|
||||
HttpsAddr: httpsAddr,
|
||||
ShowStartBanner: showStartBanner,
|
||||
AllowedOrigins: allowedOrigins,
|
||||
CertificateDomains: args,
|
||||
})
|
||||
|
||||
if err != http.ErrServerClosed {
|
||||
@@ -43,15 +59,15 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||
command.PersistentFlags().StringVar(
|
||||
&httpAddr,
|
||||
"http",
|
||||
"127.0.0.1:8090",
|
||||
"api HTTP server address",
|
||||
"",
|
||||
"TCP address to listen for the HTTP server\n(if domain args are specified - default to 0.0.0.0:80, otherwise - default to 127.0.0.1:8090)",
|
||||
)
|
||||
|
||||
command.PersistentFlags().StringVar(
|
||||
&httpsAddr,
|
||||
"https",
|
||||
"",
|
||||
"api HTTPS server address (auto TLS via Let's Encrypt)\nthe incoming --http address traffic also will be redirected to this address",
|
||||
"TCP address to listen for the HTTPS server\n(if domain args are specified - default to 0.0.0.0:443, otherwise - default to empty string, aka. no TLS)\nThe incoming HTTP traffic also will be auto redirected to the HTTPS version",
|
||||
)
|
||||
|
||||
return command
|
||||
|
||||
+1
-1
@@ -375,7 +375,6 @@ func (app *BaseApp) ResetBootstrapState() error {
|
||||
|
||||
app.dao = nil
|
||||
app.logsDao = nil
|
||||
app.settings = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -471,6 +470,7 @@ func (app *BaseApp) NewMailClient() mailer.Mailer {
|
||||
Password: app.Settings().Smtp.Password,
|
||||
Tls: app.Settings().Smtp.Tls,
|
||||
AuthMethod: app.Settings().Smtp.AuthMethod,
|
||||
LocalName: app.Settings().Smtp.LocalName,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+43
-27
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/archive"
|
||||
"github.com/pocketbase/pocketbase/tools/cron"
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
"github.com/pocketbase/pocketbase/tools/inflector"
|
||||
"github.com/pocketbase/pocketbase/tools/osutils"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
@@ -46,27 +47,31 @@ func (app *BaseApp) CreateBackup(ctx context.Context, name string) error {
|
||||
return errors.New("try again later - another backup/restore operation has already been started")
|
||||
}
|
||||
|
||||
// auto generate backup name
|
||||
if name == "" {
|
||||
name = fmt.Sprintf(
|
||||
"pb_backup_%s.zip",
|
||||
time.Now().UTC().Format("20060102150405"),
|
||||
)
|
||||
name = app.generateBackupName("pb_backup_")
|
||||
}
|
||||
|
||||
app.Cache().Set(CacheKeyActiveBackup, name)
|
||||
defer app.Cache().Remove(CacheKeyActiveBackup)
|
||||
|
||||
// Archive pb_data in a temp directory, exluding the "backups" dir itself (if exist).
|
||||
// root dir entries to exclude from the backup generation
|
||||
exclude := []string{LocalBackupsDirName, LocalTempDirName}
|
||||
|
||||
// make sure that the special temp directory exists
|
||||
// note: it needs to be inside the current pb_data to avoid "cross-device link" errors
|
||||
localTempDir := filepath.Join(app.DataDir(), LocalTempDirName)
|
||||
if err := os.MkdirAll(localTempDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("failed to create a temp dir: %w", err)
|
||||
}
|
||||
|
||||
// Archive pb_data in a temp directory, exluding the "backups" and the temp dirs.
|
||||
//
|
||||
// Run in transaction to temporary block other writes (transactions uses the NonconcurrentDB connection).
|
||||
// ---
|
||||
tempPath := filepath.Join(os.TempDir(), "pb_backup_"+security.PseudorandomString(4))
|
||||
tempPath := filepath.Join(localTempDir, "pb_backup_"+security.PseudorandomString(4))
|
||||
createErr := app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
|
||||
if err := archive.Create(app.DataDir(), tempPath, LocalBackupsDirName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// @todo consider experimenting with temp switching the readonly pragma after the db interface change
|
||||
return archive.Create(app.DataDir(), tempPath, exclude...)
|
||||
})
|
||||
if createErr != nil {
|
||||
return createErr
|
||||
@@ -152,7 +157,15 @@ func (app *BaseApp) RestoreBackup(ctx context.Context, name string) error {
|
||||
}
|
||||
defer br.Close()
|
||||
|
||||
tempZip, err := os.CreateTemp(os.TempDir(), "pb_restore")
|
||||
// make sure that the special temp directory exists
|
||||
// note: it needs to be inside the current pb_data to avoid "cross-device link" errors
|
||||
localTempDir := filepath.Join(app.DataDir(), LocalTempDirName)
|
||||
if err := os.MkdirAll(localTempDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("failed to create a temp dir: %w", err)
|
||||
}
|
||||
|
||||
// create a temp zip file from the blob.Reader and try to extract it
|
||||
tempZip, err := os.CreateTemp(localTempDir, "pb_restore_zip")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -162,13 +175,7 @@ func (app *BaseApp) RestoreBackup(ctx context.Context, name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// make sure that the special temp directory
|
||||
if err := os.MkdirAll(filepath.Join(app.DataDir(), LocalTempDirName), os.ModePerm); err != nil {
|
||||
return fmt.Errorf("failed to create a temp dir: %w", err)
|
||||
}
|
||||
|
||||
// note: it needs to be inside the current pb_data to avoid "cross-device link" errors
|
||||
extractedDataDir := filepath.Join(app.DataDir(), LocalTempDirName, "pb_restore_"+security.PseudorandomString(4))
|
||||
extractedDataDir := filepath.Join(localTempDir, "pb_restore_"+security.PseudorandomString(4))
|
||||
defer os.RemoveAll(extractedDataDir)
|
||||
if err := archive.Extract(tempZip.Name(), extractedDataDir); err != nil {
|
||||
return err
|
||||
@@ -192,7 +199,7 @@ func (app *BaseApp) RestoreBackup(ctx context.Context, name string) error {
|
||||
// move the current pb_data content to a special temp location
|
||||
// that will hold the old data between dirs replace
|
||||
// (the temp dir will be automatically removed on the next app start)
|
||||
oldTempDataDir := filepath.Join(app.DataDir(), LocalTempDirName, "old_pb_data_"+security.PseudorandomString(4))
|
||||
oldTempDataDir := filepath.Join(localTempDir, "old_pb_data_"+security.PseudorandomString(4))
|
||||
if err := osutils.MoveDirContent(app.DataDir(), oldTempDataDir, exclude...); err != nil {
|
||||
return fmt.Errorf("failed to move the current pb_data content to a temp location: %w", err)
|
||||
}
|
||||
@@ -227,7 +234,6 @@ func (app *BaseApp) RestoreBackup(ctx context.Context, name string) error {
|
||||
}
|
||||
|
||||
// initAutobackupHooks registers the autobackup app serve hooks.
|
||||
// @todo add tests
|
||||
func (app *BaseApp) initAutobackupHooks() error {
|
||||
c := cron.New()
|
||||
isServe := false
|
||||
@@ -241,13 +247,9 @@ func (app *BaseApp) initAutobackupHooks() error {
|
||||
}
|
||||
|
||||
c.Add("@autobackup", rawSchedule, func() {
|
||||
autoPrefix := "@auto_pb_backup_"
|
||||
const autoPrefix = "@auto_pb_backup_"
|
||||
|
||||
name := fmt.Sprintf(
|
||||
"%s%s.zip",
|
||||
autoPrefix,
|
||||
time.Now().UTC().Format("20060102150405"),
|
||||
)
|
||||
name := app.generateBackupName(autoPrefix)
|
||||
|
||||
if err := app.CreateBackup(context.Background(), name); err != nil && app.IsDebug() {
|
||||
// @todo replace after logs generalization
|
||||
@@ -326,3 +328,17 @@ func (app *BaseApp) initAutobackupHooks() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *BaseApp) generateBackupName(prefix string) string {
|
||||
appName := inflector.Snakecase(app.Settings().Meta.AppName)
|
||||
if len(appName) > 50 {
|
||||
appName = appName[:50]
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%s%s_%s.zip",
|
||||
prefix,
|
||||
appName,
|
||||
time.Now().UTC().Format("20060102150405"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ func TestCreateBackup(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
// set some long app name with spaces and special characters
|
||||
app.Settings().Meta.AppName = "test @! " + strings.Repeat("a", 100)
|
||||
|
||||
expectedAppNamePrefix := "test_" + strings.Repeat("a", 45)
|
||||
|
||||
// test pending error
|
||||
app.Cache().Set(core.CacheKeyActiveBackup, "")
|
||||
if err := app.CreateBackup(context.Background(), "test.zip"); err == nil {
|
||||
@@ -49,8 +54,8 @@ func TestCreateBackup(t *testing.T) {
|
||||
}
|
||||
|
||||
expectedFiles := []string{
|
||||
`^pb_backup_\w+\.zip$`,
|
||||
`^pb_backup_\w+\.zip.attrs$`,
|
||||
`^pb_backup_` + expectedAppNamePrefix + `_\w+\.zip$`,
|
||||
`^pb_backup_` + expectedAppNamePrefix + `_\w+\.zip.attrs$`,
|
||||
"custom",
|
||||
"custom.attrs",
|
||||
}
|
||||
|
||||
@@ -118,10 +118,6 @@ func TestBaseAppBootstrap(t *testing.T) {
|
||||
if app.logsDao != nil {
|
||||
t.Fatalf("Expected app.logsDao to be nil, got %v.", app.logsDao)
|
||||
}
|
||||
|
||||
if app.settings != nil {
|
||||
t.Fatalf("Expected app.settings to be nil, got %v.", app.settings)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseAppGetters(t *testing.T) {
|
||||
|
||||
@@ -378,6 +378,12 @@ func (dao *Dao) saveViewCollection(newCollection, oldCollection *models.Collecti
|
||||
}
|
||||
}
|
||||
|
||||
// wrap view query if necessary
|
||||
query, err = txDao.normalizeViewQueryId(query)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to normalize view query id: %w", err)
|
||||
}
|
||||
|
||||
// (re)create the view
|
||||
if err := txDao.SaveView(newCollection.Name, query); err != nil {
|
||||
return err
|
||||
@@ -389,6 +395,52 @@ func (dao *Dao) saveViewCollection(newCollection, oldCollection *models.Collecti
|
||||
})
|
||||
}
|
||||
|
||||
// @todo consider removing once custom id types are supported
|
||||
//
|
||||
// normalizeViewQueryId wraps (if necessary) the provided view query
|
||||
// with a subselect to ensure that the id column is a text since
|
||||
// currently we don't support non-string model ids
|
||||
// (see https://github.com/pocketbase/pocketbase/issues/3110).
|
||||
func (dao *Dao) normalizeViewQueryId(query string) (string, error) {
|
||||
parsed, err := dao.parseQueryToFields(query)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
needWrapping := true
|
||||
|
||||
idField := parsed[schema.FieldNameId]
|
||||
if idField != nil && idField.field != nil &&
|
||||
idField.field.Type != schema.FieldTypeJson &&
|
||||
idField.field.Type != schema.FieldTypeNumber &&
|
||||
idField.field.Type != schema.FieldTypeBool {
|
||||
needWrapping = false
|
||||
}
|
||||
|
||||
if !needWrapping {
|
||||
return query, nil // no changes needed
|
||||
}
|
||||
|
||||
// raw parse to preserve the columns order
|
||||
rawParsed := new(identifiersParser)
|
||||
if err := rawParsed.parse(query); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
columns := make([]string, 0, len(rawParsed.columns))
|
||||
for _, col := range rawParsed.columns {
|
||||
if col.alias == schema.FieldNameId {
|
||||
columns = append(columns, fmt.Sprintf("cast([[%s]] as text) [[%s]]", col.alias, col.alias))
|
||||
} else {
|
||||
columns = append(columns, "[["+col.alias+"]]")
|
||||
}
|
||||
}
|
||||
|
||||
query = fmt.Sprintf("SELECT %s FROM (%s)", strings.Join(columns, ","), query)
|
||||
|
||||
return query, nil
|
||||
}
|
||||
|
||||
// resaveViewsWithChangedSchema updates all view collections with changed schemas.
|
||||
func (dao *Dao) resaveViewsWithChangedSchema(excludeIds ...string) error {
|
||||
collections, err := dao.FindCollectionsByType(models.CollectionTypeView)
|
||||
|
||||
+113
-3
@@ -210,11 +210,12 @@ func TestDeleteCollection(t *testing.T) {
|
||||
{colUnsaved, true},
|
||||
{colReferenced, true},
|
||||
{colSystem, true},
|
||||
{colBase, true}, // depend on view1, view2 and view2
|
||||
{colView1, true}, // view2 depend on it
|
||||
{colView2, false},
|
||||
{colView1, false}, // no longer has dependent collections
|
||||
{colBase, false},
|
||||
{colAuth, false}, // should delete also its related external auths
|
||||
{colBase, false}, // no longer has dependent views
|
||||
{colAuth, false}, // should delete also its related external auths
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
@@ -393,8 +394,117 @@ func TestSaveCollectionIndirectViewsUpdate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveCollectionViewWrapping(t *testing.T) {
|
||||
viewName := "test_wrapping"
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
query string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"no wrapping - text field",
|
||||
"select text as id, bool from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select text as id, bool from demo1)",
|
||||
},
|
||||
{
|
||||
"no wrapping - id field",
|
||||
"select text as id, bool from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select text as id, bool from demo1)",
|
||||
},
|
||||
{
|
||||
"no wrapping - relation field",
|
||||
"select rel_one as id, bool from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select rel_one as id, bool from demo1)",
|
||||
},
|
||||
{
|
||||
"no wrapping - select field",
|
||||
"select select_many as id, bool from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select select_many as id, bool from demo1)",
|
||||
},
|
||||
{
|
||||
"no wrapping - email field",
|
||||
"select email as id, bool from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select email as id, bool from demo1)",
|
||||
},
|
||||
{
|
||||
"no wrapping - datetime field",
|
||||
"select datetime as id, bool from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select datetime as id, bool from demo1)",
|
||||
},
|
||||
{
|
||||
"no wrapping - url field",
|
||||
"select url as id, bool from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select url as id, bool from demo1)",
|
||||
},
|
||||
{
|
||||
"wrapping - bool field",
|
||||
"select bool as id, text as txt, url from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (SELECT cast(`id` as text) `id`,`txt`,`url` FROM (select bool as id, text as txt, url from demo1))",
|
||||
},
|
||||
{
|
||||
"wrapping - bool field (different order)",
|
||||
"select text as txt, url, bool as id from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (SELECT `txt`,`url`,cast(`id` as text) `id` FROM (select text as txt, url, bool as id from demo1))",
|
||||
},
|
||||
{
|
||||
"wrapping - json field",
|
||||
"select json as id, text, url from demo1",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (SELECT cast(`id` as text) `id`,`text`,`url` FROM (select json as id, text, url from demo1))",
|
||||
},
|
||||
{
|
||||
"wrapping - numeric id",
|
||||
"select 1 as id",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (SELECT cast(`id` as text) `id` FROM (select 1 as id))",
|
||||
},
|
||||
{
|
||||
"wrapping - expresion",
|
||||
"select ('test') as id",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (SELECT cast(`id` as text) `id` FROM (select ('test') as id))",
|
||||
},
|
||||
{
|
||||
"no wrapping - cast as text",
|
||||
"select cast('test' as text) as id",
|
||||
"CREATE VIEW `test_wrapping` AS SELECT * FROM (select cast('test' as text) as id)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
collection := &models.Collection{
|
||||
Name: viewName,
|
||||
Type: models.CollectionTypeView,
|
||||
Options: types.JsonMap{
|
||||
"query": s.query,
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Dao().SaveCollection(collection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var sql string
|
||||
|
||||
rowErr := app.Dao().DB().NewQuery("SELECT sql FROM sqlite_master WHERE type='view' AND name={:name}").
|
||||
Bind(dbx.Params{"name": viewName}).
|
||||
Row(&sql)
|
||||
if rowErr != nil {
|
||||
t.Fatalf("Failed to retrieve view sql: %v", rowErr)
|
||||
}
|
||||
|
||||
if sql != s.expected {
|
||||
t.Fatalf("Expected query \n%v, \ngot \n%v", s.expected, sql)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportCollections(t *testing.T) {
|
||||
totalCollections := 10
|
||||
totalCollections := 11
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
|
||||
+25
-10
@@ -252,23 +252,31 @@ func (dao *Dao) FindFirstRecordByData(
|
||||
// FindRecordsByFilter returns limit number of records matching the
|
||||
// provided string filter.
|
||||
//
|
||||
// NB! Use the last "params" argument to bind untrusted user variables!
|
||||
//
|
||||
// The sort argument is optional and can be empty string OR the same format
|
||||
// used in the web APIs, eg. "-created,title".
|
||||
//
|
||||
// If the limit argument is <= 0, no limit is applied to the query and
|
||||
// all matching records are returned.
|
||||
//
|
||||
// NB! Don't put untrusted user input in the filter string as it
|
||||
// practically would allow the users to inject their own custom filter.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// dao.FindRecordsByFilter("posts", "title ~ 'lorem ipsum' && visible = true", "-created", 10)
|
||||
// dao.FindRecordsByFilter(
|
||||
// "posts",
|
||||
// "title ~ {:title} && visible = {:visible}",
|
||||
// "-created",
|
||||
// 10,
|
||||
// 0,
|
||||
// dbx.Params{"title": "lorem ipsum", "visible": true}
|
||||
// )
|
||||
func (dao *Dao) FindRecordsByFilter(
|
||||
collectionNameOrId string,
|
||||
filter string,
|
||||
sort string,
|
||||
limit int,
|
||||
offset int,
|
||||
params ...dbx.Params,
|
||||
) ([]*models.Record, error) {
|
||||
collection, err := dao.FindCollectionByNameOrId(collectionNameOrId)
|
||||
if err != nil {
|
||||
@@ -286,7 +294,7 @@ func (dao *Dao) FindRecordsByFilter(
|
||||
true, // allow searching hidden/protected fields like "email"
|
||||
)
|
||||
|
||||
expr, err := search.FilterData(filter).BuildExpr(resolver)
|
||||
expr, err := search.FilterData(filter).BuildExpr(resolver, params...)
|
||||
if err != nil || expr == nil {
|
||||
return nil, errors.New("invalid or empty filter expression")
|
||||
}
|
||||
@@ -307,6 +315,10 @@ func (dao *Dao) FindRecordsByFilter(
|
||||
resolver.UpdateQuery(q) // attaches any adhoc joins and aliases
|
||||
// ---
|
||||
|
||||
if offset > 0 {
|
||||
q.Offset(int64(offset))
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
q.Limit(int64(limit))
|
||||
}
|
||||
@@ -322,14 +334,17 @@ func (dao *Dao) FindRecordsByFilter(
|
||||
|
||||
// FindFirstRecordByFilter returns the first available record matching the provided filter.
|
||||
//
|
||||
// NB! Don't put untrusted user input in the filter string as it
|
||||
// practically would allow the users to inject their own custom filter.
|
||||
// NB! Use the last params argument to bind untrusted user variables!
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// dao.FindFirstRecordByFilter("posts", "slug='test'")
|
||||
func (dao *Dao) FindFirstRecordByFilter(collectionNameOrId string, filter string) (*models.Record, error) {
|
||||
result, err := dao.FindRecordsByFilter(collectionNameOrId, filter, "", 1)
|
||||
// dao.FindFirstRecordByFilter("posts", "slug={:slug} && status='public'", dbx.Params{"slug": "test"})
|
||||
func (dao *Dao) FindFirstRecordByFilter(
|
||||
collectionNameOrId string,
|
||||
filter string,
|
||||
params ...dbx.Params,
|
||||
) (*models.Record, error) {
|
||||
result, err := dao.FindRecordsByFilter(collectionNameOrId, filter, "", 1, 0, params...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
"github.com/pocketbase/pocketbase/tools/dbutils"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
@@ -159,10 +158,6 @@ func (dao *Dao) SyncRecordTableSchema(newCollection *models.Collection, oldColle
|
||||
return err
|
||||
}
|
||||
|
||||
if err := txDao.syncRelationDisplayFieldsChanges(newCollection, renamedFieldNames, deletedFieldNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return txDao.createCollectionIndexes(newCollection)
|
||||
})
|
||||
}
|
||||
@@ -294,50 +289,6 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
|
||||
})
|
||||
}
|
||||
|
||||
func (dao *Dao) syncRelationDisplayFieldsChanges(collection *models.Collection, renamedFieldNames map[string]string, deletedFieldNames []string) error {
|
||||
if len(renamedFieldNames) == 0 && len(deletedFieldNames) == 0 {
|
||||
return nil // nothing to sync
|
||||
}
|
||||
|
||||
refs, err := dao.FindCollectionReferences(collection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for refCollection, refFields := range refs {
|
||||
for _, refField := range refFields {
|
||||
options, _ := refField.Options.(*schema.RelationOptions)
|
||||
if options == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// remove deleted (if any)
|
||||
newDisplayFields := list.SubtractSlice(options.DisplayFields, deletedFieldNames)
|
||||
|
||||
for old, new := range renamedFieldNames {
|
||||
for i, name := range newDisplayFields {
|
||||
if name == old {
|
||||
newDisplayFields[i] = new
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// has changes
|
||||
if len(list.SubtractSlice(options.DisplayFields, newDisplayFields)) > 0 {
|
||||
options.DisplayFields = newDisplayFields
|
||||
|
||||
// direct collection save to prevent self-referencing
|
||||
// recursion and unnecessary records table sync checks
|
||||
if err := dao.Save(refCollection); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dao *Dao) dropCollectionIndex(collection *models.Collection) error {
|
||||
if collection.IsView() {
|
||||
return nil // views don't have indexes
|
||||
|
||||
+71
-28
@@ -436,6 +436,8 @@ func TestFindRecordsByFilter(t *testing.T) {
|
||||
filter string
|
||||
sort string
|
||||
limit int
|
||||
offset int
|
||||
params []dbx.Params
|
||||
expectError bool
|
||||
expectRecordIds []string
|
||||
}{
|
||||
@@ -445,6 +447,8 @@ func TestFindRecordsByFilter(t *testing.T) {
|
||||
"id != ''",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
@@ -454,6 +458,8 @@ func TestFindRecordsByFilter(t *testing.T) {
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
@@ -463,6 +469,8 @@ func TestFindRecordsByFilter(t *testing.T) {
|
||||
"someMissingField > 1",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
true,
|
||||
nil,
|
||||
},
|
||||
@@ -472,6 +480,8 @@ func TestFindRecordsByFilter(t *testing.T) {
|
||||
"id != ''",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
nil,
|
||||
false,
|
||||
[]string{
|
||||
"llvuca81nly1qls",
|
||||
@@ -485,6 +495,8 @@ func TestFindRecordsByFilter(t *testing.T) {
|
||||
"id != '' && active=true",
|
||||
"-created,title",
|
||||
-1, // should behave the same as 0
|
||||
0,
|
||||
nil,
|
||||
false,
|
||||
[]string{
|
||||
"0yxhwia2amd8gec",
|
||||
@@ -492,47 +504,64 @@ func TestFindRecordsByFilter(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
"with limit",
|
||||
"with limit and offset",
|
||||
"demo2",
|
||||
"id != ''",
|
||||
"title",
|
||||
2,
|
||||
1,
|
||||
nil,
|
||||
false,
|
||||
[]string{
|
||||
"achvryl401bhse3",
|
||||
"0yxhwia2amd8gec",
|
||||
},
|
||||
},
|
||||
{
|
||||
"with placeholder params",
|
||||
"demo2",
|
||||
"active = {:active}",
|
||||
"",
|
||||
10,
|
||||
0,
|
||||
[]dbx.Params{{"active": false}},
|
||||
false,
|
||||
[]string{
|
||||
"llvuca81nly1qls",
|
||||
"achvryl401bhse3",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
records, err := app.Dao().FindRecordsByFilter(
|
||||
s.collectionIdOrName,
|
||||
s.filter,
|
||||
s.sort,
|
||||
s.limit,
|
||||
)
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
records, err := app.Dao().FindRecordsByFilter(
|
||||
s.collectionIdOrName,
|
||||
s.filter,
|
||||
s.sort,
|
||||
s.limit,
|
||||
s.offset,
|
||||
s.params...,
|
||||
)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
t.Errorf("[%s] Expected hasErr to be %v, got %v (%v)", s.name, s.expectError, hasErr, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if hasErr {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(records) != len(s.expectRecordIds) {
|
||||
t.Errorf("[%s] Expected %d records, got %d", s.name, len(s.expectRecordIds), len(records))
|
||||
continue
|
||||
}
|
||||
|
||||
for i, id := range s.expectRecordIds {
|
||||
if id != records[i].Id {
|
||||
t.Errorf("[%s] Expected record with id %q, got %q at index %d", s.name, id, records[i].Id, i)
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
t.Fatalf("[%s] Expected hasErr to be %v, got %v (%v)", s.name, s.expectError, hasErr, err)
|
||||
}
|
||||
}
|
||||
|
||||
if hasErr {
|
||||
return
|
||||
}
|
||||
|
||||
if len(records) != len(s.expectRecordIds) {
|
||||
t.Fatalf("[%s] Expected %d records, got %d", s.name, len(s.expectRecordIds), len(records))
|
||||
}
|
||||
|
||||
for i, id := range s.expectRecordIds {
|
||||
if id != records[i].Id {
|
||||
t.Fatalf("[%s] Expected record with id %q, got %q at index %d", s.name, id, records[i].Id, i)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,6 +573,7 @@ func TestFindFirstRecordByFilter(t *testing.T) {
|
||||
name string
|
||||
collectionIdOrName string
|
||||
filter string
|
||||
params []dbx.Params
|
||||
expectError bool
|
||||
expectRecordId string
|
||||
}{
|
||||
@@ -551,6 +581,7 @@ func TestFindFirstRecordByFilter(t *testing.T) {
|
||||
"missing collection",
|
||||
"missing",
|
||||
"id != ''",
|
||||
nil,
|
||||
true,
|
||||
"",
|
||||
},
|
||||
@@ -558,6 +589,7 @@ func TestFindFirstRecordByFilter(t *testing.T) {
|
||||
"missing filter",
|
||||
"demo2",
|
||||
"",
|
||||
nil,
|
||||
true,
|
||||
"",
|
||||
},
|
||||
@@ -565,6 +597,7 @@ func TestFindFirstRecordByFilter(t *testing.T) {
|
||||
"invalid filter",
|
||||
"demo2",
|
||||
"someMissingField > 1",
|
||||
nil,
|
||||
true,
|
||||
"",
|
||||
},
|
||||
@@ -572,6 +605,7 @@ func TestFindFirstRecordByFilter(t *testing.T) {
|
||||
"valid filter but no matches",
|
||||
"demo2",
|
||||
"id = 'test'",
|
||||
nil,
|
||||
true,
|
||||
"",
|
||||
},
|
||||
@@ -579,13 +613,22 @@ func TestFindFirstRecordByFilter(t *testing.T) {
|
||||
"valid filter and multiple matches",
|
||||
"demo2",
|
||||
"id != ''",
|
||||
nil,
|
||||
false,
|
||||
"llvuca81nly1qls",
|
||||
},
|
||||
{
|
||||
"with placeholder params",
|
||||
"demo2",
|
||||
"active = {:active}",
|
||||
[]dbx.Params{{"active": false}},
|
||||
false,
|
||||
"llvuca81nly1qls",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
record, err := app.Dao().FindFirstRecordByFilter(s.collectionIdOrName, s.filter)
|
||||
record, err := app.Dao().FindFirstRecordByFilter(s.collectionIdOrName, s.filter, s.params...)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
|
||||
+36
-9
@@ -235,6 +235,8 @@ func defaultViewField(name string) *schema.SchemaField {
|
||||
}
|
||||
}
|
||||
|
||||
var castRegex = regexp.MustCompile(`(?i)^cast\s*\(.*\s+as\s+(\w+)\s*\)$`)
|
||||
|
||||
func (dao *Dao) parseQueryToFields(selectQuery string) (map[string]*queryField, error) {
|
||||
p := new(identifiersParser)
|
||||
if err := p.parse(selectQuery); err != nil {
|
||||
@@ -257,15 +259,8 @@ func (dao *Dao) parseQueryToFields(selectQuery string) (map[string]*queryField,
|
||||
for _, col := range p.columns {
|
||||
colLower := strings.ToLower(col.original)
|
||||
|
||||
// numeric expression cast
|
||||
if strings.Contains(colLower, "(") &&
|
||||
(strings.HasPrefix(colLower, "count(") ||
|
||||
strings.HasPrefix(colLower, "total(") ||
|
||||
strings.Contains(colLower, " as numeric") ||
|
||||
strings.Contains(colLower, " as real") ||
|
||||
strings.Contains(colLower, " as int") ||
|
||||
strings.Contains(colLower, " as integer") ||
|
||||
strings.Contains(colLower, " as decimal")) {
|
||||
// numeric aggregations
|
||||
if strings.HasPrefix(colLower, "count(") || strings.HasPrefix(colLower, "total(") {
|
||||
result[col.alias] = &queryField{
|
||||
field: &schema.SchemaField{
|
||||
Name: col.alias,
|
||||
@@ -275,6 +270,38 @@ func (dao *Dao) parseQueryToFields(selectQuery string) (map[string]*queryField,
|
||||
continue
|
||||
}
|
||||
|
||||
castMatch := castRegex.FindStringSubmatch(colLower)
|
||||
|
||||
// numeric casts
|
||||
if len(castMatch) == 2 {
|
||||
switch castMatch[1] {
|
||||
case "real", "integer", "int", "decimal", "numeric":
|
||||
result[col.alias] = &queryField{
|
||||
field: &schema.SchemaField{
|
||||
Name: col.alias,
|
||||
Type: schema.FieldTypeNumber,
|
||||
},
|
||||
}
|
||||
continue
|
||||
case "text":
|
||||
result[col.alias] = &queryField{
|
||||
field: &schema.SchemaField{
|
||||
Name: col.alias,
|
||||
Type: schema.FieldTypeText,
|
||||
},
|
||||
}
|
||||
continue
|
||||
case "boolean", "bool":
|
||||
result[col.alias] = &queryField{
|
||||
field: &schema.SchemaField{
|
||||
Name: col.alias,
|
||||
Type: schema.FieldTypeBool,
|
||||
},
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Split(col.original, ".")
|
||||
|
||||
var fieldName string
|
||||
|
||||
+7
-1
@@ -330,7 +330,7 @@ func TestCreateViewSchema(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
"query with numeric casts",
|
||||
"query with casts",
|
||||
`select
|
||||
a.id,
|
||||
count(a.id) count,
|
||||
@@ -339,6 +339,9 @@ func TestCreateViewSchema(t *testing.T) {
|
||||
cast(a.id as real) cast_real,
|
||||
cast(a.id as decimal) cast_decimal,
|
||||
cast(a.id as numeric) cast_numeric,
|
||||
cast(a.id as text) cast_text,
|
||||
cast(a.id as bool) cast_bool,
|
||||
cast(a.id as boolean) cast_boolean,
|
||||
avg(a.id) avg,
|
||||
sum(a.id) sum,
|
||||
total(a.id) total,
|
||||
@@ -354,6 +357,9 @@ func TestCreateViewSchema(t *testing.T) {
|
||||
"cast_real": schema.FieldTypeNumber,
|
||||
"cast_decimal": schema.FieldTypeNumber,
|
||||
"cast_numeric": schema.FieldTypeNumber,
|
||||
"cast_text": schema.FieldTypeText,
|
||||
"cast_bool": schema.FieldTypeBool,
|
||||
"cast_boolean": schema.FieldTypeBool,
|
||||
// json because they are nullable
|
||||
"sum": schema.FieldTypeJson,
|
||||
"avg": schema.FieldTypeJson,
|
||||
|
||||
@@ -42,7 +42,7 @@ func main() {
|
||||
app.RootCmd.PersistentFlags().IntVar(
|
||||
&hooksPool,
|
||||
"hooksPool",
|
||||
50,
|
||||
25,
|
||||
"the total prewarm goja.Runtime instances for the JS app hooks execution",
|
||||
)
|
||||
|
||||
|
||||
@@ -75,14 +75,14 @@ func (form *AdminPasswordResetRequest) Submit(interceptors ...InterceptorFunc[*m
|
||||
return errors.New("You have already requested a password reset.")
|
||||
}
|
||||
|
||||
// update last sent timestamp
|
||||
admin.LastResetSentAt = types.NowDateTime()
|
||||
|
||||
return runInterceptors(admin, func(m *models.Admin) error {
|
||||
if err := mails.SendAdminPasswordReset(form.app, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update last sent timestamp
|
||||
m.LastResetSentAt = types.NowDateTime()
|
||||
|
||||
return form.dao.SaveAdmin(m)
|
||||
}, interceptors...)
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func TestAdminPasswordResetRequestInterceptors(t *testing.T) {
|
||||
t.Fatalf("Expected interceptor2 to be called")
|
||||
}
|
||||
|
||||
if interceptorLastResetSentAt.String() == admin.LastResetSentAt.String() {
|
||||
t.Fatalf("Expected the form model to be filled before calling the interceptors")
|
||||
if interceptorLastResetSentAt.String() != admin.LastResetSentAt.String() {
|
||||
t.Fatalf("Expected the form model to NOT be filled before calling the interceptors")
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -38,7 +38,7 @@ func TestBackupCreateValidateAndSubmit(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
func() {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
@@ -56,47 +56,47 @@ func TestBackupCreateValidateAndSubmit(t *testing.T) {
|
||||
// parse errors
|
||||
errs, ok := result.(validation.Errors)
|
||||
if !ok && result != nil {
|
||||
t.Errorf("[%s] Failed to parse errors %v", s.name, result)
|
||||
t.Fatalf("Failed to parse errors %v", result)
|
||||
return
|
||||
}
|
||||
|
||||
// check errors
|
||||
if len(errs) > len(s.expectedErrors) {
|
||||
t.Errorf("[%s] Expected error keys %v, got %v", s.name, s.expectedErrors, errs)
|
||||
t.Fatalf("Expected error keys %v, got %v", s.expectedErrors, errs)
|
||||
}
|
||||
for _, k := range s.expectedErrors {
|
||||
if _, ok := errs[k]; !ok {
|
||||
t.Errorf("[%s] Missing expected error key %q in %v", s.name, k, errs)
|
||||
t.Fatalf("Missing expected error key %q in %v", k, errs)
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve all created backup files
|
||||
files, err := fsys.List("")
|
||||
if err != nil {
|
||||
t.Errorf("[%s] Failed to retrieve backup files", s.name)
|
||||
t.Fatal("Failed to retrieve backup files")
|
||||
return
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
if total := len(files); total != 0 {
|
||||
t.Errorf("[%s] Didn't expected backup files, found %d", s.name, total)
|
||||
t.Fatalf("Didn't expected backup files, found %d", total)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if total := len(files); total != 1 {
|
||||
t.Errorf("[%s] Expected 1 backup file, got %d", s.name, total)
|
||||
t.Fatalf("Expected 1 backup file, got %d", total)
|
||||
return
|
||||
}
|
||||
|
||||
if s.backupName == "" {
|
||||
prefix := "pb_backup_"
|
||||
if !strings.HasPrefix(files[0].Key, prefix) {
|
||||
t.Errorf("[%s] Expected the backup file, to have prefix %q: %q", s.name, prefix, files[0].Key)
|
||||
t.Fatalf("Expected the backup file, to have prefix %q: %q", prefix, files[0].Key)
|
||||
}
|
||||
} else if s.backupName != files[0].Key {
|
||||
t.Errorf("[%s] Expected backup file %q, got %q", s.name, s.backupName, files[0].Key)
|
||||
t.Fatalf("Expected backup file %q, got %q", s.backupName, files[0].Key)
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package forms
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/forms/validators"
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
)
|
||||
|
||||
// BackupUpload is a request form for uploading a new app backup.
|
||||
type BackupUpload struct {
|
||||
app core.App
|
||||
ctx context.Context
|
||||
|
||||
File *filesystem.File `json:"file"`
|
||||
}
|
||||
|
||||
// NewBackupUpload creates new BackupUpload request form.
|
||||
func NewBackupUpload(app core.App) *BackupUpload {
|
||||
return &BackupUpload{
|
||||
app: app,
|
||||
ctx: context.Background(),
|
||||
}
|
||||
}
|
||||
|
||||
// SetContext replaces the default form upload context with the provided one.
|
||||
func (form *BackupUpload) SetContext(ctx context.Context) {
|
||||
form.ctx = ctx
|
||||
}
|
||||
|
||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||
func (form *BackupUpload) Validate() error {
|
||||
return validation.ValidateStruct(form,
|
||||
validation.Field(
|
||||
&form.File,
|
||||
validation.Required,
|
||||
validation.By(validators.UploadedFileMimeType([]string{"application/zip"})),
|
||||
validation.By(form.checkUniqueName),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (form *BackupUpload) checkUniqueName(value any) error {
|
||||
v, _ := value.(*filesystem.File)
|
||||
if v == nil {
|
||||
return nil // nothing to check
|
||||
}
|
||||
|
||||
fsys, err := form.app.NewBackupsFilesystem()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fsys.Close()
|
||||
|
||||
fsys.SetContext(form.ctx)
|
||||
|
||||
if exists, err := fsys.Exists(v.OriginalName); err != nil || exists {
|
||||
return validation.NewError("validation_backup_name_exists", "Backup file with the specified name already exists.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Submit validates the form and upload the backup file.
|
||||
//
|
||||
// You can optionally provide a list of InterceptorFunc to further
|
||||
// modify the form behavior before uploading the backup.
|
||||
func (form *BackupUpload) Submit(interceptors ...InterceptorFunc[*filesystem.File]) error {
|
||||
if err := form.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return runInterceptors(form.File, func(file *filesystem.File) error {
|
||||
fsys, err := form.app.NewBackupsFilesystem()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fsys.SetContext(form.ctx)
|
||||
|
||||
return fsys.UploadFile(file, file.OriginalName)
|
||||
}, interceptors...)
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package forms_test
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/pocketbase/pocketbase/forms"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
)
|
||||
|
||||
func TestBackupUploadValidateAndSubmit(t *testing.T) {
|
||||
var zb bytes.Buffer
|
||||
zw := zip.NewWriter(&zb)
|
||||
if err := zw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f0, _ := filesystem.NewFileFromBytes([]byte("test"), "existing")
|
||||
f1, _ := filesystem.NewFileFromBytes([]byte("456"), "nozip")
|
||||
f2, _ := filesystem.NewFileFromBytes(zb.Bytes(), "existing")
|
||||
f3, _ := filesystem.NewFileFromBytes(zb.Bytes(), "zip")
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
file *filesystem.File
|
||||
expectedErrors []string
|
||||
}{
|
||||
{
|
||||
"missing file",
|
||||
nil,
|
||||
[]string{"file"},
|
||||
},
|
||||
{
|
||||
"non-zip file",
|
||||
f1,
|
||||
[]string{"file"},
|
||||
},
|
||||
{
|
||||
"zip file with non-unique name",
|
||||
f2,
|
||||
[]string{"file"},
|
||||
},
|
||||
{
|
||||
"zip file with unique name",
|
||||
f3,
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
fsys, err := app.NewBackupsFilesystem()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer fsys.Close()
|
||||
// create a dummy backup file to simulate existing backups
|
||||
if err := fsys.UploadFile(f0, f0.OriginalName); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
form := forms.NewBackupUpload(app)
|
||||
form.File = s.file
|
||||
|
||||
result := form.Submit()
|
||||
|
||||
// parse errors
|
||||
errs, ok := result.(validation.Errors)
|
||||
if !ok && result != nil {
|
||||
t.Fatalf("Failed to parse errors %v", result)
|
||||
}
|
||||
|
||||
// check errors
|
||||
if len(errs) > len(s.expectedErrors) {
|
||||
t.Fatalf("Expected error keys %v, got %v", s.expectedErrors, errs)
|
||||
}
|
||||
for _, k := range s.expectedErrors {
|
||||
if _, ok := errs[k]; !ok {
|
||||
t.Fatalf("Missing expected error key %q in %v", k, errs)
|
||||
}
|
||||
}
|
||||
|
||||
expectedFiles := []*filesystem.File{f0}
|
||||
if result == nil {
|
||||
expectedFiles = append(expectedFiles, s.file)
|
||||
}
|
||||
|
||||
// retrieve all uploaded backup files
|
||||
files, err := fsys.List("")
|
||||
if err != nil {
|
||||
t.Fatal("Failed to retrieve backup files")
|
||||
}
|
||||
|
||||
if len(files) != len(expectedFiles) {
|
||||
t.Fatalf("Expected %d files, got %d", len(expectedFiles), len(files))
|
||||
}
|
||||
|
||||
for _, ef := range expectedFiles {
|
||||
exists := false
|
||||
for _, f := range files {
|
||||
if f.Key == ef.OriginalName {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
t.Fatalf("Missing expected backup file %v", ef.OriginalName)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -229,14 +229,6 @@ func (form *CollectionUpsert) ensureNoFieldsTypeChange(value any) error {
|
||||
func (form *CollectionUpsert) checkRelationFields(value any) error {
|
||||
v, _ := value.(schema.Schema)
|
||||
|
||||
systemDisplayFields := schema.BaseModelFieldNames()
|
||||
systemDisplayFields = append(systemDisplayFields,
|
||||
schema.FieldNameUsername,
|
||||
schema.FieldNameEmail,
|
||||
schema.FieldNameEmailVisibility,
|
||||
schema.FieldNameVerified,
|
||||
)
|
||||
|
||||
for i, field := range v.Fields() {
|
||||
if field.Type != schema.FieldTypeRelation {
|
||||
continue
|
||||
@@ -289,25 +281,11 @@ func (form *CollectionUpsert) checkRelationFields(value any) error {
|
||||
"options": validation.Errors{
|
||||
"collectionId": validation.NewError(
|
||||
"validation_field_non_view_base_relation_collection",
|
||||
"Non view collections are now allowed to have a view relation.",
|
||||
"Non view collections are not allowed to have a view relation.",
|
||||
),
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
// validate displayFields (if any)
|
||||
for _, name := range options.DisplayFields {
|
||||
if relCollection.Schema.GetFieldByName(name) == nil && !list.ExistInSlice(name, systemDisplayFields) {
|
||||
return validation.Errors{fmt.Sprint(i): validation.Errors{
|
||||
"options": validation.Errors{
|
||||
"displayFields": validation.NewError(
|
||||
"validation_field_invalid_relation_displayFields",
|
||||
fmt.Sprintf("%q does not exist in the related %q collection.", name, relCollection.Name),
|
||||
),
|
||||
}},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -171,25 +171,6 @@ func TestCollectionUpsertValidateAndSubmit(t *testing.T) {
|
||||
}`,
|
||||
[]string{"schema"},
|
||||
},
|
||||
{
|
||||
"create failure - missing relation display field",
|
||||
"",
|
||||
`{
|
||||
"name": "test_new",
|
||||
"type": "base",
|
||||
"schema": [
|
||||
{
|
||||
"name":"test",
|
||||
"type":"relation",
|
||||
"options":{
|
||||
"collectionId":"wsmn24bux7wo113",
|
||||
"displayFields":["text", "missing"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
[]string{"schema"},
|
||||
},
|
||||
{
|
||||
"create failure - check auth options validators",
|
||||
"",
|
||||
@@ -605,7 +586,7 @@ func TestCollectionUpsertValidateAndSubmit(t *testing.T) {
|
||||
}
|
||||
|
||||
if form.Name != collection.Name {
|
||||
t.Errorf("Expected Name %q, got %q", collection.Name, form.Name)
|
||||
t.Fatalf("Expected Name %q, got %q", collection.Name, form.Name)
|
||||
}
|
||||
|
||||
if form.Type != collection.Type {
|
||||
|
||||
@@ -38,7 +38,7 @@ func TestCollectionsImportValidate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCollectionsImportSubmit(t *testing.T) {
|
||||
totalCollections := 10
|
||||
totalCollections := 11
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
|
||||
@@ -78,14 +78,14 @@ func (form *RecordPasswordResetRequest) Submit(interceptors ...InterceptorFunc[*
|
||||
return errors.New("You've already requested a password reset.")
|
||||
}
|
||||
|
||||
// update last sent timestamp
|
||||
authRecord.Set(schema.FieldNameLastResetSentAt, types.NowDateTime())
|
||||
|
||||
return runInterceptors(authRecord, func(m *models.Record) error {
|
||||
if err := mails.SendRecordPasswordReset(form.app, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update last sent timestamp
|
||||
m.Set(schema.FieldNameLastResetSentAt, types.NowDateTime())
|
||||
|
||||
return form.dao.SaveRecord(m)
|
||||
}, interceptors...)
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ func TestRecordPasswordResetRequestInterceptors(t *testing.T) {
|
||||
t.Fatalf("Expected interceptor2 to be called")
|
||||
}
|
||||
|
||||
if interceptorLastResetSentAt.String() == authRecord.LastResetSentAt().String() {
|
||||
t.Fatalf("Expected the form model to be filled before calling the interceptors")
|
||||
if interceptorLastResetSentAt.String() != authRecord.LastResetSentAt().String() {
|
||||
t.Fatalf("Expected the form model to NOT be filled before calling the interceptors")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,9 +82,6 @@ func (form *RecordVerificationRequest) Submit(interceptors ...InterceptorFunc[*m
|
||||
if (now.Sub(lastVerificationSentAt)).Seconds() < form.resendThreshold {
|
||||
return errors.New("A verification email was already sent.")
|
||||
}
|
||||
|
||||
// update last sent timestamp
|
||||
record.SetLastVerificationSentAt(types.NowDateTime())
|
||||
}
|
||||
|
||||
return runInterceptors(record, func(m *models.Record) error {
|
||||
@@ -96,6 +93,9 @@ func (form *RecordVerificationRequest) Submit(interceptors ...InterceptorFunc[*m
|
||||
return err
|
||||
}
|
||||
|
||||
// update last sent timestamp
|
||||
m.SetLastVerificationSentAt(types.NowDateTime())
|
||||
|
||||
return form.dao.SaveRecord(m)
|
||||
}, interceptors...)
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func TestRecordVerificationRequestInterceptors(t *testing.T) {
|
||||
t.Fatalf("Expected interceptor2 to be called")
|
||||
}
|
||||
|
||||
if interceptorLastVerificationSentAt.String() == authRecord.LastVerificationSentAt().String() {
|
||||
t.Fatalf("Expected the form model to be filled before calling the interceptors")
|
||||
if interceptorLastVerificationSentAt.String() != authRecord.LastVerificationSentAt().String() {
|
||||
t.Fatalf("Expected the form model to NOT be filled before calling the interceptors")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +158,10 @@ func (validator *RecordDataValidator) checkNumberValue(field *schema.SchemaField
|
||||
|
||||
options, _ := field.Options.(*schema.NumberOptions)
|
||||
|
||||
if options.NoDecimal && val != float64(int64(val)) {
|
||||
return validation.NewError("validation_no_decimal_constraint", "Decimal numbers are not allowed")
|
||||
}
|
||||
|
||||
if options.Min != nil && val < *options.Min {
|
||||
return validation.NewError("validation_min_number_constraint", fmt.Sprintf("Must be larger than %f", *options.Min))
|
||||
}
|
||||
|
||||
@@ -180,6 +180,13 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
|
||||
Max: &max,
|
||||
},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "field4",
|
||||
Type: schema.FieldTypeNumber,
|
||||
Options: &schema.NumberOptions{
|
||||
NoDecimal: true,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err := app.Dao().SaveCollection(collection); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -201,6 +208,7 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
|
||||
"field1": nil,
|
||||
"field2": nil,
|
||||
"field3": nil,
|
||||
"field4": nil,
|
||||
},
|
||||
nil,
|
||||
[]string{"field2"},
|
||||
@@ -211,6 +219,7 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
|
||||
"field1": "invalid",
|
||||
"field2": "invalid",
|
||||
"field3": "invalid",
|
||||
"field4": "invalid",
|
||||
},
|
||||
nil,
|
||||
[]string{"field2"},
|
||||
@@ -244,6 +253,15 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
|
||||
nil,
|
||||
[]string{"field3"},
|
||||
},
|
||||
{
|
||||
"(number) check NoDecimal",
|
||||
map[string]any{
|
||||
"field2": 1,
|
||||
"field4": 456.789,
|
||||
},
|
||||
nil,
|
||||
[]string{"field4"},
|
||||
},
|
||||
{
|
||||
"(number) valid data (only required)",
|
||||
map[string]any{
|
||||
@@ -258,6 +276,7 @@ func TestRecordDataValidatorValidateNumber(t *testing.T) {
|
||||
"field1": nil,
|
||||
"field2": 123, // test value cast
|
||||
"field3": max,
|
||||
"field4": 456,
|
||||
},
|
||||
nil,
|
||||
[]string{},
|
||||
|
||||
@@ -4,11 +4,11 @@ go 1.18
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/aws/aws-sdk-go v1.44.313
|
||||
github.com/aws/aws-sdk-go v1.45.3
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/domodwyer/mailyak/v3 v3.6.1
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2
|
||||
github.com/dop251/goja v0.0.0-20230707174833-636fdf960de1
|
||||
github.com/dop251/goja_nodejs v0.0.0-20230731184339-9ba67d720a81
|
||||
github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.2
|
||||
@@ -17,46 +17,46 @@ require (
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
github.com/pocketbase/dbx v1.10.0
|
||||
github.com/pocketbase/dbx v1.10.1
|
||||
github.com/pocketbase/tygoja v0.0.0-20230618203136-2f8d57768be1
|
||||
github.com/spf13/cast v1.5.1
|
||||
github.com/spf13/cobra v1.7.0
|
||||
gocloud.dev v0.32.0
|
||||
golang.org/x/crypto v0.11.0
|
||||
golang.org/x/net v0.12.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
gocloud.dev v0.34.0
|
||||
golang.org/x/crypto v0.13.0
|
||||
golang.org/x/net v0.15.0
|
||||
golang.org/x/oauth2 v0.12.0
|
||||
golang.org/x/sync v0.3.0
|
||||
modernc.org/sqlite v1.24.0
|
||||
modernc.org/sqlite v1.25.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.75 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.0 // indirect
|
||||
github.com/aws/smithy-go v1.14.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.39 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.37 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.83 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect
|
||||
github.com/aws/smithy-go v1.14.2 // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/google/wire v0.5.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
@@ -70,26 +70,26 @@ require (
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/image v0.9.0 // indirect
|
||||
golang.org/x/image v0.12.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
golang.org/x/term v0.12.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.11.1 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.134.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect
|
||||
google.golang.org/api v0.138.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/grpc v1.57.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
lukechampine.com/uint128 v1.3.0 // indirect
|
||||
modernc.org/cc/v3 v3.41.0 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.14 // indirect
|
||||
modernc.org/ccgo/v3 v3.16.15 // indirect
|
||||
modernc.org/libc v1.24.1 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.1 // indirect
|
||||
modernc.org/opt v0.1.3 // indirect
|
||||
modernc.org/strutil v1.1.3 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q=
|
||||
cloud.google.com/go/compute v1.22.0 h1:cB8R6FtUtT1TYGl5R3xuxnW6OUIc/DrT2aiR16TTG7Y=
|
||||
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
|
||||
cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI=
|
||||
@@ -12,46 +12,46 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aws/aws-sdk-go v1.44.313 h1:u6EuNQqgAmi09GEZ5g/XGHLF0XV31WcdU5rnHyIBHBc=
|
||||
github.com/aws/aws-sdk-go v1.44.313/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.0 h1:INUDpYLt4oiPOJl0XwZDK2OVAVf0Rzo+MGVTv9f+gy8=
|
||||
github.com/aws/aws-sdk-go-v2 v1.20.0/go.mod h1:uWOr0m0jDsiWw8nnXiqZ+YG6LdvAlGYDLLf2NmHZoy4=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11 h1:/MS8AzqYNAhhRNalOmxUvYs8VEbNGifTnzhPFdcRQkQ=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.11/go.mod h1:va22++AdXht4ccO3kH2SHkHHYvZ2G9Utz+CXKmm2CaU=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.31 h1:CcacHsJjsPtHpe1MaopwPddUErmLnl+X77+7n4G2KkY=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.31/go.mod h1:pnSeuahFFvtScCHy0INXLxJ4N8H7KncD5u6A48bx3/8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.30 h1:4pt4sI4OwXrrWUGuGr5NEb2g+4IBUB/I2BVj0t2Ak7Q=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.30/go.mod h1:Scpo/dGUdxAtRKsNCaXMXONnl3gvvugbXVldy5Fz2DQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 h1:X3H6+SU21x+76LRglk21dFRgMTJMa5QcpW+SqUf5BBg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7/go.mod h1:3we0V09SwcJBzNlnyovrR2wWJhWmVdqAsmVs4uronv8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.75 h1:dKQyBQCZiu47cQ47UhwsVEr5TYu4YqoyAnue6qgybh0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.75/go.mod h1:ZduPOCZCNBJ4anGPXD9oU+3tJgOmm9VdoBQuNHvYnFY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 h1:zr/gxAZkMcvP71ZhQOcvdm8ReLjFgIXnIn0fw5AM7mo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37/go.mod h1:Pdn4j43v49Kk6+82spO3Tu5gSeQXRsxo56ePPQAvFiA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 h1:0HCMIkAkVY9KMgueD8tf4bRTUanzEYvhw7KkPXIMpO0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31/go.mod h1:fTJDMe8LOFYtqiFFFeHA+SVMAwqLhoq0kcInYoLa9Js=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 h1:+i1DOFrW3YZ3apE45tCal9+aDKK6kNEbW6Ib7e1nFxE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38/go.mod h1:1/jLp0OgOaWIetycOmycW+vYTYgTZFPttJQRgsI1PoU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0 h1:U5yySdwt2HPo/pnQec04DImLzWORbeWML1fJiLkKruI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.0/go.mod h1:EhC/83j8/hL/UB1WmExo3gkElaja/KlmZM/gl1rTfjM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12 h1:uAiiHnWihGP2rVp64fHwzLDrswGjEjsPszwRYMiYQPU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.12/go.mod h1:fUTHpOXqRQpXvEpDPSa3zxCc2fnpW6YnBoba+eQr+Bg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32 h1:kvN1jPHr9UffqqG3bSgZ8tx4+1zKVHz/Ktw/BwW6hX8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.32/go.mod h1:QmMEM7es84EUkbYWcpnkx8i5EW2uERPfrTFeOch128Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 h1:auGDJ0aLZahF5SPvkJ6WcUuX7iQ7kyl2MamV7Tm8QBk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31/go.mod h1:3+lloe3sZuBQw1aBc5MyndvodzQlyqCZ7x1QPDHaWP4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0 h1:Wgjft9X4W5pMeuqgPCHIQtbZ87wsgom7S5F8obreg+c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.0/go.mod h1:FWNzS4+zcWAP05IF7TDYTY1ysZAzIvogxWaDT9p8fsA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.0 h1:Flb+Uw+ewvmbZiaXEl+aIs1HygnwztxZmhkCe1b+YQg=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.0/go.mod h1:6SOWLiobcZZshbmECRTADIRYliPL0etqFSigauQEeT0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.0 h1:agnjK56/1jtGPehxV8QZ/AYHV++pEfl7CpYbWjHjBDc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.0/go.mod h1:TC9BubuFMVScIU+TLKamO6VZiYTkYoEHqlSQwAe2omw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.0 h1:g0Rr6COTBEaIG9TFQ0GmRkPWOGuDfySGSq2PlMcclrY=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.0/go.mod h1:XO/VcyoQ8nKyKfFW/3DMsRQXsfh/052tHTWmg3xBXRg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.0 h1:HI1YIL5Q9FtucxF5tcNpzCEyLnkeUcqg6xtOx8u09S4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.0/go.mod h1:G8SbvL0rFk4WOJroU8tKBczhsbhj2p/YY7qeJezJ3CI=
|
||||
github.com/aws/smithy-go v1.14.0 h1:+X90sB94fizKjDmwb4vyl2cTTPXTE5E2G/1mjByb0io=
|
||||
github.com/aws/smithy-go v1.14.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aws/aws-sdk-go v1.45.3 h1:Q8BksXg2ZUu2dCbA62+UCEtfvqsW8EO4tzt2IVeYAws=
|
||||
github.com/aws/aws-sdk-go v1.45.3/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.39 h1:oPVyh6fuu/u4OiW4qcuQyEtk7U7uuNBmHmJSLg1AJsQ=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.18.39/go.mod h1:+NH/ZigdPckFpgB1TRcRuWCB/Kbbvkxc/iNAKTq5RhE=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.37 h1:BvEdm09+ZEh2XtN+PVHPcYwKY3wIeB6pw7vPRM4M9/U=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.13.37/go.mod h1:ACLrdkd4CLZyXOghZ8IYumQbcooAcp2jo/s2xsFH8IM=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.83 h1:wcluDLIQ0uYaxv0fCWQRimbXkPdTgWHUD21j1CzXEwc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.83/go.mod h1:nGCBuon134gW67yAtxHKV73x+tAcY/xG4ZPNPDB1h/I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 h1:A42xdtStObqy7NGvzZKpnyNXvoOmm+FENobZ0/ssHWk=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 h1:pSB560BbVj9ZlJZF4WYj5zsytWHWKxg+NgyGV4B2L58=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvTZW2S44Lt9Mk2aYQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU=
|
||||
github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ=
|
||||
github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
@@ -71,16 +71,16 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/domodwyer/mailyak/v3 v3.6.1 h1:o3vjinPjPUpw/2Ng5N91JrlFDkoIKu4HTHw3U/CNemE=
|
||||
github.com/domodwyer/mailyak/v3 v3.6.1/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8=
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c=
|
||||
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20230531210528-d7324b2d74f7/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja v0.0.0-20230707174833-636fdf960de1 h1:sC/DYk3eEi5cKkpJX1vl+CpAM138dmuW7rutje9Eo4E=
|
||||
github.com/dop251/goja v0.0.0-20230707174833-636fdf960de1/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20230731184339-9ba67d720a81 h1:1e6KLyFnVS5BAENTnqll6IezpFqTDAmIsL0hkjBuMcY=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20230731184339-9ba67d720a81/go.mod h1:X2TOTJ+Uamd454RFp7ig2tmP3hQg0Z2Qk8gbVQmU0mk=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562 h1:ObbB2tzHWWAxzsG5futqeq2Ual2zYlo/+eMkSc5sn8w=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20230821135201-94e508132562/go.mod h1:X2TOTJ+Uamd454RFp7ig2tmP3hQg0Z2Qk8gbVQmU0mk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -120,6 +120,7 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
@@ -137,11 +138,11 @@ github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdf
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
||||
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA=
|
||||
github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
||||
github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg=
|
||||
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM=
|
||||
@@ -183,8 +184,8 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyex
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pocketbase/dbx v1.10.0 h1:58VIT7r6T+BnVbYVosvGBsPjQEic3/VFRYGT823vWSQ=
|
||||
github.com/pocketbase/dbx v1.10.0/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/dbx v1.10.1 h1:cw+vsyfCJD8YObOVeqb93YErnlxwYMkNZ4rwN0G0AaA=
|
||||
github.com/pocketbase/dbx v1.10.1/go.mod h1:xXRCIAKTHMgUCyCKZm55pUOdvFziJjQfXaWKhu2vhMs=
|
||||
github.com/pocketbase/tygoja v0.0.0-20230618203136-2f8d57768be1 h1:0FQXnhUhBeF/z6UDj9Zm8GQHFHJSUtqLr1h4/YXf3vc=
|
||||
github.com/pocketbase/tygoja v0.0.0-20230618203136-2f8d57768be1/go.mod h1:dOJ+pCyqm/jRn5kO/TX598J0e5xGDcJAZerK5atCrKI=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -215,17 +216,17 @@ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
gocloud.dev v0.32.0 h1:jHf8WSkByuAuXcvFt04OiiQH+N0zaRtxI6iEph8Bq8Y=
|
||||
gocloud.dev v0.32.0/go.mod h1:m/x/N9cRjDF5MD0i5TLFbKbqkGffl/qayXA9FcMT5Oc=
|
||||
gocloud.dev v0.34.0 h1:LzlQY+4l2cMtuNfwT2ht4+fiXwWf/NmPTnXUlLmGif4=
|
||||
gocloud.dev v0.34.0/go.mod h1:psKOachbnvY3DAOPbsFVmLIErwsbWPUG2H5i65D38vE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.9.0 h1:QrzfX26snvCM20hIhBwuHI/ThTg18b/+kcKdXHvnR+g=
|
||||
golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||
golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||
golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@@ -246,11 +247,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4=
|
||||
golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -274,15 +275,15 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
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.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -292,8 +293,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -305,26 +306,26 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc=
|
||||
golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.134.0 h1:ktL4Goua+UBgoP1eL1/60LwZJqa1sIzkLmvoR3hR6Gw=
|
||||
google.golang.org/api v0.134.0/go.mod h1:sjRL3UnjTx5UqNQS9EWr9N8p7xbHpy1k0XGRLCf3Spk=
|
||||
google.golang.org/api v0.138.0 h1:K/tVp05MxNVbHShRw9m7e9VJGdagNeTdMzqPH7AUqr0=
|
||||
google.golang.org/api v0.138.0/go.mod h1:4xyob8CxC+0GChNBvEUAk8VBKNvYOTWM9T3v3UfRxuY=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230717213848-3f92550aa753 h1:lCbbUxUDD+DiXx9Q6F/ttL0aAu7N2pz8XnmMm8ZW4NE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf h1:guOdSPaeFgN+jEJwTo1dQ71hdBm+yKSCCKuTRkJzcVo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I=
|
||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
@@ -362,22 +363,22 @@ lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
|
||||
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q=
|
||||
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
|
||||
modernc.org/ccgo/v3 v3.16.14 h1:af6KNtFgsVmnDYrWk3PQCS9XT6BXe7o3ZFJKkIKvXNQ=
|
||||
modernc.org/ccgo/v3 v3.16.14/go.mod h1:mPDSujUIaTNWQSG4eqKw+atqLOEbma6Ncsa94WbC9zo=
|
||||
modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0=
|
||||
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
|
||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
||||
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
|
||||
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/memory v1.7.1 h1:9J+2/GKTlV503mk3yv8QJ6oEpRCUrRy0ad8TXEPoV8M=
|
||||
modernc.org/memory v1.7.1/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sqlite v1.24.0 h1:EsClRIWHGhLTCX44p+Ri/JLD+vFGo0QGjasg2/F9TlI=
|
||||
modernc.org/sqlite v1.24.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
|
||||
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
|
||||
@@ -100,7 +100,7 @@ func init() {
|
||||
return err
|
||||
}
|
||||
|
||||
// inserts the system profiles collection
|
||||
// inserts the system users collection
|
||||
// -----------------------------------------------------------
|
||||
usersCollection := &models.Collection{}
|
||||
usersCollection.MarkAsNew()
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
)
|
||||
|
||||
// Resave all view collections to ensure that the proper id normalization is applied.
|
||||
// (see https://github.com/pocketbase/pocketbase/issues/3110)
|
||||
func init() {
|
||||
AppMigrations.Register(func(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
collections, err := dao.FindCollectionsByType(models.CollectionTypeView)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, collection := range collections {
|
||||
// ignore errors to allow users to adjust
|
||||
// the view queries after app start
|
||||
dao.SaveCollection(collection)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
)
|
||||
|
||||
// Copy the now deprecated RelationOptions.DisplayFields values from
|
||||
// all relation fields and register its value as Presentable under
|
||||
// the specific field in the related collection.
|
||||
//
|
||||
// If there is more than one relation to a single collection with explicitly
|
||||
// set DisplayFields only one of the configuration will be copied.
|
||||
func init() {
|
||||
AppMigrations.Register(func(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
collections := []*models.Collection{}
|
||||
if err := dao.CollectionQuery().All(&collections); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
indexedCollections := make(map[string]*models.Collection, len(collections))
|
||||
for _, collection := range collections {
|
||||
indexedCollections[collection.Id] = collection
|
||||
}
|
||||
|
||||
for _, collection := range indexedCollections {
|
||||
for _, f := range collection.Schema.Fields() {
|
||||
if f.Type != schema.FieldTypeRelation {
|
||||
continue
|
||||
}
|
||||
|
||||
options, ok := f.Options.(*schema.RelationOptions)
|
||||
if !ok || len(options.DisplayFields) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
relCollection, ok := indexedCollections[options.CollectionId]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, name := range options.DisplayFields {
|
||||
relField := relCollection.Schema.GetFieldByName(name)
|
||||
if relField != nil {
|
||||
relField.Presentable = true
|
||||
}
|
||||
}
|
||||
|
||||
// only raw model save
|
||||
if err := dao.Save(relCollection); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
}
|
||||
@@ -132,6 +132,10 @@ type SchemaField struct {
|
||||
Type string `form:"type" json:"type"`
|
||||
Required bool `form:"required" json:"required"`
|
||||
|
||||
// Presentable indicates whether the field is suitable for
|
||||
// visualization purposes (eg. in the Admin UI relation views).
|
||||
Presentable bool `form:"presentable" json:"presentable"`
|
||||
|
||||
// Deprecated: This field is no-op and will be removed in future versions.
|
||||
// Please use the collection.Indexes field to define a unique constraint.
|
||||
Unique bool `form:"unique" json:"unique"`
|
||||
@@ -452,21 +456,36 @@ func (o *TextOptions) checkRegex(value any) error {
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type NumberOptions struct {
|
||||
Min *float64 `form:"min" json:"min"`
|
||||
Max *float64 `form:"max" json:"max"`
|
||||
Min *float64 `form:"min" json:"min"`
|
||||
Max *float64 `form:"max" json:"max"`
|
||||
NoDecimal bool `form:"noDecimal" json:"noDecimal"`
|
||||
}
|
||||
|
||||
func (o NumberOptions) Validate() error {
|
||||
var maxRules []validation.Rule
|
||||
if o.Min != nil && o.Max != nil {
|
||||
maxRules = append(maxRules, validation.Min(*o.Min))
|
||||
maxRules = append(maxRules, validation.Min(*o.Min), validation.By(o.checkNoDecimal))
|
||||
}
|
||||
|
||||
return validation.ValidateStruct(&o,
|
||||
validation.Field(&o.Min, validation.By(o.checkNoDecimal)),
|
||||
validation.Field(&o.Max, maxRules...),
|
||||
)
|
||||
}
|
||||
|
||||
func (o *NumberOptions) checkNoDecimal(value any) error {
|
||||
v, _ := value.(*float64)
|
||||
if v == nil || !o.NoDecimal {
|
||||
return nil // nothing to check
|
||||
}
|
||||
|
||||
if *v != float64(int64(*v)) {
|
||||
return validation.NewError("validation_no_decimal_constraint", "Decimal numbers are not allowed.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type BoolOptions struct {
|
||||
@@ -519,6 +538,12 @@ func (o UrlOptions) Validate() error {
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
type EditorOptions struct {
|
||||
// ConvertUrls is usually used to instruct the editor whether to
|
||||
// apply url conversion (eg. stripping the domain name in case the
|
||||
// urls are using the same domain as the one where the editor is loaded).
|
||||
//
|
||||
// (see also https://www.tiny.cloud/docs/tinymce/6/url-handling/#convert_urls)
|
||||
ConvertUrls bool `form:"convertUrls" json:"convertUrls"`
|
||||
}
|
||||
|
||||
func (o EditorOptions) Validate() error {
|
||||
@@ -645,7 +670,8 @@ type RelationOptions struct {
|
||||
// If nil no limits are applied.
|
||||
MaxSelect *int `form:"maxSelect" json:"maxSelect"`
|
||||
|
||||
// DisplayFields is optional slice of collection field names used for UI purposes.
|
||||
// Deprecated: This field is no-op and will be removed in future versions.
|
||||
// Instead use the individula SchemaField.Presentable option for each field in the relation collection.
|
||||
DisplayFields []string `form:"displayFields" json:"displayFields"`
|
||||
}
|
||||
|
||||
|
||||
@@ -129,19 +129,19 @@ func TestSchemaFieldColDefinition(t *testing.T) {
|
||||
|
||||
func TestSchemaFieldString(t *testing.T) {
|
||||
f := schema.SchemaField{
|
||||
Id: "abc",
|
||||
Name: "test",
|
||||
Type: schema.FieldTypeText,
|
||||
Required: true,
|
||||
Unique: false,
|
||||
System: true,
|
||||
Id: "abc",
|
||||
Name: "test",
|
||||
Type: schema.FieldTypeText,
|
||||
Required: true,
|
||||
Presentable: true,
|
||||
System: true,
|
||||
Options: &schema.TextOptions{
|
||||
Pattern: "test",
|
||||
},
|
||||
}
|
||||
|
||||
result := f.String()
|
||||
expected := `{"system":true,"id":"abc","name":"test","type":"text","required":true,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`
|
||||
expected := `{"system":true,"id":"abc","name":"test","type":"text","required":true,"presentable":true,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`
|
||||
|
||||
if result != expected {
|
||||
t.Errorf("Expected \n%v, got \n%v", expected, result)
|
||||
@@ -156,19 +156,19 @@ func TestSchemaFieldMarshalJSON(t *testing.T) {
|
||||
// empty
|
||||
{
|
||||
schema.SchemaField{},
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"presentable":false,"unique":false,"options":null}`,
|
||||
},
|
||||
// without defined options
|
||||
{
|
||||
schema.SchemaField{
|
||||
Id: "abc",
|
||||
Name: "test",
|
||||
Type: schema.FieldTypeText,
|
||||
Required: true,
|
||||
Unique: false,
|
||||
System: true,
|
||||
Id: "abc",
|
||||
Name: "test",
|
||||
Type: schema.FieldTypeText,
|
||||
Required: true,
|
||||
Presentable: true,
|
||||
System: true,
|
||||
},
|
||||
`{"system":true,"id":"abc","name":"test","type":"text","required":true,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
|
||||
`{"system":true,"id":"abc","name":"test","type":"text","required":true,"presentable":true,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
|
||||
},
|
||||
// with defined options
|
||||
{
|
||||
@@ -182,7 +182,7 @@ func TestSchemaFieldMarshalJSON(t *testing.T) {
|
||||
Pattern: "test",
|
||||
},
|
||||
},
|
||||
`{"system":true,"id":"","name":"test","type":"text","required":true,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
|
||||
`{"system":true,"id":"","name":"test","type":"text","required":true,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -207,32 +207,32 @@ func TestSchemaFieldUnmarshalJSON(t *testing.T) {
|
||||
{
|
||||
nil,
|
||||
true,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"presentable":false,"unique":false,"options":null}`,
|
||||
},
|
||||
{
|
||||
[]byte{},
|
||||
true,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"presentable":false,"unique":false,"options":null}`,
|
||||
},
|
||||
{
|
||||
[]byte(`{"system": true}`),
|
||||
true,
|
||||
`{"system":true,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
|
||||
`{"system":true,"id":"","name":"","type":"","required":false,"presentable":false,"unique":false,"options":null}`,
|
||||
},
|
||||
{
|
||||
[]byte(`{"invalid"`),
|
||||
true,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"presentable":false,"unique":false,"options":null}`,
|
||||
},
|
||||
{
|
||||
[]byte(`{"type":"text","system":true}`),
|
||||
false,
|
||||
`{"system":true,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
|
||||
`{"system":true,"id":"","name":"","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
|
||||
},
|
||||
{
|
||||
[]byte(`{"type":"text","options":{"pattern":"test"}}`),
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
|
||||
`{"system":false,"id":"","name":"","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -470,72 +470,72 @@ func TestSchemaFieldInitOptions(t *testing.T) {
|
||||
{
|
||||
schema.SchemaField{},
|
||||
true,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"unique":false,"options":null}`,
|
||||
`{"system":false,"id":"","name":"","type":"","required":false,"presentable":false,"unique":false,"options":null}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: "unknown"},
|
||||
true,
|
||||
`{"system":false,"id":"","name":"","type":"unknown","required":false,"unique":false,"options":null}`,
|
||||
`{"system":false,"id":"","name":"","type":"unknown","required":false,"presentable":false,"unique":false,"options":null}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeText},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
|
||||
`{"system":false,"id":"","name":"","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeNumber},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"number","required":false,"unique":false,"options":{"min":null,"max":null}}`,
|
||||
`{"system":false,"id":"","name":"","type":"number","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"noDecimal":false}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeBool},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"bool","required":false,"unique":false,"options":{}}`,
|
||||
`{"system":false,"id":"","name":"","type":"bool","required":false,"presentable":false,"unique":false,"options":{}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeEmail},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"email","required":false,"unique":false,"options":{"exceptDomains":null,"onlyDomains":null}}`,
|
||||
`{"system":false,"id":"","name":"","type":"email","required":false,"presentable":false,"unique":false,"options":{"exceptDomains":null,"onlyDomains":null}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeUrl},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"url","required":false,"unique":false,"options":{"exceptDomains":null,"onlyDomains":null}}`,
|
||||
`{"system":false,"id":"","name":"","type":"url","required":false,"presentable":false,"unique":false,"options":{"exceptDomains":null,"onlyDomains":null}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeEditor},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"editor","required":false,"unique":false,"options":{}}`,
|
||||
`{"system":false,"id":"","name":"","type":"editor","required":false,"presentable":false,"unique":false,"options":{"convertUrls":false}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeDate},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"date","required":false,"unique":false,"options":{"min":"","max":""}}`,
|
||||
`{"system":false,"id":"","name":"","type":"date","required":false,"presentable":false,"unique":false,"options":{"min":"","max":""}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"select","required":false,"unique":false,"options":{"maxSelect":0,"values":null}}`,
|
||||
`{"system":false,"id":"","name":"","type":"select","required":false,"presentable":false,"unique":false,"options":{"maxSelect":0,"values":null}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeJson},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"json","required":false,"unique":false,"options":{}}`,
|
||||
`{"system":false,"id":"","name":"","type":"json","required":false,"presentable":false,"unique":false,"options":{}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeFile},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"file","required":false,"unique":false,"options":{"maxSelect":0,"maxSize":0,"mimeTypes":null,"thumbs":null,"protected":false}}`,
|
||||
`{"system":false,"id":"","name":"","type":"file","required":false,"presentable":false,"unique":false,"options":{"maxSelect":0,"maxSize":0,"mimeTypes":null,"thumbs":null,"protected":false}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"relation","required":false,"unique":false,"options":{"collectionId":"","cascadeDelete":false,"minSelect":null,"maxSelect":null,"displayFields":null}}`,
|
||||
`{"system":false,"id":"","name":"","type":"relation","required":false,"presentable":false,"unique":false,"options":{"collectionId":"","cascadeDelete":false,"minSelect":null,"maxSelect":null,"displayFields":null}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeUser},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"user","required":false,"unique":false,"options":{"maxSelect":0,"cascadeDelete":false}}`,
|
||||
`{"system":false,"id":"","name":"","type":"user","required":false,"presentable":false,"unique":false,"options":{"maxSelect":0,"cascadeDelete":false}}`,
|
||||
},
|
||||
{
|
||||
schema.SchemaField{
|
||||
@@ -543,7 +543,7 @@ func TestSchemaFieldInitOptions(t *testing.T) {
|
||||
Options: &schema.TextOptions{Pattern: "test"},
|
||||
},
|
||||
false,
|
||||
`{"system":false,"id":"","name":"","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
|
||||
`{"system":false,"id":"","name":"","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1743,8 +1743,12 @@ func TestTextOptionsValidate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNumberOptionsValidate(t *testing.T) {
|
||||
number1 := 10.0
|
||||
number2 := 20.0
|
||||
int1 := 10.0
|
||||
int2 := 20.0
|
||||
|
||||
decimal1 := 10.5
|
||||
decimal2 := 20.5
|
||||
|
||||
scenarios := []fieldOptionsScenario{
|
||||
{
|
||||
"empty",
|
||||
@@ -1754,23 +1758,41 @@ func TestNumberOptionsValidate(t *testing.T) {
|
||||
{
|
||||
"max - without min",
|
||||
schema.NumberOptions{
|
||||
Max: &number1,
|
||||
Max: &int1,
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"max - failure with min",
|
||||
schema.NumberOptions{
|
||||
Min: &number2,
|
||||
Max: &number1,
|
||||
Min: &int2,
|
||||
Max: &int1,
|
||||
},
|
||||
[]string{"max"},
|
||||
},
|
||||
{
|
||||
"max - success with min",
|
||||
schema.NumberOptions{
|
||||
Min: &number1,
|
||||
Max: &number2,
|
||||
Min: &int1,
|
||||
Max: &int2,
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"NoDecimal range failure",
|
||||
schema.NumberOptions{
|
||||
Min: &decimal1,
|
||||
Max: &decimal2,
|
||||
NoDecimal: true,
|
||||
},
|
||||
[]string{"min", "max"},
|
||||
},
|
||||
{
|
||||
"NoDecimal range success",
|
||||
schema.NumberOptions{
|
||||
Min: &int1,
|
||||
Max: &int2,
|
||||
NoDecimal: true,
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
|
||||
@@ -286,7 +286,7 @@ func TestSchemaMarshalJSON(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := `[{"system":false,"id":"f1id","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}},{"system":false,"id":"f2id","name":"test2","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`
|
||||
expected := `[{"system":false,"id":"f1id","name":"test1","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}},{"system":false,"id":"f2id","name":"test2","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`
|
||||
|
||||
if string(result) != expected {
|
||||
t.Fatalf("Expected %s, got %s", expected, string(result))
|
||||
@@ -354,7 +354,7 @@ func TestSchemaValue(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := `[{"system":false,"id":"f1id","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`
|
||||
expected := `[{"system":false,"id":"f1id","name":"test1","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`
|
||||
|
||||
if v2 != expected {
|
||||
t.Fatalf("Expected %v, got %v", expected, v2)
|
||||
@@ -377,21 +377,21 @@ func TestSchemaScan(t *testing.T) {
|
||||
{`[{}]`, true, `[]`},
|
||||
// unknown field type
|
||||
{
|
||||
`[{"system":false,"id":"123","name":"test1","type":"unknown","required":false,"unique":false}]`,
|
||||
`[{"system":false,"id":"123","name":"test1","type":"unknown","required":false,"presentable":false,"unique":false}]`,
|
||||
true,
|
||||
`[]`,
|
||||
},
|
||||
// without options
|
||||
{
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false}]`,
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"presentable":false,"unique":false}]`,
|
||||
false,
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":""}}]`,
|
||||
},
|
||||
// with options
|
||||
{
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`,
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`,
|
||||
false,
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`,
|
||||
`[{"system":false,"id":"123","name":"test1","type":"text","required":false,"presentable":false,"unique":false,"options":{"min":null,"max":null,"pattern":"test"}}]`,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -371,6 +371,12 @@ type SmtpConfig struct {
|
||||
// When set to false StartTLS command is send, leaving the server
|
||||
// to decide whether to upgrade the connection or not.
|
||||
Tls bool `form:"tls" json:"tls"`
|
||||
|
||||
// LocalName is optional domain name or IP address used for the
|
||||
// EHLO/HELO exchange (if not explicitly set, defaults to "localhost").
|
||||
//
|
||||
// This is required only by some SMTP servers, such as Gmail SMTP-relay.
|
||||
LocalName string `form:"localName" json:"localName"`
|
||||
}
|
||||
|
||||
// Validate makes SmtpConfig validatable by implementing [validation.Validatable] interface.
|
||||
@@ -393,6 +399,7 @@ func (c SmtpConfig) Validate() error {
|
||||
// validation.When(c.Enabled, validation.Required),
|
||||
validation.In(mailer.SmtpAuthLogin, mailer.SmtpAuthPlain),
|
||||
),
|
||||
validation.Field(&c.LocalName, is.Host),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -477,6 +477,26 @@ func TestSmtpConfigValidate(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
// invalid ehlo/helo name
|
||||
{
|
||||
settings.SmtpConfig{
|
||||
Enabled: true,
|
||||
Host: "example.com",
|
||||
Port: 100,
|
||||
LocalName: "invalid!",
|
||||
},
|
||||
true,
|
||||
},
|
||||
// valid ehlo/helo name
|
||||
{
|
||||
settings.SmtpConfig{
|
||||
Enabled: true,
|
||||
Host: "example.com",
|
||||
Port: 100,
|
||||
LocalName: "example.com",
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, scenario := range scenarios {
|
||||
|
||||
+44
-1
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -22,6 +23,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/forms"
|
||||
"github.com/pocketbase/pocketbase/mails"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
"github.com/pocketbase/pocketbase/tokens"
|
||||
@@ -31,6 +33,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/inflector"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/mailer"
|
||||
"github.com/pocketbase/pocketbase/tools/rest"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -111,10 +114,14 @@ func cronBinds(app core.App, loader *goja.Runtime, executors *vmsPool) {
|
||||
pr := goja.MustCompile("", "{("+handler+").apply(undefined)}", true)
|
||||
|
||||
err := scheduler.Add(jobId, cronExpr, func() {
|
||||
executors.run(func(executor *goja.Runtime) error {
|
||||
err := executors.run(func(executor *goja.Runtime) error {
|
||||
_, err := executor.RunProgram(pr)
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil && app.IsDebug() {
|
||||
fmt.Println("[cronAdd] failed to execute cron job " + jobId + ": " + err.Error())
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
panic("[cronAdd] failed to register cron job " + jobId + ": " + err.Error())
|
||||
@@ -272,6 +279,21 @@ func wrapMiddlewares(executors *vmsPool, rawMiddlewares ...goja.Value) ([]echo.M
|
||||
func baseBinds(vm *goja.Runtime) {
|
||||
vm.SetFieldNameMapper(FieldMapper{})
|
||||
|
||||
vm.Set("readerToString", func(r io.Reader, maxBytes int) (string, error) {
|
||||
if maxBytes == 0 {
|
||||
maxBytes = rest.DefaultMaxMemory
|
||||
}
|
||||
|
||||
limitReader := io.LimitReader(r, int64(maxBytes))
|
||||
|
||||
bodyBytes, readErr := io.ReadAll(limitReader)
|
||||
if readErr != nil {
|
||||
return "", readErr
|
||||
}
|
||||
|
||||
return string(bodyBytes), nil
|
||||
})
|
||||
|
||||
vm.Set("arrayOf", func(model any) any {
|
||||
mt := reflect.TypeOf(model)
|
||||
st := reflect.SliceOf(mt)
|
||||
@@ -415,6 +437,19 @@ func dbxBinds(vm *goja.Runtime) {
|
||||
obj.Set("notBetween", dbx.NotBetween)
|
||||
}
|
||||
|
||||
func mailsBinds(vm *goja.Runtime) {
|
||||
obj := vm.NewObject()
|
||||
vm.Set("$mails", obj)
|
||||
|
||||
// admin
|
||||
obj.Set("sendAdminPasswordReset", mails.SendAdminPasswordReset)
|
||||
|
||||
// record
|
||||
obj.Set("sendRecordPasswordReset", mails.SendRecordPasswordReset)
|
||||
obj.Set("sendRecordVerification", mails.SendRecordVerification)
|
||||
obj.Set("sendRecordChangeEmail", mails.SendRecordChangeEmail)
|
||||
}
|
||||
|
||||
func tokensBinds(vm *goja.Runtime) {
|
||||
obj := vm.NewObject()
|
||||
vm.Set("$tokens", obj)
|
||||
@@ -436,6 +471,14 @@ func securityBinds(vm *goja.Runtime) {
|
||||
obj := vm.NewObject()
|
||||
vm.Set("$security", obj)
|
||||
|
||||
// crypto
|
||||
obj.Set("md5", security.MD5)
|
||||
obj.Set("sha256", security.SHA256)
|
||||
obj.Set("sha512", security.SHA512)
|
||||
obj.Set("hs256", security.HS256)
|
||||
obj.Set("hs512", security.HS512)
|
||||
obj.Set("equal", security.Equal)
|
||||
|
||||
// random
|
||||
obj.Set("randomString", security.RandomString)
|
||||
obj.Set("randomStringWithAlphabet", security.RandomStringWithAlphabet)
|
||||
|
||||
+136
-20
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/filesystem"
|
||||
"github.com/pocketbase/pocketbase/tools/mailer"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
func testBindsCount(vm *goja.Runtime, namespace string, count int, t *testing.T) {
|
||||
@@ -45,7 +46,27 @@ func TestBaseBindsCount(t *testing.T) {
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
|
||||
testBindsCount(vm, "this", 13, t)
|
||||
testBindsCount(vm, "this", 14, t)
|
||||
}
|
||||
|
||||
func TestBaseBindsReaderToString(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
vm.Set("reader", strings.NewReader("test"))
|
||||
|
||||
_, err := vm.RunString(`
|
||||
let result = readerToString(reader)
|
||||
|
||||
if (result != "test") {
|
||||
throw new Error('Expected "test", got ' + result);
|
||||
}
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseBindsRecord(t *testing.T) {
|
||||
@@ -484,6 +505,60 @@ func TestDbxBinds(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMailsBindsCount(t *testing.T) {
|
||||
vm := goja.New()
|
||||
mailsBinds(vm)
|
||||
|
||||
testBindsCount(vm, "$mails", 4, t)
|
||||
}
|
||||
|
||||
func TestMailsBinds(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
admin, err := app.Dao().FindAdminByEmail("test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
record, err := app.Dao().FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
mailsBinds(vm)
|
||||
vm.Set("$app", app)
|
||||
vm.Set("admin", admin)
|
||||
vm.Set("record", record)
|
||||
|
||||
_, vmErr := vm.RunString(`
|
||||
$mails.sendAdminPasswordReset($app, admin);
|
||||
if (!$app.testMailer.lastMessage.html.includes("/_/#/confirm-password-reset/")) {
|
||||
throw new Error("Expected admin password reset email")
|
||||
}
|
||||
|
||||
$mails.sendRecordPasswordReset($app, record);
|
||||
if (!$app.testMailer.lastMessage.html.includes("/_/#/auth/confirm-password-reset/")) {
|
||||
throw new Error("Expected record password reset email")
|
||||
}
|
||||
|
||||
$mails.sendRecordVerification($app, record);
|
||||
if (!$app.testMailer.lastMessage.html.includes("/_/#/auth/confirm-verification/")) {
|
||||
throw new Error("Expected record verification email")
|
||||
}
|
||||
|
||||
$mails.sendRecordChangeEmail($app, record, "new@example.com");
|
||||
if (!$app.testMailer.lastMessage.html.includes("/_/#/auth/confirm-email-change/")) {
|
||||
throw new Error("Expected record email change email")
|
||||
}
|
||||
`)
|
||||
if vmErr != nil {
|
||||
t.Fatal(vmErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokensBindsCount(t *testing.T) {
|
||||
vm := goja.New()
|
||||
tokensBinds(vm)
|
||||
@@ -506,11 +581,11 @@ func TestTokensBinds(t *testing.T) {
|
||||
}
|
||||
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
tokensBinds(vm)
|
||||
vm.Set("$app", app)
|
||||
vm.Set("admin", admin)
|
||||
vm.Set("record", record)
|
||||
baseBinds(vm)
|
||||
tokensBinds(vm)
|
||||
|
||||
sceneraios := []struct {
|
||||
js string
|
||||
@@ -568,7 +643,44 @@ func TestSecurityBindsCount(t *testing.T) {
|
||||
vm := goja.New()
|
||||
securityBinds(vm)
|
||||
|
||||
testBindsCount(vm, "$security", 9, t)
|
||||
testBindsCount(vm, "$security", 15, t)
|
||||
}
|
||||
|
||||
func TestSecurityCryptoBinds(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
securityBinds(vm)
|
||||
|
||||
sceneraios := []struct {
|
||||
js string
|
||||
expected string
|
||||
}{
|
||||
{`$security.md5("123")`, "202cb962ac59075b964b07152d234b70"},
|
||||
{`$security.sha256("123")`, "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"},
|
||||
{`$security.sha512("123")`, "3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2"},
|
||||
{`$security.hs256("hello", "test")`, "f151ea24bda91a18e89b8bb5793ef324b2a02133cce15a28a719acbd2e58a986"},
|
||||
{`$security.hs512("hello", "test")`, "44f280e11103e295c26cd61dd1cdd8178b531b860466867c13b1c37a26b6389f8af110efbe0bb0717b9d9c87f6fe1c97b3b1690936578890e5669abf279fe7fd"},
|
||||
{`$security.equal("abc", "abc")`, "true"},
|
||||
{`$security.equal("abc", "abcd")`, "false"},
|
||||
}
|
||||
|
||||
for _, s := range sceneraios {
|
||||
t.Run(s.js, func(t *testing.T) {
|
||||
result, err := vm.RunString(s.js)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to execute js script, got %v", err)
|
||||
}
|
||||
|
||||
v := cast.ToString(result.Export())
|
||||
|
||||
if v != s.expected {
|
||||
t.Fatalf("Expected %v \ngot \n%v", s.expected, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecurityRandomStringBinds(t *testing.T) {
|
||||
@@ -590,16 +702,18 @@ func TestSecurityRandomStringBinds(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, s := range sceneraios {
|
||||
result, err := vm.RunString(s.js)
|
||||
if err != nil {
|
||||
t.Fatalf("[%s] Failed to execute js script, got %v", s.js, err)
|
||||
}
|
||||
t.Run(s.js, func(t *testing.T) {
|
||||
result, err := vm.RunString(s.js)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to execute js script, got %v", err)
|
||||
}
|
||||
|
||||
v, _ := result.Export().(string)
|
||||
v, _ := result.Export().(string)
|
||||
|
||||
if len(v) != s.length {
|
||||
t.Fatalf("[%s] Expected %d length string, \ngot \n%v", s.js, s.length, v)
|
||||
}
|
||||
if len(v) != s.length {
|
||||
t.Fatalf("Expected %d length string, \ngot \n%v", s.length, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,16 +744,18 @@ func TestSecurityJWTBinds(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, s := range sceneraios {
|
||||
result, err := vm.RunString(s.js)
|
||||
if err != nil {
|
||||
t.Fatalf("[%s] Failed to execute js script, got %v", s.js, err)
|
||||
}
|
||||
t.Run(s.js, func(t *testing.T) {
|
||||
result, err := vm.RunString(s.js)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to execute js script, got %v", err)
|
||||
}
|
||||
|
||||
raw, _ := json.Marshal(result.Export())
|
||||
raw, _ := json.Marshal(result.Export())
|
||||
|
||||
if string(raw) != s.expected {
|
||||
t.Fatalf("[%s] Expected \n%s, \ngot \n%s", s.js, s.expected, raw)
|
||||
}
|
||||
if string(raw) != s.expected {
|
||||
t.Fatalf("Expected \n%s, \ngot \n%s", s.expected, raw)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3440
-3309
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,7 @@ const heading = `
|
||||
*
|
||||
* ` + "```" + `js
|
||||
* // prints "Hello world!" on every 30 minutes
|
||||
* cronAdd("hello", "*\/30 * * * *", (c) => {
|
||||
* cronAdd("hello", "*\/30 * * * *", () => {
|
||||
* console.log("Hello world!")
|
||||
* })
|
||||
* ` + "```" + `
|
||||
@@ -180,6 +180,24 @@ declare var $app: appWithoutHooks
|
||||
*/
|
||||
declare var $template: template.Registry
|
||||
|
||||
/**
|
||||
* readerToString reads the content of the specified io.Reader until
|
||||
* EOF or maxBytes are reached.
|
||||
*
|
||||
* If maxBytes is not specified it will read up to 32MB.
|
||||
*
|
||||
* Note that after this call the reader can't be used anymore.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ` + "```" + `js
|
||||
* const rawBody = readerToString(c.request().body)
|
||||
* ` + "```" + `
|
||||
*
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare function readerToString(reader: any, maxBytes?: number): string;
|
||||
|
||||
/**
|
||||
* arrayOf creates a placeholder array of the specified models.
|
||||
* Usually used to populate DB result into an array of models.
|
||||
@@ -498,6 +516,12 @@ declare namespace $security {
|
||||
let createJWT: security.newJWT
|
||||
let encrypt: security.encrypt
|
||||
let decrypt: security.decrypt
|
||||
let hs256: security.hs256
|
||||
let hs512: security.hs512
|
||||
let equal: security.equal
|
||||
let md5: security.md5
|
||||
let sha256: security.sha256
|
||||
let sha512: security.sha512
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@@ -127,7 +127,7 @@ func Register(app core.App, config Config) error {
|
||||
})
|
||||
|
||||
if err := p.registerMigrations(); err != nil {
|
||||
return fmt.Errorf("registerHooks: %w", err)
|
||||
return fmt.Errorf("registerMigrations: %w", err)
|
||||
}
|
||||
|
||||
if err := p.registerHooks(); err != nil {
|
||||
@@ -161,12 +161,9 @@ func (p *plugin) registerMigrations() error {
|
||||
dbxBinds(vm)
|
||||
tokensBinds(vm)
|
||||
securityBinds(vm)
|
||||
// note: disallow for now and give the authors of custom SaaS offerings
|
||||
// some time to adjust their code to avoid eventual security issues
|
||||
//
|
||||
// osBinds(vm)
|
||||
// filepathBinds(vm)
|
||||
// httpClientBinds(vm)
|
||||
osBinds(vm)
|
||||
filepathBinds(vm)
|
||||
httpClientBinds(vm)
|
||||
|
||||
vm.Set("migrate", func(up, down func(db dbx.Builder) error) {
|
||||
m.AppMigrations.Register(up, down, file)
|
||||
@@ -236,6 +233,7 @@ func (p *plugin) registerHooks() error {
|
||||
requireRegistry.Enable(vm)
|
||||
console.Enable(vm)
|
||||
process.Enable(vm)
|
||||
|
||||
baseBinds(vm)
|
||||
dbxBinds(vm)
|
||||
filesystemBinds(vm)
|
||||
@@ -246,6 +244,8 @@ func (p *plugin) registerHooks() error {
|
||||
httpClientBinds(vm)
|
||||
formsBinds(vm)
|
||||
apisBinds(vm)
|
||||
mailsBinds(vm)
|
||||
|
||||
vm.Set("$app", p.app)
|
||||
vm.Set("$template", templateRegistry)
|
||||
vm.Set("__hooks", absHooksDir)
|
||||
|
||||
@@ -381,8 +381,10 @@ migrate((db) => {
|
||||
|
||||
collection.name = "test456_update"
|
||||
collection.type = "base"
|
||||
collection.listRule = null
|
||||
collection.deleteRule = "updated > 0 && @request.auth.id != ''"
|
||||
collection.listRule = "@request.auth.id != ''"
|
||||
collection.createRule = "id = \"nil_update\""
|
||||
collection.updateRule = "id = \"2_update\""
|
||||
collection.deleteRule = null
|
||||
collection.options = {}
|
||||
collection.indexes = [
|
||||
"create index test1 on test456_update (f1_name)"
|
||||
@@ -398,6 +400,7 @@ migrate((db) => {
|
||||
"name": "f4_name",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
@@ -413,10 +416,12 @@ migrate((db) => {
|
||||
"name": "f2_name_new",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": true,
|
||||
"options": {
|
||||
"min": 10,
|
||||
"max": null
|
||||
"max": null,
|
||||
"noDecimal": false
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -428,7 +433,9 @@ migrate((db) => {
|
||||
collection.name = "test456"
|
||||
collection.type = "auth"
|
||||
collection.listRule = "@request.auth.id != '' && created > 0"
|
||||
collection.deleteRule = null
|
||||
collection.createRule = null
|
||||
collection.updateRule = "id = \"2\""
|
||||
collection.deleteRule = "id = \"3\""
|
||||
collection.options = {
|
||||
"allowEmailAuth": false,
|
||||
"allowOAuth2Auth": false,
|
||||
@@ -450,6 +457,7 @@ migrate((db) => {
|
||||
"name": "f3_name",
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {}
|
||||
}))
|
||||
@@ -464,10 +472,12 @@ migrate((db) => {
|
||||
"name": "f2_name",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": true,
|
||||
"options": {
|
||||
"min": 10,
|
||||
"max": null
|
||||
"max": null,
|
||||
"noDecimal": false
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -503,9 +513,13 @@ func init() {
|
||||
|
||||
collection.Type = "base"
|
||||
|
||||
collection.ListRule = nil
|
||||
collection.ListRule = types.Pointer("@request.auth.id != ''")
|
||||
|
||||
collection.DeleteRule = types.Pointer("updated > 0 && @request.auth.id != ''")
|
||||
collection.CreateRule = types.Pointer("id = \"nil_update\"")
|
||||
|
||||
collection.UpdateRule = types.Pointer("id = \"2_update\"")
|
||||
|
||||
collection.DeleteRule = nil
|
||||
|
||||
options := map[string]any{}
|
||||
json.Unmarshal([]byte(` + "`" + `{}` + "`" + `), &options)
|
||||
@@ -526,6 +540,7 @@ func init() {
|
||||
"name": "f4_name",
|
||||
"type": "text",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": null,
|
||||
@@ -543,10 +558,12 @@ func init() {
|
||||
"name": "f2_name_new",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": true,
|
||||
"options": {
|
||||
"min": 10,
|
||||
"max": null
|
||||
"max": null,
|
||||
"noDecimal": false
|
||||
}
|
||||
}` + "`" + `), edit_f2_name_new)
|
||||
collection.Schema.AddField(edit_f2_name_new)
|
||||
@@ -566,7 +583,11 @@ func init() {
|
||||
|
||||
collection.ListRule = types.Pointer("@request.auth.id != '' && created > 0")
|
||||
|
||||
collection.DeleteRule = nil
|
||||
collection.CreateRule = nil
|
||||
|
||||
collection.UpdateRule = types.Pointer("id = \"2\"")
|
||||
|
||||
collection.DeleteRule = types.Pointer("id = \"3\"")
|
||||
|
||||
options := map[string]any{}
|
||||
json.Unmarshal([]byte(` + "`" + `{
|
||||
@@ -593,6 +614,7 @@ func init() {
|
||||
"name": "f3_name",
|
||||
"type": "bool",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {}
|
||||
}` + "`" + `), del_f3_name)
|
||||
@@ -609,10 +631,12 @@ func init() {
|
||||
"name": "f2_name",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": true,
|
||||
"options": {
|
||||
"min": 10,
|
||||
"max": null
|
||||
"max": null,
|
||||
"noDecimal": false
|
||||
}
|
||||
}` + "`" + `), edit_f2_name_new)
|
||||
collection.Schema.AddField(edit_f2_name_new)
|
||||
@@ -645,6 +669,9 @@ func init() {
|
||||
collection.Updated = collection.Created
|
||||
collection.ListRule = types.Pointer("@request.auth.id != '' && created > 0")
|
||||
collection.ViewRule = types.Pointer(`id = "1"`)
|
||||
collection.UpdateRule = types.Pointer(`id = "2"`)
|
||||
collection.CreateRule = nil
|
||||
collection.DeleteRule = types.Pointer(`id = "3"`)
|
||||
collection.Indexes = types.JsonArray[string]{"create index test1 on test456 (f1_name)"}
|
||||
collection.SetOptions(models.CollectionAuthOptions{
|
||||
ManageRule: types.Pointer("created > 0"),
|
||||
@@ -683,7 +710,11 @@ func init() {
|
||||
collection.Name = "test456_update"
|
||||
collection.Type = models.CollectionTypeBase
|
||||
collection.DeleteRule = types.Pointer(`updated > 0 && @request.auth.id != ''`)
|
||||
collection.ListRule = nil
|
||||
collection.ListRule = types.Pointer("@request.auth.id != ''")
|
||||
collection.ViewRule = types.Pointer(`id = "1"`) // no change
|
||||
collection.UpdateRule = types.Pointer(`id = "2_update"`)
|
||||
collection.CreateRule = types.Pointer(`id = "nil_update"`)
|
||||
collection.DeleteRule = nil
|
||||
collection.Indexes = types.JsonArray[string]{
|
||||
"create index test1 on test456_update (f1_name)",
|
||||
}
|
||||
|
||||
@@ -137,53 +137,61 @@ func (p *plugin) jsDiffTemplate(new *models.Collection, old *models.Collection)
|
||||
// note: strconv.Quote is used because %q converts the rule operators in unicode char codes
|
||||
// ---
|
||||
|
||||
formatRule := func(prop string, rule *string) string {
|
||||
if rule == nil {
|
||||
return fmt.Sprintf("%s.%s = null", varName, prop)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s.%s = %s", varName, prop, strconv.Quote(*rule))
|
||||
}
|
||||
|
||||
if old.ListRule != new.ListRule {
|
||||
if old.ListRule != nil && new.ListRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.listRule = null", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.listRule = %s", varName, strconv.Quote(*old.ListRule)))
|
||||
} else if old.ListRule == nil && new.ListRule != nil || *old.ListRule != *new.ListRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.listRule = %s", varName, strconv.Quote(*new.ListRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.listRule = null", varName))
|
||||
oldRule := formatRule("listRule", old.ListRule)
|
||||
newRule := formatRule("listRule", new.ListRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.ViewRule != new.ViewRule {
|
||||
if old.ViewRule != nil && new.ViewRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.viewRule = null", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.viewRule = %s", varName, strconv.Quote(*old.ViewRule)))
|
||||
} else if old.ViewRule == nil && new.ViewRule != nil || *old.ViewRule != *new.ViewRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.viewRule = %s", varName, strconv.Quote(*new.ViewRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.viewRule = null", varName))
|
||||
oldRule := formatRule("viewRule", old.ViewRule)
|
||||
newRule := formatRule("viewRule", new.ViewRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.CreateRule != new.CreateRule {
|
||||
if old.CreateRule != nil && new.CreateRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.createRule = null", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.createRule = %s", varName, strconv.Quote(*old.CreateRule)))
|
||||
} else if old.CreateRule == nil && new.CreateRule != nil || *old.CreateRule != *new.CreateRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.createRule = %s", varName, strconv.Quote(*new.CreateRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.createRule = null", varName))
|
||||
oldRule := formatRule("createRule", old.CreateRule)
|
||||
newRule := formatRule("createRule", new.CreateRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.UpdateRule != new.UpdateRule {
|
||||
if old.UpdateRule != nil && new.UpdateRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.updateRule = null", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.updateRule = %s", varName, strconv.Quote(*old.UpdateRule)))
|
||||
} else if old.UpdateRule == nil && new.UpdateRule != nil || *old.UpdateRule != *new.UpdateRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.updateRule = %s", varName, strconv.Quote(*new.UpdateRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.updateRule = null", varName))
|
||||
oldRule := formatRule("updateRule", old.UpdateRule)
|
||||
newRule := formatRule("updateRule", new.UpdateRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.DeleteRule != new.DeleteRule {
|
||||
if old.DeleteRule != nil && new.DeleteRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.deleteRule = null", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.deleteRule = %s", varName, strconv.Quote(*old.DeleteRule)))
|
||||
} else if old.DeleteRule == nil && new.DeleteRule != nil || *old.DeleteRule != *new.DeleteRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.deleteRule = %s", varName, strconv.Quote(*new.DeleteRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.deleteRule = null", varName))
|
||||
oldRule := formatRule("deleteRule", old.DeleteRule)
|
||||
newRule := formatRule("deleteRule", new.DeleteRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,53 +529,61 @@ func (p *plugin) goDiffTemplate(new *models.Collection, old *models.Collection)
|
||||
// note: strconv.Quote is used because %q converts the rule operators in unicode char codes
|
||||
// ---
|
||||
|
||||
formatRule := func(prop string, rule *string) string {
|
||||
if rule == nil {
|
||||
return fmt.Sprintf("%s.%s = nil\n", varName, prop)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s.%s = types.Pointer(%s)\n", varName, prop, strconv.Quote(*rule))
|
||||
}
|
||||
|
||||
if old.ListRule != new.ListRule {
|
||||
if old.ListRule != nil && new.ListRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.ListRule = nil\n", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.ListRule = types.Pointer(%s)\n", varName, strconv.Quote(*old.ListRule)))
|
||||
} else if old.ListRule == nil && new.ListRule != nil || *old.ListRule != *new.ListRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.ListRule = types.Pointer(%s)\n", varName, strconv.Quote(*new.ListRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.ListRule = nil\n", varName))
|
||||
oldRule := formatRule("ListRule", old.ListRule)
|
||||
newRule := formatRule("ListRule", new.ListRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.ViewRule != new.ViewRule {
|
||||
if old.ViewRule != nil && new.ViewRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.ViewRule = nil\n", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.ViewRule = types.Pointer(%s)\n", varName, strconv.Quote(*old.ViewRule)))
|
||||
} else if old.ViewRule == nil && new.ViewRule != nil || *old.ViewRule != *new.ViewRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.ViewRule = types.Pointer(%s)\n", varName, strconv.Quote(*new.ViewRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.ViewRule = nil\n", varName))
|
||||
oldRule := formatRule("ViewRule", old.ViewRule)
|
||||
newRule := formatRule("ViewRule", new.ViewRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.CreateRule != new.CreateRule {
|
||||
if old.CreateRule != nil && new.CreateRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.CreateRule = nil\n", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.CreateRule = types.Pointer(%s)\n", varName, strconv.Quote(*old.CreateRule)))
|
||||
} else if old.CreateRule == nil && new.CreateRule != nil || *old.CreateRule != *new.CreateRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.CreateRule = types.Pointer(%s)\n", varName, strconv.Quote(*new.CreateRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.CreateRule = nil\n", varName))
|
||||
oldRule := formatRule("CreateRule", old.CreateRule)
|
||||
newRule := formatRule("CreateRule", new.CreateRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.UpdateRule != new.UpdateRule {
|
||||
if old.UpdateRule != nil && new.UpdateRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.UpdateRule = nil\n", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.UpdateRule = types.Pointer(%s)\n", varName, strconv.Quote(*old.UpdateRule)))
|
||||
} else if old.UpdateRule == nil && new.UpdateRule != nil || *old.UpdateRule != *new.UpdateRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.UpdateRule = types.Pointer(%s)\n", varName, strconv.Quote(*new.UpdateRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.UpdateRule = nil\n", varName))
|
||||
oldRule := formatRule("UpdateRule", old.UpdateRule)
|
||||
newRule := formatRule("UpdateRule", new.UpdateRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
if old.DeleteRule != new.DeleteRule {
|
||||
if old.DeleteRule != nil && new.DeleteRule == nil {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.DeleteRule = nil\n", varName))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.DeleteRule = types.Pointer(%s)\n", varName, strconv.Quote(*old.DeleteRule)))
|
||||
} else if old.DeleteRule == nil && new.DeleteRule != nil || *old.DeleteRule != *new.DeleteRule {
|
||||
upParts = append(upParts, fmt.Sprintf("%s.DeleteRule = types.Pointer(%s)\n", varName, strconv.Quote(*new.DeleteRule)))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.DeleteRule = nil\n", varName))
|
||||
oldRule := formatRule("DeleteRule", old.DeleteRule)
|
||||
newRule := formatRule("DeleteRule", new.DeleteRule)
|
||||
|
||||
if oldRule != newRule {
|
||||
upParts = append(upParts, newRule)
|
||||
downParts = append(downParts, oldRule)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-11
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -37,9 +38,9 @@ type ApiScenario struct {
|
||||
|
||||
// test hooks
|
||||
// ---
|
||||
TestAppFactory func() (*TestApp, error)
|
||||
TestAppFactory func(t *testing.T) *TestApp
|
||||
BeforeTestFunc func(t *testing.T, app *TestApp, e *echo.Echo)
|
||||
AfterTestFunc func(t *testing.T, app *TestApp, e *echo.Echo)
|
||||
AfterTestFunc func(t *testing.T, app *TestApp, res *http.Response)
|
||||
}
|
||||
|
||||
// Test executes the test scenario.
|
||||
@@ -54,16 +55,17 @@ func (scenario *ApiScenario) Test(t *testing.T) {
|
||||
|
||||
func (scenario *ApiScenario) test(t *testing.T) {
|
||||
var testApp *TestApp
|
||||
var testAppErr error
|
||||
if scenario.TestAppFactory != nil {
|
||||
// @todo consider passing the testing instance to the factory and maybe remove the error from the declaration
|
||||
// (see https://github.com/pocketbase/pocketbase/discussions/3025)
|
||||
testApp, testAppErr = scenario.TestAppFactory()
|
||||
testApp = scenario.TestAppFactory(t)
|
||||
if testApp == nil {
|
||||
t.Fatal("TestAppFactory must return a non-nill app instance")
|
||||
}
|
||||
} else {
|
||||
var testAppErr error
|
||||
testApp, testAppErr = NewTestApp()
|
||||
}
|
||||
if testAppErr != nil {
|
||||
t.Fatalf("Failed to initialize the test app instance: %v", testAppErr)
|
||||
if testAppErr != nil {
|
||||
t.Fatalf("Failed to initialize the test app instance: %v", testAppErr)
|
||||
}
|
||||
}
|
||||
defer testApp.Cleanup()
|
||||
|
||||
@@ -172,8 +174,7 @@ func (scenario *ApiScenario) test(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// @todo consider adding the response body to the AfterTestFunc args
|
||||
if scenario.AfterTestFunc != nil {
|
||||
scenario.AfterTestFunc(t, testApp, e)
|
||||
scenario.AfterTestFunc(t, testApp, res)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -2,6 +2,7 @@ package archive
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"compress/flate"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
@@ -27,6 +28,11 @@ func Create(src string, dest string, skipPaths ...string) error {
|
||||
zw := zip.NewWriter(zf)
|
||||
defer zw.Close()
|
||||
|
||||
// register a custom Deflate compressor
|
||||
zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
|
||||
return flate.NewWriter(out, flate.BestSpeed)
|
||||
})
|
||||
|
||||
if err := zipAddFS(zw, os.DirFS(src), skipPaths...); err != nil {
|
||||
// try to cleanup at least the created zip file
|
||||
os.Remove(dest)
|
||||
|
||||
+24
-3
@@ -62,20 +62,41 @@ func (s *Schedule) IsDue(m *Moment) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
var macros = map[string]string{
|
||||
"@yearly": "0 0 1 1 *",
|
||||
"@annually": "0 0 1 1 *",
|
||||
"@monthly": "0 0 1 * *",
|
||||
"@weekly": "0 0 * * 0",
|
||||
"@daily": "0 0 * * *",
|
||||
"@midnight": "0 0 * * *",
|
||||
"@hourly": "0 * * * *",
|
||||
}
|
||||
|
||||
// NewSchedule creates a new Schedule from a cron expression.
|
||||
//
|
||||
// A cron expression is consisted of 5 segments separated by space,
|
||||
// A cron expression could be a macro OR 5 segments separated by space,
|
||||
// representing: minute, hour, day of the month, month and day of the week.
|
||||
//
|
||||
// Each segment could be in the following formats:
|
||||
// The following segment formats are supported:
|
||||
// - wildcard: *
|
||||
// - range: 1-30
|
||||
// - step: */n or 1-30/n
|
||||
// - list: 1,2,3,10-20/n
|
||||
//
|
||||
// The following macros are supported:
|
||||
// - @yearly (or @annually)
|
||||
// - @monthly
|
||||
// - @weekly
|
||||
// - @daily (or @midnight)
|
||||
// - @hourly
|
||||
func NewSchedule(cronExpr string) (*Schedule, error) {
|
||||
if v, ok := macros[cronExpr]; ok {
|
||||
cronExpr = v
|
||||
}
|
||||
|
||||
segments := strings.Split(cronExpr, " ")
|
||||
if len(segments) != 5 {
|
||||
return nil, errors.New("invalid cron expression - must have exactly 5 space separated segments")
|
||||
return nil, errors.New("invalid cron expression - must be a valid macro or to have exactly 5 space separated segments")
|
||||
}
|
||||
|
||||
minutes, err := parseCronSegment(segments[0], 0, 59)
|
||||
|
||||
@@ -208,6 +208,43 @@ func TestNewSchedule(t *testing.T) {
|
||||
false,
|
||||
`{"minutes":{"0":{},"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"24":{},"25":{},"26":{},"27":{},"28":{},"29":{},"3":{},"30":{},"31":{},"32":{},"33":{},"34":{},"35":{},"36":{},"37":{},"38":{},"39":{},"4":{},"40":{},"41":{},"42":{},"43":{},"44":{},"45":{},"46":{},"47":{},"48":{},"49":{},"5":{},"50":{},"51":{},"52":{},"53":{},"54":{},"55":{},"56":{},"57":{},"58":{},"59":{},"6":{},"7":{},"8":{},"9":{}},"hours":{"0":{},"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"days":{"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"24":{},"25":{},"26":{},"27":{},"28":{},"29":{},"3":{},"30":{},"31":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"months":{"1":{},"10":{},"11":{},"12":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"daysOfWeek":{"1":{},"2":{},"4":{}}}`,
|
||||
},
|
||||
|
||||
// macros
|
||||
{
|
||||
"@yearly",
|
||||
false,
|
||||
`{"minutes":{"0":{}},"hours":{"0":{}},"days":{"1":{}},"months":{"1":{}},"daysOfWeek":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{}}}`,
|
||||
},
|
||||
{
|
||||
"@annually",
|
||||
false,
|
||||
`{"minutes":{"0":{}},"hours":{"0":{}},"days":{"1":{}},"months":{"1":{}},"daysOfWeek":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{}}}`,
|
||||
},
|
||||
{
|
||||
"@monthly",
|
||||
false,
|
||||
`{"minutes":{"0":{}},"hours":{"0":{}},"days":{"1":{}},"months":{"1":{},"10":{},"11":{},"12":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"daysOfWeek":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{}}}`,
|
||||
},
|
||||
{
|
||||
"@weekly",
|
||||
false,
|
||||
`{"minutes":{"0":{}},"hours":{"0":{}},"days":{"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"24":{},"25":{},"26":{},"27":{},"28":{},"29":{},"3":{},"30":{},"31":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"months":{"1":{},"10":{},"11":{},"12":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"daysOfWeek":{"0":{}}}`,
|
||||
},
|
||||
{
|
||||
"@daily",
|
||||
false,
|
||||
`{"minutes":{"0":{}},"hours":{"0":{}},"days":{"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"24":{},"25":{},"26":{},"27":{},"28":{},"29":{},"3":{},"30":{},"31":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"months":{"1":{},"10":{},"11":{},"12":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"daysOfWeek":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{}}}`,
|
||||
},
|
||||
{
|
||||
"@midnight",
|
||||
false,
|
||||
`{"minutes":{"0":{}},"hours":{"0":{}},"days":{"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"24":{},"25":{},"26":{},"27":{},"28":{},"29":{},"3":{},"30":{},"31":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"months":{"1":{},"10":{},"11":{},"12":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"daysOfWeek":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{}}}`,
|
||||
},
|
||||
{
|
||||
"@hourly",
|
||||
false,
|
||||
`{"minutes":{"0":{}},"hours":{"0":{},"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"days":{"1":{},"10":{},"11":{},"12":{},"13":{},"14":{},"15":{},"16":{},"17":{},"18":{},"19":{},"2":{},"20":{},"21":{},"22":{},"23":{},"24":{},"25":{},"26":{},"27":{},"28":{},"29":{},"3":{},"30":{},"31":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"months":{"1":{},"10":{},"11":{},"12":{},"2":{},"3":{},"4":{},"5":{},"6":{},"7":{},"8":{},"9":{}},"daysOfWeek":{"0":{},"1":{},"2":{},"3":{},"4":{},"5":{},"6":{}}}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
@@ -74,7 +74,9 @@ func NewLocal(dirPath string) (*System, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bucket, err := fileblob.OpenBucket(dirPath, nil)
|
||||
bucket, err := fileblob.OpenBucket(dirPath, &fileblob.Options{
|
||||
NoTempDir: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+19
-6
@@ -39,12 +39,21 @@ func NewSmtpClient(
|
||||
// SmtpClient defines a SMTP mail client structure that implements
|
||||
// `mailer.Mailer` interface.
|
||||
type SmtpClient struct {
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
Tls bool
|
||||
AuthMethod string // default to "PLAIN"
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
Tls bool
|
||||
|
||||
// SMTP auth method to use
|
||||
// (if not explicitly set, defaults to "PLAIN")
|
||||
AuthMethod string
|
||||
|
||||
// LocalName is optional domain name used for the EHLO/HELO exchange
|
||||
// (if not explicitly set, defaults to "localhost").
|
||||
//
|
||||
// This is required only by some SMTP servers, such as Gmail SMTP-relay.
|
||||
LocalName string
|
||||
}
|
||||
|
||||
// Send implements `mailer.Mailer` interface.
|
||||
@@ -71,6 +80,10 @@ func (c *SmtpClient) Send(m *Message) error {
|
||||
yak = mailyak.New(fmt.Sprintf("%s:%d", c.Host, c.Port), smtpAuth)
|
||||
}
|
||||
|
||||
if c.LocalName != "" {
|
||||
yak.LocalName(c.LocalName)
|
||||
}
|
||||
|
||||
if m.From.Name != "" {
|
||||
yak.FromName(m.From.Name)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/search"
|
||||
)
|
||||
|
||||
@@ -25,8 +26,10 @@ func (s *Serializer) Serialize(c echo.Context, i any, indent string) error {
|
||||
fieldsParam = "fields"
|
||||
}
|
||||
|
||||
statusCode := c.Response().Status
|
||||
|
||||
param := c.QueryParam(fieldsParam)
|
||||
if param == "" {
|
||||
if param == "" || statusCode < 200 || statusCode > 299 {
|
||||
return s.DefaultJSONSerializer.Serialize(c, i, indent)
|
||||
}
|
||||
|
||||
@@ -92,6 +95,24 @@ func pickMapFields(data map[string]any, fields []string) {
|
||||
return // nothing to pick
|
||||
}
|
||||
|
||||
if list.ExistInSlice("*", fields) {
|
||||
// append all missing root level data keys
|
||||
for k := range data {
|
||||
var exists bool
|
||||
|
||||
for _, f := range fields {
|
||||
if strings.HasPrefix(f+".", k+".") {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !exists {
|
||||
fields = append(fields, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DataLoop:
|
||||
for k := range data {
|
||||
matchingFields := make([]string, 0, len(fields))
|
||||
|
||||
@@ -16,6 +16,7 @@ func TestSerialize(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
serializer rest.Serializer
|
||||
statusCode int
|
||||
data any
|
||||
query string
|
||||
expected string
|
||||
@@ -23,6 +24,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"empty query",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"",
|
||||
`{"a":1,"b":2,"c":"test"}`,
|
||||
@@ -30,6 +32,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"empty fields",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"fields=",
|
||||
`{"a":1,"b":2,"c":"test"}`,
|
||||
@@ -37,6 +40,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"missing fields",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"fields=missing",
|
||||
`{}`,
|
||||
@@ -44,6 +48,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"non map response",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
"test",
|
||||
"fields=a,b,test",
|
||||
`"test"`,
|
||||
@@ -51,13 +56,39 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"non slice of map response",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
[]any{"a", "b", "test"},
|
||||
"fields=a,test",
|
||||
`["a","b","test"]`,
|
||||
},
|
||||
{
|
||||
"map with no matching field",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"fields=missing", // test individual fields trim
|
||||
`{}`,
|
||||
},
|
||||
{
|
||||
">299 response",
|
||||
rest.Serializer{},
|
||||
300,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"fields=missing",
|
||||
`{"a":1,"b":2,"c":"test"}`,
|
||||
},
|
||||
{
|
||||
"<200 response",
|
||||
rest.Serializer{},
|
||||
199,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"fields=missing",
|
||||
`{"a":1,"b":2,"c":"test"}`,
|
||||
},
|
||||
{
|
||||
"map with existing and missing fields",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"fields=a, c ,missing", // test individual fields trim
|
||||
`{"a":1,"c":"test"}`,
|
||||
@@ -65,6 +96,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"custom fields param",
|
||||
rest.Serializer{FieldsParam: "custom"},
|
||||
200,
|
||||
map[string]any{"a": 1, "b": 2, "c": "test"},
|
||||
"custom=a, c ,missing", // test individual fields trim
|
||||
`{"a":1,"c":"test"}`,
|
||||
@@ -72,6 +104,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"slice of maps with existing and missing fields",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
[]any{
|
||||
map[string]any{"a": 11, "b": 11, "c": "test1"},
|
||||
map[string]any{"a": 22, "b": 22, "c": "test2"},
|
||||
@@ -82,6 +115,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"nested fields with mixed map and any slices",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
@@ -139,6 +173,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"SearchResult",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
search.Result{
|
||||
Page: 1,
|
||||
PerPage: 10,
|
||||
@@ -155,6 +190,7 @@ func TestSerialize(t *testing.T) {
|
||||
{
|
||||
"*SearchResult",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
&search.Result{
|
||||
Page: 1,
|
||||
PerPage: 10,
|
||||
@@ -168,28 +204,100 @@ func TestSerialize(t *testing.T) {
|
||||
"fields=a,c",
|
||||
`{"items":[{"a":11,"c":"test1"},{"a":22,"c":"test2"}],"page":1,"perPage":10,"totalItems":20,"totalPages":30}`,
|
||||
},
|
||||
{
|
||||
"root wildcard",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
&search.Result{
|
||||
Page: 1,
|
||||
PerPage: 10,
|
||||
TotalItems: 20,
|
||||
TotalPages: 30,
|
||||
Items: []any{
|
||||
map[string]any{"a": 11, "b": 11, "c": "test1"},
|
||||
map[string]any{"a": 22, "b": 22, "c": "test2"},
|
||||
},
|
||||
},
|
||||
"fields=*",
|
||||
`{"items":[{"a":11,"b":11,"c":"test1"},{"a":22,"b":22,"c":"test2"}],"page":1,"perPage":10,"totalItems":20,"totalPages":30}`,
|
||||
},
|
||||
{
|
||||
"root wildcard with nested exception",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{
|
||||
"id": "123",
|
||||
"title": "lorem",
|
||||
"rel": map[string]any{
|
||||
"id": "456",
|
||||
"title": "rel_title",
|
||||
},
|
||||
},
|
||||
"fields=*,rel.id",
|
||||
`{"id":"123","rel":{"id":"456"},"title":"lorem"}`,
|
||||
},
|
||||
{
|
||||
"sub wildcard",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{
|
||||
"id": "123",
|
||||
"title": "lorem",
|
||||
"rel": map[string]any{
|
||||
"id": "456",
|
||||
"title": "rel_title",
|
||||
"sub": map[string]any{
|
||||
"id": "789",
|
||||
"title": "sub_title",
|
||||
},
|
||||
},
|
||||
},
|
||||
"fields=id,rel.*",
|
||||
`{"id":"123","rel":{"id":"456","sub":{"id":"789","title":"sub_title"},"title":"rel_title"}}`,
|
||||
},
|
||||
{
|
||||
"sub wildcard with nested exception",
|
||||
rest.Serializer{},
|
||||
200,
|
||||
map[string]any{
|
||||
"id": "123",
|
||||
"title": "lorem",
|
||||
"rel": map[string]any{
|
||||
"id": "456",
|
||||
"title": "rel_title",
|
||||
"sub": map[string]any{
|
||||
"id": "789",
|
||||
"title": "sub_title",
|
||||
},
|
||||
},
|
||||
},
|
||||
"fields=id,rel.*,rel.sub.id",
|
||||
`{"id":"123","rel":{"id":"456","sub":{"id":"789"},"title":"rel_title"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
e := echo.New()
|
||||
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
req.URL.RawQuery = s.query
|
||||
rec := httptest.NewRecorder()
|
||||
c := e.NewContext(req, rec)
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
||||
req.URL.RawQuery = s.query
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
if err := s.serializer.Serialize(c, s.data, ""); err != nil {
|
||||
t.Errorf("[%s] Serialize failure: %v", s.name, err)
|
||||
continue
|
||||
}
|
||||
e := echo.New()
|
||||
c := e.NewContext(req, rec)
|
||||
c.Response().Status = s.statusCode
|
||||
|
||||
rawBody, err := io.ReadAll(rec.Result().Body)
|
||||
if err != nil {
|
||||
t.Errorf("[%s] Failed to read request body: %v", s.name, err)
|
||||
continue
|
||||
}
|
||||
if err := s.serializer.Serialize(c, s.data, ""); err != nil {
|
||||
t.Fatalf("Serialize failure: %v", err)
|
||||
}
|
||||
|
||||
if v := strings.TrimSpace(string(rawBody)); v != s.expected {
|
||||
t.Fatalf("[%s] Expected body\n%v \ngot: \n%v", s.name, s.expected, v)
|
||||
}
|
||||
rawBody, err := io.ReadAll(rec.Result().Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read request body: %v", err)
|
||||
}
|
||||
|
||||
if v := strings.TrimSpace(string(rawBody)); v != s.expected {
|
||||
t.Fatalf("Expected body\n%v \ngot: \n%v", s.expected, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+59
-19
@@ -1,25 +1,29 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ganigeorgiev/fexpr"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/pocketbase/pocketbase/tools/store"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// FilterData is a filter expression string following the `fexpr` package grammar.
|
||||
//
|
||||
// The filter string can also contain dbx placeholder parameters (eg. "title = {:name}"),
|
||||
// that will be safely replaced and properly quoted inplace with the placeholderReplacements values.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var filter FilterData = "id = null || (name = 'test' && status = true)"
|
||||
// var filter FilterData = "id = null || (name = 'test' && status = true) || (total >= {:min} && total <= {:max})"
|
||||
// resolver := search.NewSimpleFieldResolver("id", "name", "status")
|
||||
// expr, err := filter.BuildExpr(resolver)
|
||||
// expr, err := filter.BuildExpr(resolver, dbx.Params{"min": 100, "max": 200})
|
||||
type FilterData string
|
||||
|
||||
// parsedFilterData holds a cache with previously parsed filter data expressions
|
||||
@@ -27,10 +31,41 @@ type FilterData string
|
||||
var parsedFilterData = store.New(make(map[string][]fexpr.ExprGroup, 50))
|
||||
|
||||
// BuildExpr parses the current filter data and returns a new db WHERE expression.
|
||||
func (f FilterData) BuildExpr(fieldResolver FieldResolver) (dbx.Expression, error) {
|
||||
//
|
||||
// The filter string can also contain dbx placeholder parameters (eg. "title = {:name}"),
|
||||
// that will be safely replaced and properly quoted inplace with the placeholderReplacements values.
|
||||
func (f FilterData) BuildExpr(
|
||||
fieldResolver FieldResolver,
|
||||
placeholderReplacements ...dbx.Params,
|
||||
) (dbx.Expression, error) {
|
||||
raw := string(f)
|
||||
|
||||
// replace the placeholder params in the raw string filter
|
||||
for _, p := range placeholderReplacements {
|
||||
for key, value := range p {
|
||||
var replacement string
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
replacement = "null"
|
||||
case bool, float64, float32, int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8:
|
||||
replacement = cast.ToString(v)
|
||||
default:
|
||||
replacement = cast.ToString(v)
|
||||
|
||||
// try to json serialize as fallback
|
||||
if replacement == "" {
|
||||
raw, _ := json.Marshal(v)
|
||||
replacement = string(raw)
|
||||
}
|
||||
|
||||
replacement = strconv.Quote(replacement)
|
||||
}
|
||||
raw = strings.ReplaceAll(raw, "{:"+key+"}", replacement)
|
||||
}
|
||||
}
|
||||
|
||||
if parsedFilterData.Has(raw) {
|
||||
return f.build(parsedFilterData.Get(raw), fieldResolver)
|
||||
return buildParsedFilterExpr(parsedFilterData.Get(raw), fieldResolver)
|
||||
}
|
||||
data, err := fexpr.Parse(raw)
|
||||
if err != nil {
|
||||
@@ -39,10 +74,10 @@ func (f FilterData) BuildExpr(fieldResolver FieldResolver) (dbx.Expression, erro
|
||||
// store in cache
|
||||
// (the limit size is arbitrary and it is there to prevent the cache growing too big)
|
||||
parsedFilterData.SetIfLessThanLimit(raw, data, 500)
|
||||
return f.build(data, fieldResolver)
|
||||
return buildParsedFilterExpr(data, fieldResolver)
|
||||
}
|
||||
|
||||
func (f FilterData) build(data []fexpr.ExprGroup, fieldResolver FieldResolver) (dbx.Expression, error) {
|
||||
func buildParsedFilterExpr(data []fexpr.ExprGroup, fieldResolver FieldResolver) (dbx.Expression, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("empty filter expression")
|
||||
}
|
||||
@@ -55,11 +90,11 @@ func (f FilterData) build(data []fexpr.ExprGroup, fieldResolver FieldResolver) (
|
||||
|
||||
switch item := group.Item.(type) {
|
||||
case fexpr.Expr:
|
||||
expr, exprErr = f.resolveTokenizedExpr(item, fieldResolver)
|
||||
expr, exprErr = resolveTokenizedExpr(item, fieldResolver)
|
||||
case fexpr.ExprGroup:
|
||||
expr, exprErr = f.build([]fexpr.ExprGroup{item}, fieldResolver)
|
||||
expr, exprErr = buildParsedFilterExpr([]fexpr.ExprGroup{item}, fieldResolver)
|
||||
case []fexpr.ExprGroup:
|
||||
expr, exprErr = f.build(item, fieldResolver)
|
||||
expr, exprErr = buildParsedFilterExpr(item, fieldResolver)
|
||||
default:
|
||||
exprErr = errors.New("unsupported expression item")
|
||||
}
|
||||
@@ -84,7 +119,7 @@ func (f FilterData) build(data []fexpr.ExprGroup, fieldResolver FieldResolver) (
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f FilterData) resolveTokenizedExpr(expr fexpr.Expr, fieldResolver FieldResolver) (dbx.Expression, error) {
|
||||
func resolveTokenizedExpr(expr fexpr.Expr, fieldResolver FieldResolver) (dbx.Expression, error) {
|
||||
lResult, lErr := resolveToken(expr.Left, fieldResolver)
|
||||
if lErr != nil || lResult.Identifier == "" {
|
||||
return nil, fmt.Errorf("invalid left operand %q - %v", expr.Left.Literal, lErr)
|
||||
@@ -95,10 +130,10 @@ func (f FilterData) resolveTokenizedExpr(expr fexpr.Expr, fieldResolver FieldRes
|
||||
return nil, fmt.Errorf("invalid right operand %q - %v", expr.Right.Literal, rErr)
|
||||
}
|
||||
|
||||
return buildExpr(lResult, expr.Op, rResult)
|
||||
return buildResolversExpr(lResult, expr.Op, rResult)
|
||||
}
|
||||
|
||||
func buildExpr(
|
||||
func buildResolversExpr(
|
||||
left *ResolverResult,
|
||||
op fexpr.SignOp,
|
||||
right *ResolverResult,
|
||||
@@ -182,14 +217,19 @@ func buildExpr(
|
||||
func resolveToken(token fexpr.Token, fieldResolver FieldResolver) (*ResolverResult, error) {
|
||||
switch token.Type {
|
||||
case fexpr.TokenIdentifier:
|
||||
// current datetime constant
|
||||
// check for macros
|
||||
// ---
|
||||
if token.Literal == "@now" {
|
||||
if macroFunc, ok := identifierMacros[token.Literal]; ok {
|
||||
placeholder := "t" + security.PseudorandomString(5)
|
||||
|
||||
macroValue, err := macroFunc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ResolverResult{
|
||||
Identifier: "{:" + placeholder + "}",
|
||||
Params: dbx.Params{placeholder: types.NowDateTime().String()},
|
||||
Params: dbx.Params{placeholder: macroValue},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -469,7 +509,7 @@ func (e *manyVsManyExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
lAlias := "__ml" + security.PseudorandomString(5)
|
||||
rAlias := "__mr" + security.PseudorandomString(5)
|
||||
|
||||
whereExpr, buildErr := buildExpr(
|
||||
whereExpr, buildErr := buildResolversExpr(
|
||||
&ResolverResult{
|
||||
Identifier: "[[" + lAlias + ".multiMatchValue]]",
|
||||
},
|
||||
@@ -536,9 +576,9 @@ func (e *manyVsOneExpr) Build(db *dbx.DB, params dbx.Params) string {
|
||||
var buildErr error
|
||||
|
||||
if e.inverse {
|
||||
whereExpr, buildErr = buildExpr(r2, e.op, r1)
|
||||
whereExpr, buildErr = buildResolversExpr(r2, e.op, r1)
|
||||
} else {
|
||||
whereExpr, buildErr = buildExpr(r1, e.op, r2)
|
||||
whereExpr, buildErr = buildResolversExpr(r1, e.op, r2)
|
||||
}
|
||||
|
||||
if buildErr != nil {
|
||||
|
||||
+128
-86
@@ -1,15 +1,19 @@
|
||||
package search_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/tools/search"
|
||||
)
|
||||
|
||||
func TestFilterDataBuildExpr(t *testing.T) {
|
||||
resolver := search.NewSimpleFieldResolver("test1", "test2", "test3", "test4.sub")
|
||||
resolver := search.NewSimpleFieldResolver("test1", "test2", "test3", `^test4.\w+$`)
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
@@ -25,7 +29,10 @@ func TestFilterDataBuildExpr(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"invalid format",
|
||||
"(test1 > 1", true, ""},
|
||||
"(test1 > 1",
|
||||
true,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"invalid operator",
|
||||
"test1 + 123",
|
||||
@@ -42,11 +49,7 @@ func TestFilterDataBuildExpr(t *testing.T) {
|
||||
"simple expression",
|
||||
"test1 > 1",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("[[test1]] > {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("}") +
|
||||
"$",
|
||||
"[[test1]] > {:TEST}",
|
||||
},
|
||||
{
|
||||
"empty string vs null",
|
||||
@@ -58,135 +61,174 @@ func TestFilterDataBuildExpr(t *testing.T) {
|
||||
"like with 2 columns",
|
||||
"test1 ~ test2",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("[[test1]] LIKE ('%' || [[test2]] || '%') ESCAPE '\\'") +
|
||||
"$",
|
||||
"[[test1]] LIKE ('%' || [[test2]] || '%') ESCAPE '\\'",
|
||||
},
|
||||
{
|
||||
"like with right column operand",
|
||||
"'lorem' ~ test1",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("{:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} LIKE ('%' || [[test1]] || '%') ESCAPE '\\'") +
|
||||
"$",
|
||||
"{:TEST} LIKE ('%' || [[test1]] || '%') ESCAPE '\\'",
|
||||
},
|
||||
{
|
||||
"like with left column operand and text as right operand",
|
||||
"test1 ~ 'lorem'",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("[[test1]] LIKE {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} ESCAPE '\\'") +
|
||||
"$",
|
||||
"[[test1]] LIKE {:TEST} ESCAPE '\\'",
|
||||
},
|
||||
{
|
||||
"not like with 2 columns",
|
||||
"test1 !~ test2",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("[[test1]] NOT LIKE ('%' || [[test2]] || '%') ESCAPE '\\'") +
|
||||
"$",
|
||||
"[[test1]] NOT LIKE ('%' || [[test2]] || '%') ESCAPE '\\'",
|
||||
},
|
||||
{
|
||||
"not like with right column operand",
|
||||
"'lorem' !~ test1",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("{:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} NOT LIKE ('%' || [[test1]] || '%') ESCAPE '\\'") +
|
||||
"$",
|
||||
"{:TEST} NOT LIKE ('%' || [[test1]] || '%') ESCAPE '\\'",
|
||||
},
|
||||
{
|
||||
"like with left column operand and text as right operand",
|
||||
"test1 !~ 'lorem'",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("[[test1]] NOT LIKE {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} ESCAPE '\\'") +
|
||||
"$",
|
||||
"[[test1]] NOT LIKE {:TEST} ESCAPE '\\'",
|
||||
},
|
||||
{
|
||||
"current datetime constant",
|
||||
"test1 > @now",
|
||||
"macros",
|
||||
`
|
||||
test4.1 > @now &&
|
||||
test4.2 > @second &&
|
||||
test4.3 > @minute &&
|
||||
test4.4 > @hour &&
|
||||
test4.5 > @day &&
|
||||
test4.6 > @year &&
|
||||
test4.7 > @month &&
|
||||
test4.9 > @weekday &&
|
||||
test4.9 > @todayStart &&
|
||||
test4.10 > @todayEnd &&
|
||||
test4.11 > @monthStart &&
|
||||
test4.12 > @monthEnd &&
|
||||
test4.13 > @yearStart &&
|
||||
test4.14 > @yearEnd
|
||||
`,
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("[[test1]] > {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("}") +
|
||||
"$",
|
||||
"([[test4.1]] > {:TEST} AND [[test4.2]] > {:TEST} AND [[test4.3]] > {:TEST} AND [[test4.4]] > {:TEST} AND [[test4.5]] > {:TEST} AND [[test4.6]] > {:TEST} AND [[test4.7]] > {:TEST} AND [[test4.9]] > {:TEST} AND [[test4.9]] > {:TEST} AND [[test4.10]] > {:TEST} AND [[test4.11]] > {:TEST} AND [[test4.12]] > {:TEST} AND [[test4.13]] > {:TEST} AND [[test4.14]] > {:TEST})",
|
||||
},
|
||||
{
|
||||
"complex expression",
|
||||
"((test1 > 1) || (test2 != 2)) && test3 ~ '%%example' && test4.sub = null",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("(([[test1]] > {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} OR [[test2]] != {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("}) AND [[test3]] LIKE {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} ESCAPE '\\' AND ([[test4.sub]] = '' OR [[test4.sub]] IS NULL))") +
|
||||
"$",
|
||||
"(([[test1]] > {:TEST} OR [[test2]] != {:TEST}) AND [[test3]] LIKE {:TEST} ESCAPE '\\' AND ([[test4.sub]] = '' OR [[test4.sub]] IS NULL))",
|
||||
},
|
||||
{
|
||||
"combination of special literals (null, true, false)",
|
||||
"test1=true && test2 != false && null = test3 || null != test4.sub",
|
||||
false,
|
||||
"^" + regexp.QuoteMeta("([[test1]] = 1 AND [[test2]] != 0 AND ('' = [[test3]] OR [[test3]] IS NULL) OR ('' != [[test4.sub]] AND [[test4.sub]] IS NOT NULL))") + "$",
|
||||
"([[test1]] = 1 AND [[test2]] != 0 AND ('' = [[test3]] OR [[test3]] IS NULL) OR ('' != [[test4.sub]] AND [[test4.sub]] IS NOT NULL))",
|
||||
},
|
||||
{
|
||||
"all operators",
|
||||
"(test1 = test2 || test2 != test3) && (test2 ~ 'example' || test2 !~ '%%abc') && 'switch1%%' ~ test1 && 'switch2' !~ test2 && test3 > 1 && test3 >= 0 && test3 <= 4 && 2 < 5",
|
||||
false,
|
||||
"^" +
|
||||
regexp.QuoteMeta("((COALESCE([[test1]], '') = COALESCE([[test2]], '') OR COALESCE([[test2]], '') != COALESCE([[test3]], '')) AND ([[test2]] LIKE {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} ESCAPE '\\' OR [[test2]] NOT LIKE {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} ESCAPE '\\') AND {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} LIKE ('%' || [[test1]] || '%') ESCAPE '\\' AND {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} NOT LIKE ('%' || [[test2]] || '%') ESCAPE '\\' AND [[test3]] > {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} AND [[test3]] >= {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} AND [[test3]] <= {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} AND {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("} < {:") +
|
||||
".+" +
|
||||
regexp.QuoteMeta("})") +
|
||||
"$",
|
||||
"((COALESCE([[test1]], '') = COALESCE([[test2]], '') OR COALESCE([[test2]], '') != 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})",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
expr, err := s.filterData.BuildExpr(resolver)
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
expr, err := s.filterData.BuildExpr(resolver)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
t.Errorf("[%s] Expected hasErr %v, got %v (%v)", s.name, s.expectError, hasErr, err)
|
||||
continue
|
||||
}
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
t.Fatalf("[%s] Expected hasErr %v, got %v (%v)", s.name, s.expectError, hasErr, err)
|
||||
}
|
||||
|
||||
if hasErr {
|
||||
continue
|
||||
}
|
||||
if hasErr {
|
||||
return
|
||||
}
|
||||
|
||||
dummyDB := &dbx.DB{}
|
||||
rawSql := expr.Build(dummyDB, map[string]any{})
|
||||
dummyDB := &dbx.DB{}
|
||||
|
||||
pattern := regexp.MustCompile(s.expectPattern)
|
||||
if !pattern.MatchString(rawSql) {
|
||||
t.Errorf("[%s] Pattern %v don't match with expression: \n%v", s.name, s.expectPattern, rawSql)
|
||||
}
|
||||
rawSql := expr.Build(dummyDB, dbx.Params{})
|
||||
|
||||
// replace TEST placeholder with .+ regex pattern
|
||||
expectPattern := strings.ReplaceAll(
|
||||
"^"+regexp.QuoteMeta(s.expectPattern)+"$",
|
||||
"TEST",
|
||||
`\w+`,
|
||||
)
|
||||
|
||||
pattern := regexp.MustCompile(expectPattern)
|
||||
if !pattern.MatchString(rawSql) {
|
||||
t.Fatalf("[%s] Pattern %v don't match with expression: \n%v", s.name, expectPattern, rawSql)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterDataBuildExprWithParams(t *testing.T) {
|
||||
// create a dummy db
|
||||
sqlDB, err := sql.Open("sqlite", "file::memory:?cache=shared")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
db := dbx.NewFromDB(sqlDB, "sqlite")
|
||||
|
||||
calledQueries := []string{}
|
||||
db.QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {
|
||||
calledQueries = append(calledQueries, sql)
|
||||
}
|
||||
db.ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {
|
||||
calledQueries = append(calledQueries, sql)
|
||||
}
|
||||
|
||||
date, err := time.Parse("2006-01-02", "2023-01-01")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
resolver := search.NewSimpleFieldResolver(`^test\w+$`)
|
||||
|
||||
filter := search.FilterData(`
|
||||
test1 = {:test1} ||
|
||||
test2 = {:test2} ||
|
||||
test3a = {:test3} ||
|
||||
test3b = {:test3} ||
|
||||
test4 = {:test4} ||
|
||||
test5 = {:test5} ||
|
||||
test6 = {:test6} ||
|
||||
test7 = {:test7} ||
|
||||
test8 = {:test8} ||
|
||||
test9 = {:test9} ||
|
||||
test10 = {:test10} ||
|
||||
test11 = {:test11} ||
|
||||
test12 = {:test12}
|
||||
`)
|
||||
|
||||
replacements := []dbx.Params{
|
||||
{"test1": true},
|
||||
{"test2": false},
|
||||
{"test3": 123.456},
|
||||
{"test4": nil},
|
||||
{"test5": "", "test6": "simple", "test7": `'single_quotes'`, "test8": `"double_quotes"`, "test9": `escape\"quote`},
|
||||
{"test10": date},
|
||||
{"test11": []string{"a", "b", `"quote`}},
|
||||
{"test12": map[string]any{"a": 123, "b": `quote"`}},
|
||||
}
|
||||
|
||||
expr, err := filter.BuildExpr(resolver, replacements...)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db.Select().Where(expr).Build().Execute()
|
||||
|
||||
if len(calledQueries) != 1 {
|
||||
t.Fatalf("Expected 1 query, got %d", len(calledQueries))
|
||||
}
|
||||
|
||||
expectedQuery := `SELECT * WHERE ([[test1]] = 1 OR [[test2]] = 0 OR [[test3a]] = 123.456 OR [[test3b]] = 123.456 OR ([[test4]] = '' OR [[test4]] IS NULL) OR [[test5]] = '""' OR [[test6]] = 'simple' OR [[test7]] = '''single_quotes''' OR [[test8]] = '"double_quotes"' OR [[test9]] = 'escape\\"quote' OR [[test10]] = '2023-01-01 00:00:00 +0000 UTC' OR [[test11]] = '["a","b","\\"quote"]' OR [[test12]] = '{"a":123,"b":"quote\\""}')`
|
||||
if expectedQuery != calledQueries[0] {
|
||||
t.Fatalf("Expected query \n%s, \ngot \n%s", expectedQuery, calledQueries[0])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
// note: used primarily for the tests
|
||||
var timeNow = func() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
var identifierMacros = map[string]func() (any, error){
|
||||
"@now": func() (any, error) {
|
||||
today := timeNow().UTC()
|
||||
|
||||
d, err := types.ParseDateTime(today)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("@now: %w", err)
|
||||
}
|
||||
|
||||
return d.String(), nil
|
||||
},
|
||||
"@second": func() (any, error) {
|
||||
return int(timeNow().UTC().Second()), nil
|
||||
},
|
||||
"@minute": func() (any, error) {
|
||||
return int(timeNow().UTC().Minute()), nil
|
||||
},
|
||||
"@hour": func() (any, error) {
|
||||
return int(timeNow().UTC().Hour()), nil
|
||||
},
|
||||
"@day": func() (any, error) {
|
||||
return int(timeNow().UTC().Day()), nil
|
||||
},
|
||||
"@month": func() (any, error) {
|
||||
return int(timeNow().UTC().Month()), nil
|
||||
},
|
||||
"@weekday": func() (any, error) {
|
||||
return int(timeNow().UTC().Weekday()), nil
|
||||
},
|
||||
"@year": func() (any, error) {
|
||||
return int(timeNow().UTC().Year()), nil
|
||||
},
|
||||
"@todayStart": func() (any, error) {
|
||||
today := timeNow().UTC()
|
||||
start := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, time.UTC)
|
||||
|
||||
d, err := types.ParseDateTime(start)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("@todayStart: %w", err)
|
||||
}
|
||||
|
||||
return d.String(), nil
|
||||
},
|
||||
"@todayEnd": func() (any, error) {
|
||||
today := timeNow().UTC()
|
||||
|
||||
start := time.Date(today.Year(), today.Month(), today.Day(), 23, 59, 59, 999999999, time.UTC)
|
||||
|
||||
d, err := types.ParseDateTime(start)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("@todayEnd: %w", err)
|
||||
}
|
||||
|
||||
return d.String(), nil
|
||||
},
|
||||
"@monthStart": func() (any, error) {
|
||||
today := timeNow().UTC()
|
||||
start := time.Date(today.Year(), today.Month(), 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
d, err := types.ParseDateTime(start)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("@monthStart: %w", err)
|
||||
}
|
||||
|
||||
return d.String(), nil
|
||||
},
|
||||
"@monthEnd": func() (any, error) {
|
||||
today := timeNow().UTC()
|
||||
start := time.Date(today.Year(), today.Month(), 1, 23, 59, 59, 999999999, time.UTC)
|
||||
end := start.AddDate(0, 1, -1)
|
||||
|
||||
d, err := types.ParseDateTime(end)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("@monthEnd: %w", err)
|
||||
}
|
||||
|
||||
return d.String(), nil
|
||||
},
|
||||
"@yearStart": func() (any, error) {
|
||||
today := timeNow().UTC()
|
||||
start := time.Date(today.Year(), 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
d, err := types.ParseDateTime(start)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("@yearStart: %w", err)
|
||||
}
|
||||
|
||||
return d.String(), nil
|
||||
},
|
||||
"@yearEnd": func() (any, error) {
|
||||
today := timeNow().UTC()
|
||||
end := time.Date(today.Year(), 12, 31, 23, 59, 59, 999999999, time.UTC)
|
||||
|
||||
d, err := types.ParseDateTime(end)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("@yearEnd: %w", err)
|
||||
}
|
||||
|
||||
return d.String(), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestIdentifierMacros(t *testing.T) {
|
||||
originalTimeNow := timeNow
|
||||
|
||||
timeNow = func() time.Time {
|
||||
return time.Date(2023, 2, 3, 4, 5, 6, 7, time.UTC)
|
||||
}
|
||||
|
||||
testMacros := map[string]any{
|
||||
"@now": "2023-02-03 04:05:06.000Z",
|
||||
"@second": 6,
|
||||
"@minute": 5,
|
||||
"@hour": 4,
|
||||
"@day": 3,
|
||||
"@month": 2,
|
||||
"@weekday": 5,
|
||||
"@year": 2023,
|
||||
"@todayStart": "2023-02-03 00:00:00.000Z",
|
||||
"@todayEnd": "2023-02-03 23:59:59.999Z",
|
||||
"@monthStart": "2023-02-01 00:00:00.000Z",
|
||||
"@monthEnd": "2023-02-28 23:59:59.999Z",
|
||||
"@yearStart": "2023-01-01 00:00:00.000Z",
|
||||
"@yearEnd": "2023-12-31 23:59:59.999Z",
|
||||
}
|
||||
|
||||
if len(testMacros) != len(identifierMacros) {
|
||||
t.Fatalf("Expected %d macros, got %d", len(testMacros), len(identifierMacros))
|
||||
}
|
||||
|
||||
for key, expected := range testMacros {
|
||||
t.Run(key, func(t *testing.T) {
|
||||
macro, ok := identifierMacros[key]
|
||||
if !ok {
|
||||
t.Fatalf("Missing macro %s", key)
|
||||
}
|
||||
|
||||
result, err := macro()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if result != expected {
|
||||
t.Fatalf("Expected %q, got %q", expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// restore
|
||||
timeNow = originalTimeNow
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// S256Challenge creates base64 encoded sha256 challenge string derived from code.
|
||||
// The padding of the result base64 string is stripped per [RFC 7636].
|
||||
//
|
||||
// [RFC 7636]: https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
|
||||
func S256Challenge(code string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(code))
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(h.Sum(nil)), "=")
|
||||
}
|
||||
|
||||
// MD5 creates md5 hash from the provided plain text.
|
||||
func MD5(text string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(text))
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// SHA256 creates sha256 hash as defined in FIPS 180-4 from the provided text.
|
||||
func SHA256(text string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(text))
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// SHA512 creates sha512 hash as defined in FIPS 180-4 from the provided text.
|
||||
func SHA512(text string) string {
|
||||
h := sha512.New()
|
||||
h.Write([]byte(text))
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// HS256 creates a HMAC hash with sha256 digest algorithm.
|
||||
func HS256(text string, secret string) string {
|
||||
h := hmac.New(sha256.New, []byte(secret))
|
||||
h.Write([]byte(text))
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// HS512 creates a HMAC hash with sha512 digest algorithm.
|
||||
func HS512(text string, secret string) string {
|
||||
h := hmac.New(sha512.New, []byte(secret))
|
||||
h.Write([]byte(text))
|
||||
return fmt.Sprintf("%x", h.Sum(nil))
|
||||
}
|
||||
|
||||
// Equal compares two hash strings for equality without leaking timing information.
|
||||
func Equal(hash1 string, hash2 string) bool {
|
||||
return subtle.ConstantTimeCompare([]byte(hash1), []byte(hash2)) == 1
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package security_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
func TestS256Challenge(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
code string
|
||||
expected string
|
||||
}{
|
||||
{"", "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU"},
|
||||
{"123", "pmWkWSBCL51Bfkhn79xPuKBKHz__H6B-mY6G9_eieuM"},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.code, func(t *testing.T) {
|
||||
result := security.S256Challenge(s.code)
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("Expected %q, got %q", s.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMD5(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
code string
|
||||
expected string
|
||||
}{
|
||||
{"", "d41d8cd98f00b204e9800998ecf8427e"},
|
||||
{"123", "202cb962ac59075b964b07152d234b70"},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.code, func(t *testing.T) {
|
||||
result := security.MD5(s.code)
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("Expected %v, got %v", s.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHA256(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
code string
|
||||
expected string
|
||||
}{
|
||||
{"", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
|
||||
{"123", "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.code, func(t *testing.T) {
|
||||
result := security.SHA256(s.code)
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("Expected %v, got %v", s.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHA512(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
code string
|
||||
expected string
|
||||
}{
|
||||
{"", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"},
|
||||
{"123", "3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2"},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.code, func(t *testing.T) {
|
||||
result := security.SHA512(s.code)
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("Expected %v, got %v", s.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHS256(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
text string
|
||||
secret string
|
||||
expected string
|
||||
}{
|
||||
{" ", "test", "9fb4e4a12d50728683a222b4fc466a69ee977332cfcdd6b9ebb44c7121dbd99f"},
|
||||
{" ", "test2", "d792417a504716e22805d940125ec12e68e8cb18fc84674703bd96c59f1e1228"},
|
||||
{"hello", "test", "f151ea24bda91a18e89b8bb5793ef324b2a02133cce15a28a719acbd2e58a986"},
|
||||
{"hello", "test2", "16436e8dcbf3d7b5b0455573b27e6372699beb5bfe94e6a2a371b14b4ae068f4"},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("%d-%s", i, s.text), func(t *testing.T) {
|
||||
result := security.HS256(s.text, s.secret)
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("Expected \n%v, \ngot \n%v", s.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHS512(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
text string
|
||||
secret string
|
||||
expected string
|
||||
}{
|
||||
{" ", "test", "eb3bdb0352c95c38880c1f645fc7e1d1332644f938f50de0d73876e42d6f302e599bb526531ba79940e8b314369aaef3675322d8d851f9fc6ea9ed121286d196"},
|
||||
{" ", "test2", "8b69e84e9252af78ae8b1c4bed3c9f737f69a3df33064cfbefe76b36d19d1827285e543cdf066cdc8bd556cc0cd0e212d52e9c12a50cd16046181ff127f4cf7f"},
|
||||
{"hello", "test", "44f280e11103e295c26cd61dd1cdd8178b531b860466867c13b1c37a26b6389f8af110efbe0bb0717b9d9c87f6fe1c97b3b1690936578890e5669abf279fe7fd"},
|
||||
{"hello", "test2", "d7f10b1b66941b20817689b973ca9dfc971090e28cfb8becbddd6824569b323eca6a0cdf2c387aa41e15040007dca5a011dd4e4bb61cfd5011aa7354d866f6ef"},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("%d-%q", i, s.text), func(t *testing.T) {
|
||||
result := security.HS512(s.text, s.secret)
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("Expected \n%v, \ngot \n%v", s.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
hash1 string
|
||||
hash2 string
|
||||
expected bool
|
||||
}{
|
||||
{"", "", true},
|
||||
{"abc", "abd", false},
|
||||
{"abc", "abc", true},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("%qVS%q", s.hash1, s.hash2), func(t *testing.T) {
|
||||
result := security.Equal(s.hash1, s.hash2)
|
||||
|
||||
if result != s.expected {
|
||||
t.Fatalf("Expected %v, got %v", s.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,10 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
crand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// S256Challenge creates base64 encoded sha256 challenge string derived from code.
|
||||
// The padding of the result base64 string is stripped per [RFC 7636].
|
||||
//
|
||||
// [RFC 7636]: https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
|
||||
func S256Challenge(code string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(code))
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(h.Sum(nil)), "=")
|
||||
}
|
||||
|
||||
// Encrypt encrypts data with key (must be valid 32 char aes key).
|
||||
func Encrypt(data []byte, key string) (string, error) {
|
||||
block, err := aes.NewCipher([]byte(key))
|
||||
|
||||
@@ -6,24 +6,6 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
func TestS256Challenge(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
code string
|
||||
expected string
|
||||
}{
|
||||
{"", "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU"},
|
||||
{"123", "pmWkWSBCL51Bfkhn79xPuKBKHz__H6B-mY6G9_eieuM"},
|
||||
}
|
||||
|
||||
for i, scenario := range scenarios {
|
||||
result := security.S256Challenge(scenario.code)
|
||||
|
||||
if result != scenario.expected {
|
||||
t.Errorf("(%d) Expected %q, got %q", i, scenario.expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
data string
|
||||
|
||||
@@ -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.17.2"
|
||||
PB_VERSION = "v0.18.4"
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
import{S as Se,i as ye,s as Te,O as G,e as c,w,b as k,c as se,f as p,g as d,h as a,m as ae,x as U,P as ve,Q as je,k as Ae,R as Be,n as Oe,t as W,a as V,o as u,d as ne,C as Fe,p as Qe,r as L,u as Ne,N as He}from"./index-44098792.js";import{S as Ke}from"./SdkTabs-f73340a3.js";import{F as qe}from"./FieldsQueryParam-eb4f33e4.js";function Ce(n,l,o){const s=n.slice();return s[5]=l[o],s}function Pe(n,l,o){const s=n.slice();return s[5]=l[o],s}function $e(n,l){let o,s=l[5].code+"",_,f,i,h;function m(){return l[4](l[5])}return{key:n,first:null,c(){o=c("button"),_=w(s),f=k(),p(o,"class","tab-item"),L(o,"active",l[1]===l[5].code),this.first=o},m(v,C){d(v,o,C),a(o,_),a(o,f),i||(h=Ne(o,"click",m),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&U(_,s),C&6&&L(o,"active",l[1]===l[5].code)},d(v){v&&u(o),i=!1,h()}}}function Me(n,l){let o,s,_,f;return s=new He({props:{content:l[5].body}}),{key:n,first:null,c(){o=c("div"),se(s.$$.fragment),_=k(),p(o,"class","tab-item"),L(o,"active",l[1]===l[5].code),this.first=o},m(i,h){d(i,o,h),ae(s,o,null),a(o,_),f=!0},p(i,h){l=i;const m={};h&4&&(m.content=l[5].body),s.$set(m),(!f||h&6)&&L(o,"active",l[1]===l[5].code)},i(i){f||(W(s.$$.fragment,i),f=!0)},o(i){V(s.$$.fragment,i),f=!1},d(i){i&&u(o),ne(s)}}}function ze(n){var be,ke;let l,o,s=n[0].name+"",_,f,i,h,m,v,C,H=n[0].name+"",E,ie,I,P,J,j,Y,$,K,ce,q,A,re,R,z=n[0].name+"",X,de,Z,B,x,M,ee,ue,te,T,le,O,oe,S,F,g=[],he=new Map,me,Q,b=[],fe=new Map,y;P=new Ke({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(be=n[0])==null?void 0:be.name}').listAuthMethods();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(ke=n[0])==null?void 0:ke.name}').listAuthMethods();
|
||||
`}}),T=new qe({});let D=G(n[2]);const pe=e=>e[5].code;for(let e=0;e<D.length;e+=1){let t=Pe(n,D,e),r=pe(t);he.set(r,g[e]=$e(r,t))}let N=G(n[2]);const _e=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=Ce(n,N,e),r=_e(t);fe.set(r,b[e]=Me(r,t))}return{c(){l=c("h3"),o=w("List auth methods ("),_=w(s),f=w(")"),i=k(),h=c("div"),m=c("p"),v=w("Returns a public list with all allowed "),C=c("strong"),E=w(H),ie=w(" authentication methods."),I=k(),se(P.$$.fragment),J=k(),j=c("h6"),j.textContent="API details",Y=k(),$=c("div"),K=c("strong"),K.textContent="GET",ce=k(),q=c("div"),A=c("p"),re=w("/api/collections/"),R=c("strong"),X=w(z),de=w("/auth-methods"),Z=k(),B=c("div"),B.textContent="Query parameters",x=k(),M=c("table"),ee=c("thead"),ee.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',ue=k(),te=c("tbody"),se(T.$$.fragment),le=k(),O=c("div"),O.textContent="Responses",oe=k(),S=c("div"),F=c("div");for(let e=0;e<g.length;e+=1)g[e].c();me=k(),Q=c("div");for(let e=0;e<b.length;e+=1)b[e].c();p(l,"class","m-b-sm"),p(h,"class","content txt-lg m-b-sm"),p(j,"class","m-b-xs"),p(K,"class","label label-primary"),p(q,"class","content"),p($,"class","alert alert-info"),p(B,"class","section-title"),p(M,"class","table-compact table-border m-b-base"),p(O,"class","section-title"),p(F,"class","tabs-header compact combined left"),p(Q,"class","tabs-content"),p(S,"class","tabs")},m(e,t){d(e,l,t),a(l,o),a(l,_),a(l,f),d(e,i,t),d(e,h,t),a(h,m),a(m,v),a(m,C),a(C,E),a(m,ie),d(e,I,t),ae(P,e,t),d(e,J,t),d(e,j,t),d(e,Y,t),d(e,$,t),a($,K),a($,ce),a($,q),a(q,A),a(A,re),a(A,R),a(R,X),a(A,de),d(e,Z,t),d(e,B,t),d(e,x,t),d(e,M,t),a(M,ee),a(M,ue),a(M,te),ae(T,te,null),d(e,le,t),d(e,O,t),d(e,oe,t),d(e,S,t),a(S,F);for(let r=0;r<g.length;r+=1)g[r]&&g[r].m(F,null);a(S,me),a(S,Q);for(let r=0;r<b.length;r+=1)b[r]&&b[r].m(Q,null);y=!0},p(e,[t]){var ge,we;(!y||t&1)&&s!==(s=e[0].name+"")&&U(_,s),(!y||t&1)&&H!==(H=e[0].name+"")&&U(E,H);const r={};t&9&&(r.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(ge=e[0])==null?void 0:ge.name}').listAuthMethods();
|
||||
`),t&9&&(r.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(we=e[0])==null?void 0:we.name}').listAuthMethods();
|
||||
`),P.$set(r),(!y||t&1)&&z!==(z=e[0].name+"")&&U(X,z),t&6&&(D=G(e[2]),g=ve(g,t,pe,1,e,D,he,F,je,$e,null,Pe)),t&6&&(N=G(e[2]),Ae(),b=ve(b,t,_e,1,e,N,fe,Q,Be,Me,null,Ce),Oe())},i(e){if(!y){W(P.$$.fragment,e),W(T.$$.fragment,e);for(let t=0;t<N.length;t+=1)W(b[t]);y=!0}},o(e){V(P.$$.fragment,e),V(T.$$.fragment,e);for(let t=0;t<b.length;t+=1)V(b[t]);y=!1},d(e){e&&(u(l),u(i),u(h),u(I),u(J),u(j),u(Y),u($),u(Z),u(B),u(x),u(M),u(le),u(O),u(oe),u(S)),ne(P,e),ne(T);for(let t=0;t<g.length;t+=1)g[t].d();for(let t=0;t<b.length;t+=1)b[t].d()}}}function De(n,l,o){let s,{collection:_}=l,f=200,i=[];const h=m=>o(1,f=m.code);return n.$$set=m=>{"collection"in m&&o(0,_=m.collection)},o(3,s=Fe.getApiExampleUrl(Qe.baseUrl)),o(2,i=[{code:200,body:`
|
||||
{
|
||||
"usernamePassword": true,
|
||||
"emailPassword": true,
|
||||
"authProviders": [
|
||||
{
|
||||
"name": "github",
|
||||
"state": "3Yd8jNkK_6PJG6hPWwBjLqKwse6Ejd",
|
||||
"codeVerifier": "KxFDWz1B3fxscCDJ_9gHQhLuh__ie7",
|
||||
"codeChallenge": "NM1oVexB6Q6QH8uPtOUfK7tq4pmu4Jz6lNDIwoxHZNE=",
|
||||
"codeChallengeMethod": "S256",
|
||||
"authUrl": "https://github.com/login/oauth/authorize?client_id=demo&code_challenge=NM1oVexB6Q6QH8uPtOUfK7tq4pmu4Jz6lNDIwoxHZNE%3D&code_challenge_method=S256&response_type=code&scope=user&state=3Yd8jNkK_6PJG6hPWwBjLqKwse6Ejd&redirect_uri="
|
||||
},
|
||||
{
|
||||
"name": "gitlab",
|
||||
"state": "NeQSbtO5cShr_mk5__3CUukiMnymeb",
|
||||
"codeVerifier": "ahTFHOgua8mkvPAlIBGwCUJbWKR_xi",
|
||||
"codeChallenge": "O-GATkTj4eXDCnfonsqGLCd6njvTixlpCMvy5kjgOOg=",
|
||||
"codeChallengeMethod": "S256",
|
||||
"authUrl": "https://gitlab.com/oauth/authorize?client_id=demo&code_challenge=O-GATkTj4eXDCnfonsqGLCd6njvTixlpCMvy5kjgOOg%3D&code_challenge_method=S256&response_type=code&scope=read_user&state=NeQSbtO5cShr_mk5__3CUukiMnymeb&redirect_uri="
|
||||
},
|
||||
{
|
||||
"name": "google",
|
||||
"state": "zB3ZPifV1TW2GMuvuFkamSXfSNkHPQ",
|
||||
"codeVerifier": "t3CmO5VObGzdXqieakvR_fpjiW0zdO",
|
||||
"codeChallenge": "KChwoQPKYlz2anAdqtgsSTdIo8hdwtc1fh2wHMwW2Yk=",
|
||||
"codeChallengeMethod": "S256",
|
||||
"authUrl": "https://accounts.google.com/o/oauth2/auth?client_id=demo&code_challenge=KChwoQPKYlz2anAdqtgsSTdIo8hdwtc1fh2wHMwW2Yk%3D&code_challenge_method=S256&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=zB3ZPifV1TW2GMuvuFkamSXfSNkHPQ&redirect_uri="
|
||||
}
|
||||
]
|
||||
}
|
||||
`}]),[_,f,i,s,h]}class Ve extends Se{constructor(l){super(),ye(this,l,De,ze,Te,{collection:0})}}export{Ve as default};
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
import{S as $e,i as Se,s as ye,e as c,w,b as k,c as oe,f as h,g as d,h as a,m as se,x as G,N as we,P as Te,k as je,Q as Ae,n as Be,t as U,a as W,o as u,d as ae,T as Fe,C as Oe,p as Qe,r as V,u as Ne,M as He}from"./index-9e84ac8a.js";import{S as Ke}from"./SdkTabs-821a18db.js";import{F as qe}from"./FieldsQueryParam-664da4c8.js";function ve(n,l,o){const s=n.slice();return s[5]=l[o],s}function Ce(n,l,o){const s=n.slice();return s[5]=l[o],s}function Pe(n,l){let o,s=l[5].code+"",_,p,i,f;function m(){return l[4](l[5])}return{key:n,first:null,c(){o=c("button"),_=w(s),p=k(),h(o,"class","tab-item"),V(o,"active",l[1]===l[5].code),this.first=o},m(v,C){d(v,o,C),a(o,_),a(o,p),i||(f=Ne(o,"click",m),i=!0)},p(v,C){l=v,C&4&&s!==(s=l[5].code+"")&&G(_,s),C&6&&V(o,"active",l[1]===l[5].code)},d(v){v&&u(o),i=!1,f()}}}function Me(n,l){let o,s,_,p;return s=new He({props:{content:l[5].body}}),{key:n,first:null,c(){o=c("div"),oe(s.$$.fragment),_=k(),h(o,"class","tab-item"),V(o,"active",l[1]===l[5].code),this.first=o},m(i,f){d(i,o,f),se(s,o,null),a(o,_),p=!0},p(i,f){l=i;const m={};f&4&&(m.content=l[5].body),s.$set(m),(!p||f&6)&&V(o,"active",l[1]===l[5].code)},i(i){p||(U(s.$$.fragment,i),p=!0)},o(i){W(s.$$.fragment,i),p=!1},d(i){i&&u(o),ae(s)}}}function ze(n){var _e,be;let l,o,s=n[0].name+"",_,p,i,f,m,v,C,H=n[0].name+"",L,ne,E,P,I,j,J,M,K,ie,q,A,ce,Y,z=n[0].name+"",X,re,R,B,Z,$,x,de,ee,T,te,F,le,S,O,g=[],ue=new Map,fe,Q,b=[],me=new Map,y;P=new Ke({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(_e=n[0])==null?void 0:_e.name}').listAuthMethods();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(be=n[0])==null?void 0:be.name}').listAuthMethods();
|
||||
`}}),T=new qe({});let D=n[2];const pe=e=>e[5].code;for(let e=0;e<D.length;e+=1){let t=Ce(n,D,e),r=pe(t);ue.set(r,g[e]=Pe(r,t))}let N=n[2];const he=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=ve(n,N,e),r=he(t);me.set(r,b[e]=Me(r,t))}return{c(){l=c("h3"),o=w("List auth methods ("),_=w(s),p=w(")"),i=k(),f=c("div"),m=c("p"),v=w("Returns a public list with all allowed "),C=c("strong"),L=w(H),ne=w(" authentication methods."),E=k(),oe(P.$$.fragment),I=k(),j=c("h6"),j.textContent="API details",J=k(),M=c("div"),K=c("strong"),K.textContent="GET",ie=k(),q=c("div"),A=c("p"),ce=w("/api/collections/"),Y=c("strong"),X=w(z),re=w("/auth-methods"),R=k(),B=c("div"),B.textContent="Query parameters",Z=k(),$=c("table"),x=c("thead"),x.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr>`,de=k(),ee=c("tbody"),oe(T.$$.fragment),te=k(),F=c("div"),F.textContent="Responses",le=k(),S=c("div"),O=c("div");for(let e=0;e<g.length;e+=1)g[e].c();fe=k(),Q=c("div");for(let e=0;e<b.length;e+=1)b[e].c();h(l,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(j,"class","m-b-xs"),h(K,"class","label label-primary"),h(q,"class","content"),h(M,"class","alert alert-info"),h(B,"class","section-title"),h($,"class","table-compact table-border m-b-base"),h(F,"class","section-title"),h(O,"class","tabs-header compact left"),h(Q,"class","tabs-content"),h(S,"class","tabs")},m(e,t){d(e,l,t),a(l,o),a(l,_),a(l,p),d(e,i,t),d(e,f,t),a(f,m),a(m,v),a(m,C),a(C,L),a(m,ne),d(e,E,t),se(P,e,t),d(e,I,t),d(e,j,t),d(e,J,t),d(e,M,t),a(M,K),a(M,ie),a(M,q),a(q,A),a(A,ce),a(A,Y),a(Y,X),a(A,re),d(e,R,t),d(e,B,t),d(e,Z,t),d(e,$,t),a($,x),a($,de),a($,ee),se(T,ee,null),d(e,te,t),d(e,F,t),d(e,le,t),d(e,S,t),a(S,O);for(let r=0;r<g.length;r+=1)g[r]&&g[r].m(O,null);a(S,fe),a(S,Q);for(let r=0;r<b.length;r+=1)b[r]&&b[r].m(Q,null);y=!0},p(e,[t]){var ke,ge;(!y||t&1)&&s!==(s=e[0].name+"")&&G(_,s),(!y||t&1)&&H!==(H=e[0].name+"")&&G(L,H);const r={};t&9&&(r.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(ke=e[0])==null?void 0:ke.name}').listAuthMethods();
|
||||
`),t&9&&(r.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(ge=e[0])==null?void 0:ge.name}').listAuthMethods();
|
||||
`),P.$set(r),(!y||t&1)&&z!==(z=e[0].name+"")&&G(X,z),t&6&&(D=e[2],g=we(g,t,pe,1,e,D,ue,O,Te,Pe,null,Ce)),t&6&&(N=e[2],je(),b=we(b,t,he,1,e,N,me,Q,Ae,Me,null,ve),Be())},i(e){if(!y){U(P.$$.fragment,e),U(T.$$.fragment,e);for(let t=0;t<N.length;t+=1)U(b[t]);y=!0}},o(e){W(P.$$.fragment,e),W(T.$$.fragment,e);for(let t=0;t<b.length;t+=1)W(b[t]);y=!1},d(e){e&&u(l),e&&u(i),e&&u(f),e&&u(E),ae(P,e),e&&u(I),e&&u(j),e&&u(J),e&&u(M),e&&u(R),e&&u(B),e&&u(Z),e&&u($),ae(T),e&&u(te),e&&u(F),e&&u(le),e&&u(S);for(let t=0;t<g.length;t+=1)g[t].d();for(let t=0;t<b.length;t+=1)b[t].d()}}}function De(n,l,o){let s,{collection:_=new Fe}=l,p=200,i=[];const f=m=>o(1,p=m.code);return n.$$set=m=>{"collection"in m&&o(0,_=m.collection)},o(3,s=Oe.getApiExampleUrl(Qe.baseUrl)),o(2,i=[{code:200,body:`
|
||||
{
|
||||
"usernamePassword": true,
|
||||
"emailPassword": true,
|
||||
"authProviders": [
|
||||
{
|
||||
"name": "github",
|
||||
"state": "3Yd8jNkK_6PJG6hPWwBjLqKwse6Ejd",
|
||||
"codeVerifier": "KxFDWz1B3fxscCDJ_9gHQhLuh__ie7",
|
||||
"codeChallenge": "NM1oVexB6Q6QH8uPtOUfK7tq4pmu4Jz6lNDIwoxHZNE=",
|
||||
"codeChallengeMethod": "S256",
|
||||
"authUrl": "https://github.com/login/oauth/authorize?client_id=demo&code_challenge=NM1oVexB6Q6QH8uPtOUfK7tq4pmu4Jz6lNDIwoxHZNE%3D&code_challenge_method=S256&response_type=code&scope=user&state=3Yd8jNkK_6PJG6hPWwBjLqKwse6Ejd&redirect_uri="
|
||||
},
|
||||
{
|
||||
"name": "gitlab",
|
||||
"state": "NeQSbtO5cShr_mk5__3CUukiMnymeb",
|
||||
"codeVerifier": "ahTFHOgua8mkvPAlIBGwCUJbWKR_xi",
|
||||
"codeChallenge": "O-GATkTj4eXDCnfonsqGLCd6njvTixlpCMvy5kjgOOg=",
|
||||
"codeChallengeMethod": "S256",
|
||||
"authUrl": "https://gitlab.com/oauth/authorize?client_id=demo&code_challenge=O-GATkTj4eXDCnfonsqGLCd6njvTixlpCMvy5kjgOOg%3D&code_challenge_method=S256&response_type=code&scope=read_user&state=NeQSbtO5cShr_mk5__3CUukiMnymeb&redirect_uri="
|
||||
},
|
||||
{
|
||||
"name": "google",
|
||||
"state": "zB3ZPifV1TW2GMuvuFkamSXfSNkHPQ",
|
||||
"codeVerifier": "t3CmO5VObGzdXqieakvR_fpjiW0zdO",
|
||||
"codeChallenge": "KChwoQPKYlz2anAdqtgsSTdIo8hdwtc1fh2wHMwW2Yk=",
|
||||
"codeChallengeMethod": "S256",
|
||||
"authUrl": "https://accounts.google.com/o/oauth2/auth?client_id=demo&code_challenge=KChwoQPKYlz2anAdqtgsSTdIo8hdwtc1fh2wHMwW2Yk%3D&code_challenge_method=S256&response_type=code&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=zB3ZPifV1TW2GMuvuFkamSXfSNkHPQ&redirect_uri="
|
||||
}
|
||||
]
|
||||
}
|
||||
`}]),[_,p,i,s,f]}class Ve extends $e{constructor(l){super(),Se(this,l,De,ze,ye,{collection:0})}}export{Ve as default};
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import{S as je,i as xe,s as Je,N as Ue,O as J,e as s,w as k,b as p,c as K,f as b,g as d,h as o,m as I,x as de,P as Ee,Q as Ke,k as Ie,R as We,n as Ge,t as N,a as V,o as u,d as W,C as Le,p as Xe,r as G,u as Ye}from"./index-44098792.js";import{S as Ze}from"./SdkTabs-f73340a3.js";import{F as et}from"./FieldsQueryParam-eb4f33e4.js";function Ne(r,l,a){const n=r.slice();return n[5]=l[a],n}function Ve(r,l,a){const n=r.slice();return n[5]=l[a],n}function ze(r,l){let a,n=l[5].code+"",m,_,i,h;function g(){return l[4](l[5])}return{key:r,first:null,c(){a=s("button"),m=k(n),_=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(v,w){d(v,a,w),o(a,m),o(a,_),i||(h=Ye(a,"click",g),i=!0)},p(v,w){l=v,w&4&&n!==(n=l[5].code+"")&&de(m,n),w&6&&G(a,"active",l[1]===l[5].code)},d(v){v&&u(a),i=!1,h()}}}function Qe(r,l){let a,n,m,_;return n=new Ue({props:{content:l[5].body}}),{key:r,first:null,c(){a=s("div"),K(n.$$.fragment),m=p(),b(a,"class","tab-item"),G(a,"active",l[1]===l[5].code),this.first=a},m(i,h){d(i,a,h),I(n,a,null),o(a,m),_=!0},p(i,h){l=i;const g={};h&4&&(g.content=l[5].body),n.$set(g),(!_||h&6)&&G(a,"active",l[1]===l[5].code)},i(i){_||(N(n.$$.fragment,i),_=!0)},o(i){V(n.$$.fragment,i),_=!1},d(i){i&&u(a),W(n)}}}function tt(r){var De,Fe;let l,a,n=r[0].name+"",m,_,i,h,g,v,w,M,X,S,z,ue,Q,q,pe,Y,U=r[0].name+"",Z,he,fe,j,ee,D,te,T,oe,be,F,C,le,me,ae,_e,f,ke,R,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Pe,A,ie,O,ce,P,H,y=[],Re=new Map,Ae,E,$=[],Be=new Map,B;v=new Ze({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(De=r[0])==null?void 0:De.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(Fe=r[0])==null?void 0:Fe.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
`}}),R=new Ue({props:{content:"?expand=relField1,relField2.subRelField"}}),A=new et({});let x=J(r[2]);const Me=e=>e[5].code;for(let e=0;e<x.length;e+=1){let t=Ve(r,x,e),c=Me(t);Re.set(c,y[e]=ze(c,t))}let L=J(r[2]);const qe=e=>e[5].code;for(let e=0;e<L.length;e+=1){let t=Ne(r,L,e),c=qe(t);Be.set(c,$[e]=Qe(c,t))}return{c(){l=s("h3"),a=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),h=s("div"),h.innerHTML=`<p>Returns a new auth response (token and record data) for an
|
||||
<strong>already authenticated record</strong>.</p> <p><em>This method is usually called by users on page/screen reload to ensure that the previously stored
|
||||
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,g=p(),K(v.$$.fragment),w=p(),M=s("h6"),M.textContent="API details",X=p(),S=s("div"),z=s("strong"),z.textContent="POST",ue=p(),Q=s("div"),q=s("p"),pe=k("/api/collections/"),Y=s("strong"),Z=k(U),he=k("/auth-refresh"),fe=p(),j=s("p"),j.innerHTML="Requires record <code>Authorization:TOKEN</code> header",ee=p(),D=s("div"),D.textContent="Query parameters",te=p(),T=s("table"),oe=s("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',be=p(),F=s("tbody"),C=s("tr"),le=s("td"),le.textContent="expand",me=p(),ae=s("td"),ae.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ke=k(`Auto expand record relations. Ex.:
|
||||
`),K(R.$$.fragment),ge=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),$e=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),se=s("code"),se.textContent="expand",ye=k(" property (eg. "),ne=s("code"),ne.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).
|
||||
`),we=s("br"),Te=k(`
|
||||
Only the relations to which the request user has permissions to `),re=s("strong"),re.textContent="view",Ce=k(" will be expanded."),Pe=p(),K(A.$$.fragment),ie=p(),O=s("div"),O.textContent="Responses",ce=p(),P=s("div"),H=s("div");for(let e=0;e<y.length;e+=1)y[e].c();Ae=p(),E=s("div");for(let e=0;e<$.length;e+=1)$[e].c();b(l,"class","m-b-sm"),b(h,"class","content txt-lg m-b-sm"),b(M,"class","m-b-xs"),b(z,"class","label label-primary"),b(Q,"class","content"),b(j,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(D,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(O,"class","section-title"),b(H,"class","tabs-header compact combined left"),b(E,"class","tabs-content"),b(P,"class","tabs")},m(e,t){d(e,l,t),o(l,a),o(l,m),o(l,_),d(e,i,t),d(e,h,t),d(e,g,t),I(v,e,t),d(e,w,t),d(e,M,t),d(e,X,t),d(e,S,t),o(S,z),o(S,ue),o(S,Q),o(Q,q),o(q,pe),o(q,Y),o(Y,Z),o(q,he),o(S,fe),o(S,j),d(e,ee,t),d(e,D,t),d(e,te,t),d(e,T,t),o(T,oe),o(T,be),o(T,F),o(F,C),o(C,le),o(C,me),o(C,ae),o(C,_e),o(C,f),o(f,ke),I(R,f,null),o(f,ge),o(f,ve),o(f,$e),o(f,se),o(f,ye),o(f,ne),o(f,Se),o(f,we),o(f,Te),o(f,re),o(f,Ce),o(F,Pe),I(A,F,null),d(e,ie,t),d(e,O,t),d(e,ce,t),d(e,P,t),o(P,H);for(let c=0;c<y.length;c+=1)y[c]&&y[c].m(H,null);o(P,Ae),o(P,E);for(let c=0;c<$.length;c+=1)$[c]&&$[c].m(E,null);B=!0},p(e,[t]){var Oe,He;(!B||t&1)&&n!==(n=e[0].name+"")&&de(m,n);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(Oe=e[0])==null?void 0:Oe.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(He=e[0])==null?void 0:He.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
`),v.$set(c),(!B||t&1)&&U!==(U=e[0].name+"")&&de(Z,U),t&6&&(x=J(e[2]),y=Ee(y,t,Me,1,e,x,Re,H,Ke,ze,null,Ve)),t&6&&(L=J(e[2]),Ie(),$=Ee($,t,qe,1,e,L,Be,E,We,Qe,null,Ne),Ge())},i(e){if(!B){N(v.$$.fragment,e),N(R.$$.fragment,e),N(A.$$.fragment,e);for(let t=0;t<L.length;t+=1)N($[t]);B=!0}},o(e){V(v.$$.fragment,e),V(R.$$.fragment,e),V(A.$$.fragment,e);for(let t=0;t<$.length;t+=1)V($[t]);B=!1},d(e){e&&(u(l),u(i),u(h),u(g),u(w),u(M),u(X),u(S),u(ee),u(D),u(te),u(T),u(ie),u(O),u(ce),u(P)),W(v,e),W(R),W(A);for(let t=0;t<y.length;t+=1)y[t].d();for(let t=0;t<$.length;t+=1)$[t].d()}}}function ot(r,l,a){let n,{collection:m}=l,_=200,i=[];const h=g=>a(1,_=g.code);return r.$$set=g=>{"collection"in g&&a(0,m=g.collection)},r.$$.update=()=>{r.$$.dirty&1&&a(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Le.dummyCollectionRecord(m)},null,2)},{code:401,body:`
|
||||
{
|
||||
"code": 401,
|
||||
"message": "The request requires valid record authorization token to be set.",
|
||||
"data": {}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "The authorized record model is not allowed to perform this action.",
|
||||
"data": {}
|
||||
}
|
||||
`},{code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "Missing auth record context.",
|
||||
"data": {}
|
||||
}
|
||||
`}])},a(3,n=Le.getApiExampleUrl(Xe.baseUrl)),[m,_,i,n,h]}class nt extends je{constructor(l){super(),xe(this,l,ot,tt,Je,{collection:0})}}export{nt as default};
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
import{S as Ue,i as je,s as xe,M as Qe,e as s,w as k,b as p,c as J,f as b,g as d,h as o,m as K,x as ce,N as Oe,P as Je,k as Ke,Q as Ie,n as We,t as N,a as V,o as u,d as I,T as Ge,C as Ee,p as Xe,r as W,u as Ye}from"./index-9e84ac8a.js";import{S as Ze}from"./SdkTabs-821a18db.js";import{F as et}from"./FieldsQueryParam-664da4c8.js";function Le(r,l,a){const n=r.slice();return n[5]=l[a],n}function Ne(r,l,a){const n=r.slice();return n[5]=l[a],n}function Ve(r,l){let a,n=l[5].code+"",m,_,i,f;function v(){return l[4](l[5])}return{key:r,first:null,c(){a=s("button"),m=k(n),_=p(),b(a,"class","tab-item"),W(a,"active",l[1]===l[5].code),this.first=a},m($,w){d($,a,w),o(a,m),o(a,_),i||(f=Ye(a,"click",v),i=!0)},p($,w){l=$,w&4&&n!==(n=l[5].code+"")&&ce(m,n),w&6&&W(a,"active",l[1]===l[5].code)},d($){$&&u(a),i=!1,f()}}}function ze(r,l){let a,n,m,_;return n=new Qe({props:{content:l[5].body}}),{key:r,first:null,c(){a=s("div"),J(n.$$.fragment),m=p(),b(a,"class","tab-item"),W(a,"active",l[1]===l[5].code),this.first=a},m(i,f){d(i,a,f),K(n,a,null),o(a,m),_=!0},p(i,f){l=i;const v={};f&4&&(v.content=l[5].body),n.$set(v),(!_||f&6)&&W(a,"active",l[1]===l[5].code)},i(i){_||(N(n.$$.fragment,i),_=!0)},o(i){V(n.$$.fragment,i),_=!1},d(i){i&&u(a),I(n)}}}function tt(r){var qe,De;let l,a,n=r[0].name+"",m,_,i,f,v,$,w,B,G,S,z,de,Q,q,ue,X,U=r[0].name+"",Y,pe,fe,j,Z,D,ee,T,te,he,F,C,oe,be,le,me,h,_e,R,ke,ve,$e,ae,ge,se,ye,Se,we,ne,Te,Ce,M,re,H,ie,P,O,y=[],Pe=new Map,Re,E,g=[],Me=new Map,A;$=new Ze({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(qe=r[0])==null?void 0:qe.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(De=r[0])==null?void 0:De.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
`}}),R=new Qe({props:{content:"?expand=relField1,relField2.subRelField"}}),M=new et({});let x=r[2];const Ae=e=>e[5].code;for(let e=0;e<x.length;e+=1){let t=Ne(r,x,e),c=Ae(t);Pe.set(c,y[e]=Ve(c,t))}let L=r[2];const Be=e=>e[5].code;for(let e=0;e<L.length;e+=1){let t=Le(r,L,e),c=Be(t);Me.set(c,g[e]=ze(c,t))}return{c(){l=s("h3"),a=k("Auth refresh ("),m=k(n),_=k(")"),i=p(),f=s("div"),f.innerHTML=`<p>Returns a new auth response (token and record data) for an
|
||||
<strong>already authenticated record</strong>.</p>
|
||||
<p><em>This method is usually called by users on page/screen reload to ensure that the previously stored
|
||||
data in <code>pb.authStore</code> is still valid and up-to-date.</em></p>`,v=p(),J($.$$.fragment),w=p(),B=s("h6"),B.textContent="API details",G=p(),S=s("div"),z=s("strong"),z.textContent="POST",de=p(),Q=s("div"),q=s("p"),ue=k("/api/collections/"),X=s("strong"),Y=k(U),pe=k("/auth-refresh"),fe=p(),j=s("p"),j.innerHTML="Requires record <code>Authorization:TOKEN</code> header",Z=p(),D=s("div"),D.textContent="Query parameters",ee=p(),T=s("table"),te=s("thead"),te.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,he=p(),F=s("tbody"),C=s("tr"),oe=s("td"),oe.textContent="expand",be=p(),le=s("td"),le.innerHTML='<span class="label">String</span>',me=p(),h=s("td"),_e=k(`Auto expand record relations. Ex.:
|
||||
`),J(R.$$.fragment),ke=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),$e=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),ae=s("code"),ae.textContent="expand",ge=k(" property (eg. "),se=s("code"),se.textContent='"expand": {"relField1": {...}, ...}',ye=k(`).
|
||||
`),Se=s("br"),we=k(`
|
||||
Only the relations to which the request user has permissions to `),ne=s("strong"),ne.textContent="view",Te=k(" will be expanded."),Ce=p(),J(M.$$.fragment),re=p(),H=s("div"),H.textContent="Responses",ie=p(),P=s("div"),O=s("div");for(let e=0;e<y.length;e+=1)y[e].c();Re=p(),E=s("div");for(let e=0;e<g.length;e+=1)g[e].c();b(l,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(B,"class","m-b-xs"),b(z,"class","label label-primary"),b(Q,"class","content"),b(j,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(D,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(H,"class","section-title"),b(O,"class","tabs-header compact left"),b(E,"class","tabs-content"),b(P,"class","tabs")},m(e,t){d(e,l,t),o(l,a),o(l,m),o(l,_),d(e,i,t),d(e,f,t),d(e,v,t),K($,e,t),d(e,w,t),d(e,B,t),d(e,G,t),d(e,S,t),o(S,z),o(S,de),o(S,Q),o(Q,q),o(q,ue),o(q,X),o(X,Y),o(q,pe),o(S,fe),o(S,j),d(e,Z,t),d(e,D,t),d(e,ee,t),d(e,T,t),o(T,te),o(T,he),o(T,F),o(F,C),o(C,oe),o(C,be),o(C,le),o(C,me),o(C,h),o(h,_e),K(R,h,null),o(h,ke),o(h,ve),o(h,$e),o(h,ae),o(h,ge),o(h,se),o(h,ye),o(h,Se),o(h,we),o(h,ne),o(h,Te),o(F,Ce),K(M,F,null),d(e,re,t),d(e,H,t),d(e,ie,t),d(e,P,t),o(P,O);for(let c=0;c<y.length;c+=1)y[c]&&y[c].m(O,null);o(P,Re),o(P,E);for(let c=0;c<g.length;c+=1)g[c]&&g[c].m(E,null);A=!0},p(e,[t]){var Fe,He;(!A||t&1)&&n!==(n=e[0].name+"")&&ce(m,n);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(Fe=e[0])==null?void 0:Fe.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(He=e[0])==null?void 0:He.name}').authRefresh();
|
||||
|
||||
// after the above you can also access the refreshed auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
`),$.$set(c),(!A||t&1)&&U!==(U=e[0].name+"")&&ce(Y,U),t&6&&(x=e[2],y=Oe(y,t,Ae,1,e,x,Pe,O,Je,Ve,null,Ne)),t&6&&(L=e[2],Ke(),g=Oe(g,t,Be,1,e,L,Me,E,Ie,ze,null,Le),We())},i(e){if(!A){N($.$$.fragment,e),N(R.$$.fragment,e),N(M.$$.fragment,e);for(let t=0;t<L.length;t+=1)N(g[t]);A=!0}},o(e){V($.$$.fragment,e),V(R.$$.fragment,e),V(M.$$.fragment,e);for(let t=0;t<g.length;t+=1)V(g[t]);A=!1},d(e){e&&u(l),e&&u(i),e&&u(f),e&&u(v),I($,e),e&&u(w),e&&u(B),e&&u(G),e&&u(S),e&&u(Z),e&&u(D),e&&u(ee),e&&u(T),I(R),I(M),e&&u(re),e&&u(H),e&&u(ie),e&&u(P);for(let t=0;t<y.length;t+=1)y[t].d();for(let t=0;t<g.length;t+=1)g[t].d()}}}function ot(r,l,a){let n,{collection:m=new Ge}=l,_=200,i=[];const f=v=>a(1,_=v.code);return r.$$set=v=>{"collection"in v&&a(0,m=v.collection)},r.$$.update=()=>{r.$$.dirty&1&&a(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Ee.dummyCollectionRecord(m)},null,2)},{code:401,body:`
|
||||
{
|
||||
"code": 401,
|
||||
"message": "The request requires valid record authorization token to be set.",
|
||||
"data": {}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "The authorized record model is not allowed to perform this action.",
|
||||
"data": {}
|
||||
}
|
||||
`},{code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "Missing auth record context.",
|
||||
"data": {}
|
||||
}
|
||||
`}])},a(3,n=Ee.getApiExampleUrl(Xe.baseUrl)),[m,_,i,n,f]}class nt extends Ue{constructor(l){super(),je(this,l,ot,tt,xe,{collection:0})}}export{nt as default};
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
import{S as Le,i as Ee,s as Je,N as Ve,O as z,e as s,w as k,b as h,c as I,f as p,g as r,h as a,m as K,x as pe,P as We,Q as Ne,k as Qe,R as ze,n as Ie,t as V,a as L,o as c,d as G,C as Ue,p as Ke,r as X,u as Ge}from"./index-44098792.js";import{S as Xe}from"./SdkTabs-f73340a3.js";import{F as Ye}from"./FieldsQueryParam-eb4f33e4.js";function Be(i,l,o){const n=i.slice();return n[5]=l[o],n}function Fe(i,l,o){const n=i.slice();return n[5]=l[o],n}function He(i,l){let o,n=l[5].code+"",f,g,d,b;function _(){return l[4](l[5])}return{key:i,first:null,c(){o=s("button"),f=k(n),g=h(),p(o,"class","tab-item"),X(o,"active",l[1]===l[5].code),this.first=o},m(v,A){r(v,o,A),a(o,f),a(o,g),d||(b=Ge(o,"click",_),d=!0)},p(v,A){l=v,A&4&&n!==(n=l[5].code+"")&&pe(f,n),A&6&&X(o,"active",l[1]===l[5].code)},d(v){v&&c(o),d=!1,b()}}}function je(i,l){let o,n,f,g;return n=new Ve({props:{content:l[5].body}}),{key:i,first:null,c(){o=s("div"),I(n.$$.fragment),f=h(),p(o,"class","tab-item"),X(o,"active",l[1]===l[5].code),this.first=o},m(d,b){r(d,o,b),K(n,o,null),a(o,f),g=!0},p(d,b){l=d;const _={};b&4&&(_.content=l[5].body),n.$set(_),(!g||b&6)&&X(o,"active",l[1]===l[5].code)},i(d){g||(V(n.$$.fragment,d),g=!0)},o(d){L(n.$$.fragment,d),g=!1},d(d){d&&c(o),G(n)}}}function Ze(i){let l,o,n=i[0].name+"",f,g,d,b,_,v,A,P,Y,S,E,be,J,R,me,Z,N=i[0].name+"",ee,fe,te,M,ae,x,le,W,oe,y,se,ge,U,$,ne,ke,ie,_e,m,ve,C,we,Oe,Ae,re,Se,ce,ye,$e,Te,de,Ce,qe,q,ue,B,he,T,F,O=[],De=new Map,Pe,H,w=[],Re=new Map,D;v=new Xe({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${i[3]}/api/oauth2-redirect as redirect url.
|
||||
const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' });
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// const authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final pb = PocketBase('${i[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${i[3]}/api/oauth2-redirect as redirect url.
|
||||
final authData = await pb.collection('users').authWithOAuth2('google', (url) async {
|
||||
await launchUrl(url);
|
||||
});
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// final authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`}}),C=new Ve({props:{content:"?expand=relField1,relField2.subRelField"}}),q=new Ye({});let Q=z(i[2]);const Me=e=>e[5].code;for(let e=0;e<Q.length;e+=1){let t=Fe(i,Q,e),u=Me(t);De.set(u,O[e]=He(u,t))}let j=z(i[2]);const xe=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=Be(i,j,e),u=xe(t);Re.set(u,w[e]=je(u,t))}return{c(){l=s("h3"),o=k("Auth with OAuth2 ("),f=k(n),g=k(")"),d=h(),b=s("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p> <p>For more details please check the
|
||||
<a href="https://pocketbase.io/docs/authentication/#oauth2-integration" target="_blank" rel="noopener noreferrer">OAuth2 integration documentation
|
||||
</a>.</p>`,_=h(),I(v.$$.fragment),A=h(),P=s("h6"),P.textContent="API details",Y=h(),S=s("div"),E=s("strong"),E.textContent="POST",be=h(),J=s("div"),R=s("p"),me=k("/api/collections/"),Z=s("strong"),ee=k(N),fe=k("/auth-with-oauth2"),te=h(),M=s("div"),M.textContent="Body Parameters",ae=h(),x=s("table"),x.innerHTML=`<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>provider</span></div></td> <td><span class="label">String</span></td> <td>The name of the OAuth2 client provider (eg. "google").</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>code</span></div></td> <td><span class="label">String</span></td> <td>The authorization code returned from the initial request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>codeVerifier</span></div></td> <td><span class="label">String</span></td> <td>The code verifier sent with the initial request as part of the code_challenge.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>redirectUrl</span></div></td> <td><span class="label">String</span></td> <td>The redirect url sent with the initial request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>createData</span></div></td> <td><span class="label">Object</span></td> <td><p>Optional data that will be used when creating the auth record on OAuth2 sign-up.</p> <p>The created auth record must comply with the same requirements and validations in the
|
||||
regular <strong>create</strong> action.
|
||||
<br/> <em>The data can only be in <code>json</code>, aka. <code>multipart/form-data</code> and files
|
||||
upload currently are not supported during OAuth2 sign-ups.</em></p></td></tr></tbody>`,le=h(),W=s("div"),W.textContent="Query parameters",oe=h(),y=s("table"),se=s("thead"),se.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',ge=h(),U=s("tbody"),$=s("tr"),ne=s("td"),ne.textContent="expand",ke=h(),ie=s("td"),ie.innerHTML='<span class="label">String</span>',_e=h(),m=s("td"),ve=k(`Auto expand record relations. Ex.:
|
||||
`),I(C.$$.fragment),we=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Oe=s("br"),Ae=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),re=s("code"),re.textContent="expand",Se=k(" property (eg. "),ce=s("code"),ce.textContent='"expand": {"relField1": {...}, ...}',ye=k(`).
|
||||
`),$e=s("br"),Te=k(`
|
||||
Only the relations to which the request user has permissions to `),de=s("strong"),de.textContent="view",Ce=k(" will be expanded."),qe=h(),I(q.$$.fragment),ue=h(),B=s("div"),B.textContent="Responses",he=h(),T=s("div"),F=s("div");for(let e=0;e<O.length;e+=1)O[e].c();Pe=h(),H=s("div");for(let e=0;e<w.length;e+=1)w[e].c();p(l,"class","m-b-sm"),p(b,"class","content txt-lg m-b-sm"),p(P,"class","m-b-xs"),p(E,"class","label label-primary"),p(J,"class","content"),p(S,"class","alert alert-success"),p(M,"class","section-title"),p(x,"class","table-compact table-border m-b-base"),p(W,"class","section-title"),p(y,"class","table-compact table-border m-b-base"),p(B,"class","section-title"),p(F,"class","tabs-header compact combined left"),p(H,"class","tabs-content"),p(T,"class","tabs")},m(e,t){r(e,l,t),a(l,o),a(l,f),a(l,g),r(e,d,t),r(e,b,t),r(e,_,t),K(v,e,t),r(e,A,t),r(e,P,t),r(e,Y,t),r(e,S,t),a(S,E),a(S,be),a(S,J),a(J,R),a(R,me),a(R,Z),a(Z,ee),a(R,fe),r(e,te,t),r(e,M,t),r(e,ae,t),r(e,x,t),r(e,le,t),r(e,W,t),r(e,oe,t),r(e,y,t),a(y,se),a(y,ge),a(y,U),a(U,$),a($,ne),a($,ke),a($,ie),a($,_e),a($,m),a(m,ve),K(C,m,null),a(m,we),a(m,Oe),a(m,Ae),a(m,re),a(m,Se),a(m,ce),a(m,ye),a(m,$e),a(m,Te),a(m,de),a(m,Ce),a(U,qe),K(q,U,null),r(e,ue,t),r(e,B,t),r(e,he,t),r(e,T,t),a(T,F);for(let u=0;u<O.length;u+=1)O[u]&&O[u].m(F,null);a(T,Pe),a(T,H);for(let u=0;u<w.length;u+=1)w[u]&&w[u].m(H,null);D=!0},p(e,[t]){(!D||t&1)&&n!==(n=e[0].name+"")&&pe(f,n);const u={};t&8&&(u.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${e[3]}/api/oauth2-redirect as redirect url.
|
||||
const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' });
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// const authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`),t&8&&(u.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${e[3]}/api/oauth2-redirect as redirect url.
|
||||
final authData = await pb.collection('users').authWithOAuth2('google', (url) async {
|
||||
await launchUrl(url);
|
||||
});
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// final authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`),v.$set(u),(!D||t&1)&&N!==(N=e[0].name+"")&&pe(ee,N),t&6&&(Q=z(e[2]),O=We(O,t,Me,1,e,Q,De,F,Ne,He,null,Fe)),t&6&&(j=z(e[2]),Qe(),w=We(w,t,xe,1,e,j,Re,H,ze,je,null,Be),Ie())},i(e){if(!D){V(v.$$.fragment,e),V(C.$$.fragment,e),V(q.$$.fragment,e);for(let t=0;t<j.length;t+=1)V(w[t]);D=!0}},o(e){L(v.$$.fragment,e),L(C.$$.fragment,e),L(q.$$.fragment,e);for(let t=0;t<w.length;t+=1)L(w[t]);D=!1},d(e){e&&(c(l),c(d),c(b),c(_),c(A),c(P),c(Y),c(S),c(te),c(M),c(ae),c(x),c(le),c(W),c(oe),c(y),c(ue),c(B),c(he),c(T)),G(v,e),G(C),G(q);for(let t=0;t<O.length;t+=1)O[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function et(i,l,o){let n,{collection:f}=l,g=200,d=[];const b=_=>o(1,g=_.code);return i.$$set=_=>{"collection"in _&&o(0,f=_.collection)},i.$$.update=()=>{i.$$.dirty&1&&o(2,d=[{code:200,body:JSON.stringify({token:"JWT_AUTH_TOKEN",record:Ue.dummyCollectionRecord(f),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png",accessToken:"...",refreshToken:"...",rawUser:{}}},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while submitting the form.",
|
||||
"data": {
|
||||
"provider": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}])},o(3,n=Ue.getApiExampleUrl(Ke.baseUrl)),[f,g,d,n,b]}class ot extends Le{constructor(l){super(),Ee(this,l,et,Ze,Je,{collection:0})}}export{ot as default};
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
import{S as Ve,i as Le,s as Ee,M as je,e as s,w as k,b as h,c as z,f as p,g as r,h as a,m as I,x as he,N as xe,P as Je,k as Ne,Q as Qe,n as ze,t as V,a as L,o as c,d as K,T as Ie,C as We,p as Ke,r as G,u as Ge}from"./index-9e84ac8a.js";import{S as Xe}from"./SdkTabs-821a18db.js";import{F as Ye}from"./FieldsQueryParam-664da4c8.js";function Ue(i,l,o){const n=i.slice();return n[5]=l[o],n}function Be(i,l,o){const n=i.slice();return n[5]=l[o],n}function Fe(i,l){let o,n=l[5].code+"",m,g,u,b;function _(){return l[4](l[5])}return{key:i,first:null,c(){o=s("button"),m=k(n),g=h(),p(o,"class","tab-item"),G(o,"active",l[1]===l[5].code),this.first=o},m(v,A){r(v,o,A),a(o,m),a(o,g),u||(b=Ge(o,"click",_),u=!0)},p(v,A){l=v,A&4&&n!==(n=l[5].code+"")&&he(m,n),A&6&&G(o,"active",l[1]===l[5].code)},d(v){v&&c(o),u=!1,b()}}}function He(i,l){let o,n,m,g;return n=new je({props:{content:l[5].body}}),{key:i,first:null,c(){o=s("div"),z(n.$$.fragment),m=h(),p(o,"class","tab-item"),G(o,"active",l[1]===l[5].code),this.first=o},m(u,b){r(u,o,b),I(n,o,null),a(o,m),g=!0},p(u,b){l=u;const _={};b&4&&(_.content=l[5].body),n.$set(_),(!g||b&6)&&G(o,"active",l[1]===l[5].code)},i(u){g||(V(n.$$.fragment,u),g=!0)},o(u){L(n.$$.fragment,u),g=!1},d(u){u&&c(o),K(n)}}}function Ze(i){let l,o,n=i[0].name+"",m,g,u,b,_,v,A,P,X,S,E,pe,J,M,be,Y,N=i[0].name+"",Z,fe,ee,R,te,x,ae,W,le,y,oe,me,U,$,se,ge,ne,ke,f,_e,C,ve,we,Oe,ie,Ae,re,Se,ye,$e,ce,Te,Ce,q,ue,B,de,T,F,O=[],qe=new Map,De,H,w=[],Pe=new Map,D;v=new Xe({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${i[3]}/api/oauth2-redirect as redirect url.
|
||||
const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' });
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// const authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final pb = PocketBase('${i[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${i[3]}/api/oauth2-redirect as redirect url.
|
||||
final authData = await pb.collection('users').authWithOAuth2('google', (url) async {
|
||||
await launchUrl(url);
|
||||
});
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// final authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`}}),C=new je({props:{content:"?expand=relField1,relField2.subRelField"}}),q=new Ye({});let Q=i[2];const Me=e=>e[5].code;for(let e=0;e<Q.length;e+=1){let t=Be(i,Q,e),d=Me(t);qe.set(d,O[e]=Fe(d,t))}let j=i[2];const Re=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=Ue(i,j,e),d=Re(t);Pe.set(d,w[e]=He(d,t))}return{c(){l=s("h3"),o=k("Auth with OAuth2 ("),m=k(n),g=k(")"),u=h(),b=s("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p>
|
||||
<p>For more details please check the
|
||||
<a href="https://pocketbase.io/docs/authentication/#oauth2-integration" target="_blank" rel="noopener noreferrer">OAuth2 integration documentation
|
||||
</a>.</p>`,_=h(),z(v.$$.fragment),A=h(),P=s("h6"),P.textContent="API details",X=h(),S=s("div"),E=s("strong"),E.textContent="POST",pe=h(),J=s("div"),M=s("p"),be=k("/api/collections/"),Y=s("strong"),Z=k(N),fe=k("/auth-with-oauth2"),ee=h(),R=s("div"),R.textContent="Body Parameters",te=h(),x=s("table"),x.innerHTML=`<thead><tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr></thead>
|
||||
<tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>provider</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The name of the OAuth2 client provider (eg. "google").</td></tr>
|
||||
<tr><td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>code</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The authorization code returned from the initial request.</td></tr>
|
||||
<tr><td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>codeVerifier</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The code verifier sent with the initial request as part of the code_challenge.</td></tr>
|
||||
<tr><td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>redirectUrl</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The redirect url sent with the initial request.</td></tr>
|
||||
<tr><td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<span>createData</span></div></td>
|
||||
<td><span class="label">Object</span></td>
|
||||
<td><p>Optional data that will be used when creating the auth record on OAuth2 sign-up.</p>
|
||||
<p>The created auth record must comply with the same requirements and validations in the
|
||||
regular <strong>create</strong> action.
|
||||
<br/>
|
||||
<em>The data can only be in <code>json</code>, aka. <code>multipart/form-data</code> and files
|
||||
upload currently are not supported during OAuth2 sign-ups.</em></p></td></tr></tbody>`,ae=h(),W=s("div"),W.textContent="Query parameters",le=h(),y=s("table"),oe=s("thead"),oe.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,me=h(),U=s("tbody"),$=s("tr"),se=s("td"),se.textContent="expand",ge=h(),ne=s("td"),ne.innerHTML='<span class="label">String</span>',ke=h(),f=s("td"),_e=k(`Auto expand record relations. Ex.:
|
||||
`),z(C.$$.fragment),ve=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),we=s("br"),Oe=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),ie=s("code"),ie.textContent="expand",Ae=k(" property (eg. "),re=s("code"),re.textContent='"expand": {"relField1": {...}, ...}',Se=k(`).
|
||||
`),ye=s("br"),$e=k(`
|
||||
Only the relations to which the request user has permissions to `),ce=s("strong"),ce.textContent="view",Te=k(" will be expanded."),Ce=h(),z(q.$$.fragment),ue=h(),B=s("div"),B.textContent="Responses",de=h(),T=s("div"),F=s("div");for(let e=0;e<O.length;e+=1)O[e].c();De=h(),H=s("div");for(let e=0;e<w.length;e+=1)w[e].c();p(l,"class","m-b-sm"),p(b,"class","content txt-lg m-b-sm"),p(P,"class","m-b-xs"),p(E,"class","label label-primary"),p(J,"class","content"),p(S,"class","alert alert-success"),p(R,"class","section-title"),p(x,"class","table-compact table-border m-b-base"),p(W,"class","section-title"),p(y,"class","table-compact table-border m-b-base"),p(B,"class","section-title"),p(F,"class","tabs-header compact left"),p(H,"class","tabs-content"),p(T,"class","tabs")},m(e,t){r(e,l,t),a(l,o),a(l,m),a(l,g),r(e,u,t),r(e,b,t),r(e,_,t),I(v,e,t),r(e,A,t),r(e,P,t),r(e,X,t),r(e,S,t),a(S,E),a(S,pe),a(S,J),a(J,M),a(M,be),a(M,Y),a(Y,Z),a(M,fe),r(e,ee,t),r(e,R,t),r(e,te,t),r(e,x,t),r(e,ae,t),r(e,W,t),r(e,le,t),r(e,y,t),a(y,oe),a(y,me),a(y,U),a(U,$),a($,se),a($,ge),a($,ne),a($,ke),a($,f),a(f,_e),I(C,f,null),a(f,ve),a(f,we),a(f,Oe),a(f,ie),a(f,Ae),a(f,re),a(f,Se),a(f,ye),a(f,$e),a(f,ce),a(f,Te),a(U,Ce),I(q,U,null),r(e,ue,t),r(e,B,t),r(e,de,t),r(e,T,t),a(T,F);for(let d=0;d<O.length;d+=1)O[d]&&O[d].m(F,null);a(T,De),a(T,H);for(let d=0;d<w.length;d+=1)w[d]&&w[d].m(H,null);D=!0},p(e,[t]){(!D||t&1)&&n!==(n=e[0].name+"")&&he(m,n);const d={};t&8&&(d.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${e[3]}/api/oauth2-redirect as redirect url.
|
||||
const authData = await pb.collection('users').authWithOAuth2({ provider: 'google' });
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// const authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`),t&8&&(d.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
// OAuth2 authentication with a single realtime call.
|
||||
//
|
||||
// Make sure to register ${e[3]}/api/oauth2-redirect as redirect url.
|
||||
final authData = await pb.collection('users').authWithOAuth2('google', (url) async {
|
||||
await launchUrl(url);
|
||||
});
|
||||
|
||||
// OR authenticate with manual OAuth2 code exchange
|
||||
// final authData = await pb.collection('users').authWithOAuth2Code(...);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated model
|
||||
pb.authStore.clear();
|
||||
`),v.$set(d),(!D||t&1)&&N!==(N=e[0].name+"")&&he(Z,N),t&6&&(Q=e[2],O=xe(O,t,Me,1,e,Q,qe,F,Je,Fe,null,Be)),t&6&&(j=e[2],Ne(),w=xe(w,t,Re,1,e,j,Pe,H,Qe,He,null,Ue),ze())},i(e){if(!D){V(v.$$.fragment,e),V(C.$$.fragment,e),V(q.$$.fragment,e);for(let t=0;t<j.length;t+=1)V(w[t]);D=!0}},o(e){L(v.$$.fragment,e),L(C.$$.fragment,e),L(q.$$.fragment,e);for(let t=0;t<w.length;t+=1)L(w[t]);D=!1},d(e){e&&c(l),e&&c(u),e&&c(b),e&&c(_),K(v,e),e&&c(A),e&&c(P),e&&c(X),e&&c(S),e&&c(ee),e&&c(R),e&&c(te),e&&c(x),e&&c(ae),e&&c(W),e&&c(le),e&&c(y),K(C),K(q),e&&c(ue),e&&c(B),e&&c(de),e&&c(T);for(let t=0;t<O.length;t+=1)O[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function et(i,l,o){let n,{collection:m=new Ie}=l,g=200,u=[];const b=_=>o(1,g=_.code);return i.$$set=_=>{"collection"in _&&o(0,m=_.collection)},i.$$.update=()=>{i.$$.dirty&1&&o(2,u=[{code:200,body:JSON.stringify({token:"JWT_AUTH_TOKEN",record:We.dummyCollectionRecord(m),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarUrl:"https://example.com/avatar.png",accessToken:"...",refreshToken:"...",rawUser:{}}},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while submitting the form.",
|
||||
"data": {
|
||||
"provider": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}])},o(3,n=We.getApiExampleUrl(Ke.baseUrl)),[m,g,u,n,b]}class ot extends Ve{constructor(l){super(),Le(this,l,et,Ze,Ee,{collection:0})}}export{ot as default};
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
import{S as ve,i as ye,s as ge,M as we,e as s,w as f,b as d,c as ot,f as h,g as r,h as e,m as st,x as Ut,N as ue,P as $e,k as Pe,Q as Re,n as Ce,t as Z,a as x,o as c,d as nt,T as Te,C as fe,p as Ae,r as it,u as Oe}from"./index-9e84ac8a.js";import{S as Me}from"./SdkTabs-821a18db.js";import{F as Ue}from"./FieldsQueryParam-664da4c8.js";function pe(n,l,a){const i=n.slice();return i[8]=l[a],i}function be(n,l,a){const i=n.slice();return i[8]=l[a],i}function De(n){let l;return{c(){l=f("email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function Ee(n){let l;return{c(){l=f("username")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function We(n){let l;return{c(){l=f("username/email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function me(n){let l;return{c(){l=s("strong"),l.textContent="username"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function he(n){let l;return{c(){l=f("or")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function _e(n){let l;return{c(){l=s("strong"),l.textContent="email"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function ke(n,l){let a,i=l[8].code+"",S,m,p,u;function _(){return l[7](l[8])}return{key:n,first:null,c(){a=s("button"),S=f(i),m=d(),h(a,"class","tab-item"),it(a,"active",l[3]===l[8].code),this.first=a},m(R,C){r(R,a,C),e(a,S),e(a,m),p||(u=Oe(a,"click",_),p=!0)},p(R,C){l=R,C&16&&i!==(i=l[8].code+"")&&Ut(S,i),C&24&&it(a,"active",l[3]===l[8].code)},d(R){R&&c(a),p=!1,u()}}}function Se(n,l){let a,i,S,m;return i=new we({props:{content:l[8].body}}),{key:n,first:null,c(){a=s("div"),ot(i.$$.fragment),S=d(),h(a,"class","tab-item"),it(a,"active",l[3]===l[8].code),this.first=a},m(p,u){r(p,a,u),st(i,a,null),e(a,S),m=!0},p(p,u){l=p;const _={};u&16&&(_.content=l[8].body),i.$set(_),(!m||u&24)&&it(a,"active",l[3]===l[8].code)},i(p){m||(Z(i.$$.fragment,p),m=!0)},o(p){x(i.$$.fragment,p),m=!1},d(p){p&&c(a),nt(i)}}}function Le(n){var ie,re;let l,a,i=n[0].name+"",S,m,p,u,_,R,C,T,B,Dt,rt,O,ct,N,dt,M,tt,Et,et,I,Wt,ut,lt=n[0].name+"",ft,Lt,pt,V,bt,U,mt,Bt,Q,D,ht,qt,_t,Ft,$,Ht,kt,St,wt,Yt,vt,yt,j,gt,E,$t,Nt,J,W,Pt,It,Rt,Vt,k,Qt,q,jt,Jt,Kt,Ct,zt,Tt,Gt,Xt,Zt,At,xt,te,F,Ot,K,Mt,L,z,A=[],ee=new Map,le,G,w=[],ae=new Map,H;function oe(t,o){if(t[1]&&t[2])return We;if(t[1])return Ee;if(t[2])return De}let Y=oe(n),P=Y&&Y(n);O=new Me({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[6]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(ie=n[0])==null?void 0:ie.name}').authWithPassword(
|
||||
'${n[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[6]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(re=n[0])==null?void 0:re.name}').authWithPassword(
|
||||
'${n[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`}});let v=n[1]&&me(),y=n[1]&&n[2]&&he(),g=n[2]&&_e();q=new we({props:{content:"?expand=relField1,relField2.subRelField"}}),F=new Ue({});let at=n[4];const se=t=>t[8].code;for(let t=0;t<at.length;t+=1){let o=be(n,at,t),b=se(o);ee.set(b,A[t]=ke(b,o))}let X=n[4];const ne=t=>t[8].code;for(let t=0;t<X.length;t+=1){let o=pe(n,X,t),b=ne(o);ae.set(b,w[t]=Se(b,o))}return{c(){l=s("h3"),a=f("Auth with password ("),S=f(i),m=f(")"),p=d(),u=s("div"),_=s("p"),R=f(`Returns new auth token and account data by a combination of
|
||||
`),C=s("strong"),P&&P.c(),T=f(`
|
||||
and `),B=s("strong"),B.textContent="password",Dt=f("."),rt=d(),ot(O.$$.fragment),ct=d(),N=s("h6"),N.textContent="API details",dt=d(),M=s("div"),tt=s("strong"),tt.textContent="POST",Et=d(),et=s("div"),I=s("p"),Wt=f("/api/collections/"),ut=s("strong"),ft=f(lt),Lt=f("/auth-with-password"),pt=d(),V=s("div"),V.textContent="Body Parameters",bt=d(),U=s("table"),mt=s("thead"),mt.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr>`,Bt=d(),Q=s("tbody"),D=s("tr"),ht=s("td"),ht.innerHTML=`<div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>identity</span></div>`,qt=d(),_t=s("td"),_t.innerHTML='<span class="label">String</span>',Ft=d(),$=s("td"),Ht=f(`The
|
||||
`),v&&v.c(),kt=d(),y&&y.c(),St=d(),g&&g.c(),wt=f(`
|
||||
of the record to authenticate.`),Yt=d(),vt=s("tr"),vt.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>password</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The auth record password.</td>`,yt=d(),j=s("div"),j.textContent="Query parameters",gt=d(),E=s("table"),$t=s("thead"),$t.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,Nt=d(),J=s("tbody"),W=s("tr"),Pt=s("td"),Pt.textContent="expand",It=d(),Rt=s("td"),Rt.innerHTML='<span class="label">String</span>',Vt=d(),k=s("td"),Qt=f(`Auto expand record relations. Ex.:
|
||||
`),ot(q.$$.fragment),jt=f(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Jt=s("br"),Kt=f(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),Ct=s("code"),Ct.textContent="expand",zt=f(" property (eg. "),Tt=s("code"),Tt.textContent='"expand": {"relField1": {...}, ...}',Gt=f(`).
|
||||
`),Xt=s("br"),Zt=f(`
|
||||
Only the relations to which the request user has permissions to `),At=s("strong"),At.textContent="view",xt=f(" will be expanded."),te=d(),ot(F.$$.fragment),Ot=d(),K=s("div"),K.textContent="Responses",Mt=d(),L=s("div"),z=s("div");for(let t=0;t<A.length;t+=1)A[t].c();le=d(),G=s("div");for(let t=0;t<w.length;t+=1)w[t].c();h(l,"class","m-b-sm"),h(u,"class","content txt-lg m-b-sm"),h(N,"class","m-b-xs"),h(tt,"class","label label-primary"),h(et,"class","content"),h(M,"class","alert alert-success"),h(V,"class","section-title"),h(U,"class","table-compact table-border m-b-base"),h(j,"class","section-title"),h(E,"class","table-compact table-border m-b-base"),h(K,"class","section-title"),h(z,"class","tabs-header compact left"),h(G,"class","tabs-content"),h(L,"class","tabs")},m(t,o){r(t,l,o),e(l,a),e(l,S),e(l,m),r(t,p,o),r(t,u,o),e(u,_),e(_,R),e(_,C),P&&P.m(C,null),e(_,T),e(_,B),e(_,Dt),r(t,rt,o),st(O,t,o),r(t,ct,o),r(t,N,o),r(t,dt,o),r(t,M,o),e(M,tt),e(M,Et),e(M,et),e(et,I),e(I,Wt),e(I,ut),e(ut,ft),e(I,Lt),r(t,pt,o),r(t,V,o),r(t,bt,o),r(t,U,o),e(U,mt),e(U,Bt),e(U,Q),e(Q,D),e(D,ht),e(D,qt),e(D,_t),e(D,Ft),e(D,$),e($,Ht),v&&v.m($,null),e($,kt),y&&y.m($,null),e($,St),g&&g.m($,null),e($,wt),e(Q,Yt),e(Q,vt),r(t,yt,o),r(t,j,o),r(t,gt,o),r(t,E,o),e(E,$t),e(E,Nt),e(E,J),e(J,W),e(W,Pt),e(W,It),e(W,Rt),e(W,Vt),e(W,k),e(k,Qt),st(q,k,null),e(k,jt),e(k,Jt),e(k,Kt),e(k,Ct),e(k,zt),e(k,Tt),e(k,Gt),e(k,Xt),e(k,Zt),e(k,At),e(k,xt),e(J,te),st(F,J,null),r(t,Ot,o),r(t,K,o),r(t,Mt,o),r(t,L,o),e(L,z);for(let b=0;b<A.length;b+=1)A[b]&&A[b].m(z,null);e(L,le),e(L,G);for(let b=0;b<w.length;b+=1)w[b]&&w[b].m(G,null);H=!0},p(t,[o]){var ce,de;(!H||o&1)&&i!==(i=t[0].name+"")&&Ut(S,i),Y!==(Y=oe(t))&&(P&&P.d(1),P=Y&&Y(t),P&&(P.c(),P.m(C,null)));const b={};o&97&&(b.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${t[6]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(ce=t[0])==null?void 0:ce.name}').authWithPassword(
|
||||
'${t[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`),o&97&&(b.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${t[6]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(de=t[0])==null?void 0:de.name}').authWithPassword(
|
||||
'${t[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`),O.$set(b),(!H||o&1)&<!==(lt=t[0].name+"")&&Ut(ft,lt),t[1]?v||(v=me(),v.c(),v.m($,kt)):v&&(v.d(1),v=null),t[1]&&t[2]?y||(y=he(),y.c(),y.m($,St)):y&&(y.d(1),y=null),t[2]?g||(g=_e(),g.c(),g.m($,wt)):g&&(g.d(1),g=null),o&24&&(at=t[4],A=ue(A,o,se,1,t,at,ee,z,$e,ke,null,be)),o&24&&(X=t[4],Pe(),w=ue(w,o,ne,1,t,X,ae,G,Re,Se,null,pe),Ce())},i(t){if(!H){Z(O.$$.fragment,t),Z(q.$$.fragment,t),Z(F.$$.fragment,t);for(let o=0;o<X.length;o+=1)Z(w[o]);H=!0}},o(t){x(O.$$.fragment,t),x(q.$$.fragment,t),x(F.$$.fragment,t);for(let o=0;o<w.length;o+=1)x(w[o]);H=!1},d(t){t&&c(l),t&&c(p),t&&c(u),P&&P.d(),t&&c(rt),nt(O,t),t&&c(ct),t&&c(N),t&&c(dt),t&&c(M),t&&c(pt),t&&c(V),t&&c(bt),t&&c(U),v&&v.d(),y&&y.d(),g&&g.d(),t&&c(yt),t&&c(j),t&&c(gt),t&&c(E),nt(q),nt(F),t&&c(Ot),t&&c(K),t&&c(Mt),t&&c(L);for(let o=0;o<A.length;o+=1)A[o].d();for(let o=0;o<w.length;o+=1)w[o].d()}}}function Be(n,l,a){let i,S,m,p,{collection:u=new Te}=l,_=200,R=[];const C=T=>a(3,_=T.code);return n.$$set=T=>{"collection"in T&&a(0,u=T.collection)},n.$$.update=()=>{var T,B;n.$$.dirty&1&&a(2,S=(T=u==null?void 0:u.options)==null?void 0:T.allowEmailAuth),n.$$.dirty&1&&a(1,m=(B=u==null?void 0:u.options)==null?void 0:B.allowUsernameAuth),n.$$.dirty&6&&a(5,p=m&&S?"YOUR_USERNAME_OR_EMAIL":m?"YOUR_USERNAME":"YOUR_EMAIL"),n.$$.dirty&1&&a(4,R=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:fe.dummyCollectionRecord(u)},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"identity": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}])},a(6,i=fe.getApiExampleUrl(Ae.baseUrl)),[u,m,S,_,R,p,i,C]}class Ye extends ve{constructor(l){super(),ye(this,l,Be,Le,ge,{collection:0})}}export{Ye as default};
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import{S as we,i as ye,s as $e,N as ve,O as ot,e as n,w as p,b as d,c as nt,f as m,g as r,h as e,m as st,x as Dt,P as pe,Q as Pe,k as Re,R as Ce,n as Oe,t as Z,a as x,o as c,d as it,C as fe,p as Ae,r as rt,u as Te}from"./index-44098792.js";import{S as Ue}from"./SdkTabs-f73340a3.js";import{F as Me}from"./FieldsQueryParam-eb4f33e4.js";function he(s,l,a){const i=s.slice();return i[8]=l[a],i}function be(s,l,a){const i=s.slice();return i[8]=l[a],i}function De(s){let l;return{c(){l=p("email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function Ee(s){let l;return{c(){l=p("username")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function We(s){let l;return{c(){l=p("username/email")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function me(s){let l;return{c(){l=n("strong"),l.textContent="username"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function _e(s){let l;return{c(){l=p("or")},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function ke(s){let l;return{c(){l=n("strong"),l.textContent="email"},m(a,i){r(a,l,i)},d(a){a&&c(l)}}}function ge(s,l){let a,i=l[8].code+"",g,b,f,u;function _(){return l[7](l[8])}return{key:s,first:null,c(){a=n("button"),g=p(i),b=d(),m(a,"class","tab-item"),rt(a,"active",l[3]===l[8].code),this.first=a},m(R,C){r(R,a,C),e(a,g),e(a,b),f||(u=Te(a,"click",_),f=!0)},p(R,C){l=R,C&16&&i!==(i=l[8].code+"")&&Dt(g,i),C&24&&rt(a,"active",l[3]===l[8].code)},d(R){R&&c(a),f=!1,u()}}}function Se(s,l){let a,i,g,b;return i=new ve({props:{content:l[8].body}}),{key:s,first:null,c(){a=n("div"),nt(i.$$.fragment),g=d(),m(a,"class","tab-item"),rt(a,"active",l[3]===l[8].code),this.first=a},m(f,u){r(f,a,u),st(i,a,null),e(a,g),b=!0},p(f,u){l=f;const _={};u&16&&(_.content=l[8].body),i.$set(_),(!b||u&24)&&rt(a,"active",l[3]===l[8].code)},i(f){b||(Z(i.$$.fragment,f),b=!0)},o(f){x(i.$$.fragment,f),b=!1},d(f){f&&c(a),it(i)}}}function Le(s){var re,ce;let l,a,i=s[0].name+"",g,b,f,u,_,R,C,O,B,Et,ct,T,dt,N,ut,U,tt,Wt,et,I,Lt,pt,lt=s[0].name+"",ft,Bt,ht,V,bt,M,mt,qt,Q,D,_t,Ft,kt,Ht,$,Yt,gt,St,vt,Nt,wt,yt,j,$t,E,Pt,It,J,W,Rt,Vt,Ct,Qt,k,jt,q,Jt,Kt,zt,Ot,Gt,At,Xt,Zt,xt,Tt,te,ee,F,Ut,K,Mt,L,z,A=[],le=new Map,ae,G,S=[],oe=new Map,H;function ne(t,o){if(t[1]&&t[2])return We;if(t[1])return Ee;if(t[2])return De}let Y=ne(s),P=Y&&Y(s);T=new Ue({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${s[6]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(re=s[0])==null?void 0:re.name}').authWithPassword(
|
||||
'${s[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${s[6]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(ce=s[0])==null?void 0:ce.name}').authWithPassword(
|
||||
'${s[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`}});let v=s[1]&&me(),w=s[1]&&s[2]&&_e(),y=s[2]&&ke();q=new ve({props:{content:"?expand=relField1,relField2.subRelField"}}),F=new Me({});let at=ot(s[4]);const se=t=>t[8].code;for(let t=0;t<at.length;t+=1){let o=be(s,at,t),h=se(o);le.set(h,A[t]=ge(h,o))}let X=ot(s[4]);const ie=t=>t[8].code;for(let t=0;t<X.length;t+=1){let o=he(s,X,t),h=ie(o);oe.set(h,S[t]=Se(h,o))}return{c(){l=n("h3"),a=p("Auth with password ("),g=p(i),b=p(")"),f=d(),u=n("div"),_=n("p"),R=p(`Returns new auth token and account data by a combination of
|
||||
`),C=n("strong"),P&&P.c(),O=p(`
|
||||
and `),B=n("strong"),B.textContent="password",Et=p("."),ct=d(),nt(T.$$.fragment),dt=d(),N=n("h6"),N.textContent="API details",ut=d(),U=n("div"),tt=n("strong"),tt.textContent="POST",Wt=d(),et=n("div"),I=n("p"),Lt=p("/api/collections/"),pt=n("strong"),ft=p(lt),Bt=p("/auth-with-password"),ht=d(),V=n("div"),V.textContent="Body Parameters",bt=d(),M=n("table"),mt=n("thead"),mt.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',qt=d(),Q=n("tbody"),D=n("tr"),_t=n("td"),_t.innerHTML='<div class="inline-flex"><span class="label label-success">Required</span> <span>identity</span></div>',Ft=d(),kt=n("td"),kt.innerHTML='<span class="label">String</span>',Ht=d(),$=n("td"),Yt=p(`The
|
||||
`),v&&v.c(),gt=d(),w&&w.c(),St=d(),y&&y.c(),vt=p(`
|
||||
of the record to authenticate.`),Nt=d(),wt=n("tr"),wt.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>The auth record password.</td>',yt=d(),j=n("div"),j.textContent="Query parameters",$t=d(),E=n("table"),Pt=n("thead"),Pt.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',It=d(),J=n("tbody"),W=n("tr"),Rt=n("td"),Rt.textContent="expand",Vt=d(),Ct=n("td"),Ct.innerHTML='<span class="label">String</span>',Qt=d(),k=n("td"),jt=p(`Auto expand record relations. Ex.:
|
||||
`),nt(q.$$.fragment),Jt=p(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Kt=n("br"),zt=p(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),Ot=n("code"),Ot.textContent="expand",Gt=p(" property (eg. "),At=n("code"),At.textContent='"expand": {"relField1": {...}, ...}',Xt=p(`).
|
||||
`),Zt=n("br"),xt=p(`
|
||||
Only the relations to which the request user has permissions to `),Tt=n("strong"),Tt.textContent="view",te=p(" will be expanded."),ee=d(),nt(F.$$.fragment),Ut=d(),K=n("div"),K.textContent="Responses",Mt=d(),L=n("div"),z=n("div");for(let t=0;t<A.length;t+=1)A[t].c();ae=d(),G=n("div");for(let t=0;t<S.length;t+=1)S[t].c();m(l,"class","m-b-sm"),m(u,"class","content txt-lg m-b-sm"),m(N,"class","m-b-xs"),m(tt,"class","label label-primary"),m(et,"class","content"),m(U,"class","alert alert-success"),m(V,"class","section-title"),m(M,"class","table-compact table-border m-b-base"),m(j,"class","section-title"),m(E,"class","table-compact table-border m-b-base"),m(K,"class","section-title"),m(z,"class","tabs-header compact combined left"),m(G,"class","tabs-content"),m(L,"class","tabs")},m(t,o){r(t,l,o),e(l,a),e(l,g),e(l,b),r(t,f,o),r(t,u,o),e(u,_),e(_,R),e(_,C),P&&P.m(C,null),e(_,O),e(_,B),e(_,Et),r(t,ct,o),st(T,t,o),r(t,dt,o),r(t,N,o),r(t,ut,o),r(t,U,o),e(U,tt),e(U,Wt),e(U,et),e(et,I),e(I,Lt),e(I,pt),e(pt,ft),e(I,Bt),r(t,ht,o),r(t,V,o),r(t,bt,o),r(t,M,o),e(M,mt),e(M,qt),e(M,Q),e(Q,D),e(D,_t),e(D,Ft),e(D,kt),e(D,Ht),e(D,$),e($,Yt),v&&v.m($,null),e($,gt),w&&w.m($,null),e($,St),y&&y.m($,null),e($,vt),e(Q,Nt),e(Q,wt),r(t,yt,o),r(t,j,o),r(t,$t,o),r(t,E,o),e(E,Pt),e(E,It),e(E,J),e(J,W),e(W,Rt),e(W,Vt),e(W,Ct),e(W,Qt),e(W,k),e(k,jt),st(q,k,null),e(k,Jt),e(k,Kt),e(k,zt),e(k,Ot),e(k,Gt),e(k,At),e(k,Xt),e(k,Zt),e(k,xt),e(k,Tt),e(k,te),e(J,ee),st(F,J,null),r(t,Ut,o),r(t,K,o),r(t,Mt,o),r(t,L,o),e(L,z);for(let h=0;h<A.length;h+=1)A[h]&&A[h].m(z,null);e(L,ae),e(L,G);for(let h=0;h<S.length;h+=1)S[h]&&S[h].m(G,null);H=!0},p(t,[o]){var de,ue;(!H||o&1)&&i!==(i=t[0].name+"")&&Dt(g,i),Y!==(Y=ne(t))&&(P&&P.d(1),P=Y&&Y(t),P&&(P.c(),P.m(C,null)));const h={};o&97&&(h.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${t[6]}');
|
||||
|
||||
...
|
||||
|
||||
const authData = await pb.collection('${(de=t[0])==null?void 0:de.name}').authWithPassword(
|
||||
'${t[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
console.log(pb.authStore.isValid);
|
||||
console.log(pb.authStore.token);
|
||||
console.log(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`),o&97&&(h.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${t[6]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(ue=t[0])==null?void 0:ue.name}').authWithPassword(
|
||||
'${t[5]}',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
|
||||
// after the above you can also access the auth data from the authStore
|
||||
print(pb.authStore.isValid);
|
||||
print(pb.authStore.token);
|
||||
print(pb.authStore.model.id);
|
||||
|
||||
// "logout" the last authenticated account
|
||||
pb.authStore.clear();
|
||||
`),T.$set(h),(!H||o&1)&<!==(lt=t[0].name+"")&&Dt(ft,lt),t[1]?v||(v=me(),v.c(),v.m($,gt)):v&&(v.d(1),v=null),t[1]&&t[2]?w||(w=_e(),w.c(),w.m($,St)):w&&(w.d(1),w=null),t[2]?y||(y=ke(),y.c(),y.m($,vt)):y&&(y.d(1),y=null),o&24&&(at=ot(t[4]),A=pe(A,o,se,1,t,at,le,z,Pe,ge,null,be)),o&24&&(X=ot(t[4]),Re(),S=pe(S,o,ie,1,t,X,oe,G,Ce,Se,null,he),Oe())},i(t){if(!H){Z(T.$$.fragment,t),Z(q.$$.fragment,t),Z(F.$$.fragment,t);for(let o=0;o<X.length;o+=1)Z(S[o]);H=!0}},o(t){x(T.$$.fragment,t),x(q.$$.fragment,t),x(F.$$.fragment,t);for(let o=0;o<S.length;o+=1)x(S[o]);H=!1},d(t){t&&(c(l),c(f),c(u),c(ct),c(dt),c(N),c(ut),c(U),c(ht),c(V),c(bt),c(M),c(yt),c(j),c($t),c(E),c(Ut),c(K),c(Mt),c(L)),P&&P.d(),it(T,t),v&&v.d(),w&&w.d(),y&&y.d(),it(q),it(F);for(let o=0;o<A.length;o+=1)A[o].d();for(let o=0;o<S.length;o+=1)S[o].d()}}}function Be(s,l,a){let i,g,b,f,{collection:u}=l,_=200,R=[];const C=O=>a(3,_=O.code);return s.$$set=O=>{"collection"in O&&a(0,u=O.collection)},s.$$.update=()=>{var O,B;s.$$.dirty&1&&a(2,g=(O=u==null?void 0:u.options)==null?void 0:O.allowEmailAuth),s.$$.dirty&1&&a(1,b=(B=u==null?void 0:u.options)==null?void 0:B.allowUsernameAuth),s.$$.dirty&6&&a(5,f=b&&g?"YOUR_USERNAME_OR_EMAIL":b?"YOUR_USERNAME":"YOUR_EMAIL"),s.$$.dirty&1&&a(4,R=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:fe.dummyCollectionRecord(u)},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"identity": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}])},a(6,i=fe.getApiExampleUrl(Ae.baseUrl)),[u,b,g,_,R,f,i,C]}class Ye extends we{constructor(l){super(),ye(this,l,Be,Le,$e,{collection:0})}}export{Ye as default};
|
||||
+14
File diff suppressed because one or more lines are too long
-14
File diff suppressed because one or more lines are too long
@@ -0,0 +1,56 @@
|
||||
import{S as $e,i as Pe,s as Se,O as Y,e as r,w as v,b as k,c as ge,f as b,g as d,h as o,m as ve,x as j,P as ue,Q as we,k as Oe,R as Re,n as Te,t as x,a as ee,o as m,d as Ce,C as ye,p as Ee,r as H,u as Be,N as qe}from"./index-44098792.js";import{S as Ae}from"./SdkTabs-f73340a3.js";function be(n,l,s){const a=n.slice();return a[5]=l[s],a}function _e(n,l,s){const a=n.slice();return a[5]=l[s],a}function he(n,l){let s,a=l[5].code+"",_,u,i,p;function f(){return l[4](l[5])}return{key:n,first:null,c(){s=r("button"),_=v(a),u=k(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m(C,$){d(C,s,$),o(s,_),o(s,u),i||(p=Be(s,"click",f),i=!0)},p(C,$){l=C,$&4&&a!==(a=l[5].code+"")&&j(_,a),$&6&&H(s,"active",l[1]===l[5].code)},d(C){C&&m(s),i=!1,p()}}}function ke(n,l){let s,a,_,u;return a=new qe({props:{content:l[5].body}}),{key:n,first:null,c(){s=r("div"),ge(a.$$.fragment),_=k(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m(i,p){d(i,s,p),ve(a,s,null),o(s,_),u=!0},p(i,p){l=i;const f={};p&4&&(f.content=l[5].body),a.$set(f),(!u||p&6)&&H(s,"active",l[1]===l[5].code)},i(i){u||(x(a.$$.fragment,i),u=!0)},o(i){ee(a.$$.fragment,i),u=!1},d(i){i&&m(s),Ce(a)}}}function Ue(n){var de,me;let l,s,a=n[0].name+"",_,u,i,p,f,C,$,D=n[0].name+"",F,te,I,P,L,R,Q,S,N,le,K,T,se,z,M=n[0].name+"",G,ae,J,y,V,E,X,B,Z,w,q,g=[],ne=new Map,oe,A,h=[],ie=new Map,O;P=new Ae({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(de=n[0])==null?void 0:de.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=n[0])==null?void 0:me.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`}});let W=Y(n[2]);const ce=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=_e(n,W,e),c=ce(t);ne.set(c,g[e]=he(c,t))}let U=Y(n[2]);const re=e=>e[5].code;for(let e=0;e<U.length;e+=1){let t=be(n,U,e),c=re(t);ie.set(c,h[e]=ke(c,t))}return{c(){l=r("h3"),s=v("Confirm email change ("),_=v(a),u=v(")"),i=k(),p=r("div"),f=r("p"),C=v("Confirms "),$=r("strong"),F=v(D),te=v(" email change request."),I=k(),ge(P.$$.fragment),L=k(),R=r("h6"),R.textContent="API details",Q=k(),S=r("div"),N=r("strong"),N.textContent="POST",le=k(),K=r("div"),T=r("p"),se=v("/api/collections/"),z=r("strong"),G=v(M),ae=v("/confirm-email-change"),J=k(),y=r("div"),y.textContent="Body Parameters",V=k(),E=r("table"),E.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>',X=k(),B=r("div"),B.textContent="Responses",Z=k(),w=r("div"),q=r("div");for(let e=0;e<g.length;e+=1)g[e].c();oe=k(),A=r("div");for(let e=0;e<h.length;e+=1)h[e].c();b(l,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(R,"class","m-b-xs"),b(N,"class","label label-primary"),b(K,"class","content"),b(S,"class","alert alert-success"),b(y,"class","section-title"),b(E,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact combined left"),b(A,"class","tabs-content"),b(w,"class","tabs")},m(e,t){d(e,l,t),o(l,s),o(l,_),o(l,u),d(e,i,t),d(e,p,t),o(p,f),o(f,C),o(f,$),o($,F),o(f,te),d(e,I,t),ve(P,e,t),d(e,L,t),d(e,R,t),d(e,Q,t),d(e,S,t),o(S,N),o(S,le),o(S,K),o(K,T),o(T,se),o(T,z),o(z,G),o(T,ae),d(e,J,t),d(e,y,t),d(e,V,t),d(e,E,t),d(e,X,t),d(e,B,t),d(e,Z,t),d(e,w,t),o(w,q);for(let c=0;c<g.length;c+=1)g[c]&&g[c].m(q,null);o(w,oe),o(w,A);for(let c=0;c<h.length;c+=1)h[c]&&h[c].m(A,null);O=!0},p(e,[t]){var pe,fe;(!O||t&1)&&a!==(a=e[0].name+"")&&j(_,a),(!O||t&1)&&D!==(D=e[0].name+"")&&j(F,D);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(pe=e[0])==null?void 0:pe.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=e[0])==null?void 0:fe.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),P.$set(c),(!O||t&1)&&M!==(M=e[0].name+"")&&j(G,M),t&6&&(W=Y(e[2]),g=ue(g,t,ce,1,e,W,ne,q,we,he,null,_e)),t&6&&(U=Y(e[2]),Oe(),h=ue(h,t,re,1,e,U,ie,A,Re,ke,null,be),Te())},i(e){if(!O){x(P.$$.fragment,e);for(let t=0;t<U.length;t+=1)x(h[t]);O=!0}},o(e){ee(P.$$.fragment,e);for(let t=0;t<h.length;t+=1)ee(h[t]);O=!1},d(e){e&&(m(l),m(i),m(p),m(I),m(L),m(R),m(Q),m(S),m(J),m(y),m(V),m(E),m(X),m(B),m(Z),m(w)),Ce(P,e);for(let t=0;t<g.length;t+=1)g[t].d();for(let t=0;t<h.length;t+=1)h[t].d()}}}function De(n,l,s){let a,{collection:_}=l,u=204,i=[];const p=f=>s(1,u=f.code);return n.$$set=f=>{"collection"in f&&s(0,_=f.collection)},s(3,a=ye.getApiExampleUrl(Ee.baseUrl)),s(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[_,u,i,a,p]}class Me extends $e{constructor(l){super(),Pe(this,l,De,Ue,Se,{collection:0})}}export{Me as default};
|
||||
@@ -1,66 +0,0 @@
|
||||
import{S as Ce,i as $e,s as we,e as r,w as g,b as h,c as he,f as b,g as f,h as n,m as ve,x as Y,N as pe,P as Pe,k as Se,Q as Oe,n as Te,t as Z,a as x,o as m,d as ge,T as Re,C as Ee,p as ye,r as j,u as Be,M as qe}from"./index-9e84ac8a.js";import{S as Ae}from"./SdkTabs-821a18db.js";function ue(o,l,s){const a=o.slice();return a[5]=l[s],a}function be(o,l,s){const a=o.slice();return a[5]=l[s],a}function _e(o,l){let s,a=l[5].code+"",_,u,i,d;function p(){return l[4](l[5])}return{key:o,first:null,c(){s=r("button"),_=g(a),u=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(C,$){f(C,s,$),n(s,_),n(s,u),i||(d=Be(s,"click",p),i=!0)},p(C,$){l=C,$&4&&a!==(a=l[5].code+"")&&Y(_,a),$&6&&j(s,"active",l[1]===l[5].code)},d(C){C&&m(s),i=!1,d()}}}function ke(o,l){let s,a,_,u;return a=new qe({props:{content:l[5].body}}),{key:o,first:null,c(){s=r("div"),he(a.$$.fragment),_=h(),b(s,"class","tab-item"),j(s,"active",l[1]===l[5].code),this.first=s},m(i,d){f(i,s,d),ve(a,s,null),n(s,_),u=!0},p(i,d){l=i;const p={};d&4&&(p.content=l[5].body),a.$set(p),(!u||d&6)&&j(s,"active",l[1]===l[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&m(s),ge(a)}}}function Ue(o){var re,fe;let l,s,a=o[0].name+"",_,u,i,d,p,C,$,D=o[0].name+"",H,ee,F,w,I,T,L,P,M,te,N,R,le,Q,K=o[0].name+"",z,se,G,E,J,y,V,B,X,S,q,v=[],ae=new Map,oe,A,k=[],ne=new Map,O;w=new Ae({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(re=o[0])==null?void 0:re.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=o[0])==null?void 0:fe.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`}});let W=o[2];const ie=e=>e[5].code;for(let e=0;e<W.length;e+=1){let t=be(o,W,e),c=ie(t);ae.set(c,v[e]=_e(c,t))}let U=o[2];const ce=e=>e[5].code;for(let e=0;e<U.length;e+=1){let t=ue(o,U,e),c=ce(t);ne.set(c,k[e]=ke(c,t))}return{c(){l=r("h3"),s=g("Confirm email change ("),_=g(a),u=g(")"),i=h(),d=r("div"),p=r("p"),C=g("Confirms "),$=r("strong"),H=g(D),ee=g(" email change request."),F=h(),he(w.$$.fragment),I=h(),T=r("h6"),T.textContent="API details",L=h(),P=r("div"),M=r("strong"),M.textContent="POST",te=h(),N=r("div"),R=r("p"),le=g("/api/collections/"),Q=r("strong"),z=g(K),se=g("/confirm-email-change"),G=h(),E=r("div"),E.textContent="Body Parameters",J=h(),y=r("table"),y.innerHTML=`<thead><tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr></thead>
|
||||
<tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<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>`,V=h(),B=r("div"),B.textContent="Responses",X=h(),S=r("div"),q=r("div");for(let e=0;e<v.length;e+=1)v[e].c();oe=h(),A=r("div");for(let e=0;e<k.length;e+=1)k[e].c();b(l,"class","m-b-sm"),b(d,"class","content txt-lg m-b-sm"),b(T,"class","m-b-xs"),b(M,"class","label label-primary"),b(N,"class","content"),b(P,"class","alert alert-success"),b(E,"class","section-title"),b(y,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(q,"class","tabs-header compact left"),b(A,"class","tabs-content"),b(S,"class","tabs")},m(e,t){f(e,l,t),n(l,s),n(l,_),n(l,u),f(e,i,t),f(e,d,t),n(d,p),n(p,C),n(p,$),n($,H),n(p,ee),f(e,F,t),ve(w,e,t),f(e,I,t),f(e,T,t),f(e,L,t),f(e,P,t),n(P,M),n(P,te),n(P,N),n(N,R),n(R,le),n(R,Q),n(Q,z),n(R,se),f(e,G,t),f(e,E,t),f(e,J,t),f(e,y,t),f(e,V,t),f(e,B,t),f(e,X,t),f(e,S,t),n(S,q);for(let c=0;c<v.length;c+=1)v[c]&&v[c].m(q,null);n(S,oe),n(S,A);for(let c=0;c<k.length;c+=1)k[c]&&k[c].m(A,null);O=!0},p(e,[t]){var me,de;(!O||t&1)&&a!==(a=e[0].name+"")&&Y(_,a),(!O||t&1)&&D!==(D=e[0].name+"")&&Y(H,D);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=e[0])==null?void 0:me.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(de=e[0])==null?void 0:de.name}').confirmEmailChange(
|
||||
'TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),w.$set(c),(!O||t&1)&&K!==(K=e[0].name+"")&&Y(z,K),t&6&&(W=e[2],v=pe(v,t,ie,1,e,W,ae,q,Pe,_e,null,be)),t&6&&(U=e[2],Se(),k=pe(k,t,ce,1,e,U,ne,A,Oe,ke,null,ue),Te())},i(e){if(!O){Z(w.$$.fragment,e);for(let t=0;t<U.length;t+=1)Z(k[t]);O=!0}},o(e){x(w.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);O=!1},d(e){e&&m(l),e&&m(i),e&&m(d),e&&m(F),ge(w,e),e&&m(I),e&&m(T),e&&m(L),e&&m(P),e&&m(G),e&&m(E),e&&m(J),e&&m(y),e&&m(V),e&&m(B),e&&m(X),e&&m(S);for(let t=0;t<v.length;t+=1)v[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function De(o,l,s){let a,{collection:_=new Re}=l,u=204,i=[];const d=p=>s(1,u=p.code);return o.$$set=p=>{"collection"in p&&s(0,_=p.collection)},s(3,a=Ee.getApiExampleUrl(ye.baseUrl)),s(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[_,u,i,a,d]}class Ke extends Ce{constructor(l){super(),$e(this,l,De,Ue,we,{collection:0})}}export{Ke as default};
|
||||
@@ -0,0 +1,61 @@
|
||||
import{S as Re,i as Oe,s as Ce,O as K,e as r,w,b as h,c as ge,f as b,g as d,h as n,m as Pe,x as U,P as _e,Q as Ne,k as We,R as $e,n as Ee,t as ee,a as te,o as p,d as Se,C as ye,p as Ae,r as j,u as Te,N as De}from"./index-44098792.js";import{S as qe}from"./SdkTabs-f73340a3.js";function ke(o,s,l){const a=o.slice();return a[5]=s[l],a}function he(o,s,l){const a=o.slice();return a[5]=s[l],a}function ve(o,s){let l,a=s[5].code+"",_,u,i,f;function m(){return s[4](s[5])}return{key:o,first:null,c(){l=r("button"),_=w(a),u=h(),b(l,"class","tab-item"),j(l,"active",s[1]===s[5].code),this.first=l},m(g,P){d(g,l,P),n(l,_),n(l,u),i||(f=Te(l,"click",m),i=!0)},p(g,P){s=g,P&4&&a!==(a=s[5].code+"")&&U(_,a),P&6&&j(l,"active",s[1]===s[5].code)},d(g){g&&p(l),i=!1,f()}}}function we(o,s){let l,a,_,u;return a=new De({props:{content:s[5].body}}),{key:o,first:null,c(){l=r("div"),ge(a.$$.fragment),_=h(),b(l,"class","tab-item"),j(l,"active",s[1]===s[5].code),this.first=l},m(i,f){d(i,l,f),Pe(a,l,null),n(l,_),u=!0},p(i,f){s=i;const m={};f&4&&(m.content=s[5].body),a.$set(m),(!u||f&6)&&j(l,"active",s[1]===s[5].code)},i(i){u||(ee(a.$$.fragment,i),u=!0)},o(i){te(a.$$.fragment,i),u=!1},d(i){i&&p(l),Se(a)}}}function Be(o){var fe,me;let s,l,a=o[0].name+"",_,u,i,f,m,g,P,q=o[0].name+"",H,se,le,L,Q,S,z,N,G,R,B,ae,M,W,ne,J,F=o[0].name+"",V,oe,X,$,Y,E,Z,y,x,O,A,v=[],ie=new Map,ce,T,k=[],re=new Map,C;S=new qe({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=o[0])==null?void 0:fe.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=o[0])==null?void 0:me.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`}});let I=K(o[2]);const de=e=>e[5].code;for(let e=0;e<I.length;e+=1){let t=he(o,I,e),c=de(t);ie.set(c,v[e]=ve(c,t))}let D=K(o[2]);const pe=e=>e[5].code;for(let e=0;e<D.length;e+=1){let t=ke(o,D,e),c=pe(t);re.set(c,k[e]=we(c,t))}return{c(){s=r("h3"),l=w("Confirm password reset ("),_=w(a),u=w(")"),i=h(),f=r("div"),m=r("p"),g=w("Confirms "),P=r("strong"),H=w(q),se=w(" password reset request and sets a new password."),le=h(),L=r("p"),L.textContent=`After this request all previously issued tokens for the specific record will be automatically
|
||||
invalidated.`,Q=h(),ge(S.$$.fragment),z=h(),N=r("h6"),N.textContent="API details",G=h(),R=r("div"),B=r("strong"),B.textContent="POST",ae=h(),M=r("div"),W=r("p"),ne=w("/api/collections/"),J=r("strong"),V=w(F),oe=w("/confirm-password-reset"),X=h(),$=r("div"),$.textContent="Body Parameters",Y=h(),E=r("table"),E.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>',Z=h(),y=r("div"),y.textContent="Responses",x=h(),O=r("div"),A=r("div");for(let e=0;e<v.length;e+=1)v[e].c();ce=h(),T=r("div");for(let e=0;e<k.length;e+=1)k[e].c();b(s,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(N,"class","m-b-xs"),b(B,"class","label label-primary"),b(M,"class","content"),b(R,"class","alert alert-success"),b($,"class","section-title"),b(E,"class","table-compact table-border m-b-base"),b(y,"class","section-title"),b(A,"class","tabs-header compact combined left"),b(T,"class","tabs-content"),b(O,"class","tabs")},m(e,t){d(e,s,t),n(s,l),n(s,_),n(s,u),d(e,i,t),d(e,f,t),n(f,m),n(m,g),n(m,P),n(P,H),n(m,se),n(f,le),n(f,L),d(e,Q,t),Pe(S,e,t),d(e,z,t),d(e,N,t),d(e,G,t),d(e,R,t),n(R,B),n(R,ae),n(R,M),n(M,W),n(W,ne),n(W,J),n(J,V),n(W,oe),d(e,X,t),d(e,$,t),d(e,Y,t),d(e,E,t),d(e,Z,t),d(e,y,t),d(e,x,t),d(e,O,t),n(O,A);for(let c=0;c<v.length;c+=1)v[c]&&v[c].m(A,null);n(O,ce),n(O,T);for(let c=0;c<k.length;c+=1)k[c]&&k[c].m(T,null);C=!0},p(e,[t]){var ue,be;(!C||t&1)&&a!==(a=e[0].name+"")&&U(_,a),(!C||t&1)&&q!==(q=e[0].name+"")&&U(H,q);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(ue=e[0])==null?void 0:ue.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(be=e[0])==null?void 0:be.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`),S.$set(c),(!C||t&1)&&F!==(F=e[0].name+"")&&U(V,F),t&6&&(I=K(e[2]),v=_e(v,t,de,1,e,I,ie,A,Ne,ve,null,he)),t&6&&(D=K(e[2]),We(),k=_e(k,t,pe,1,e,D,re,T,$e,we,null,ke),Ee())},i(e){if(!C){ee(S.$$.fragment,e);for(let t=0;t<D.length;t+=1)ee(k[t]);C=!0}},o(e){te(S.$$.fragment,e);for(let t=0;t<k.length;t+=1)te(k[t]);C=!1},d(e){e&&(p(s),p(i),p(f),p(Q),p(z),p(N),p(G),p(R),p(X),p($),p(Y),p(E),p(Z),p(y),p(x),p(O)),Se(S,e);for(let t=0;t<v.length;t+=1)v[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Me(o,s,l){let a,{collection:_}=s,u=204,i=[];const f=m=>l(1,u=m.code);return o.$$set=m=>{"collection"in m&&l(0,_=m.collection)},l(3,a=ye.getApiExampleUrl(Ae.baseUrl)),l(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[_,u,i,a,f]}class Ke extends Re{constructor(s){super(),Oe(this,s,Me,Be,Ce,{collection:0})}}export{Ke as default};
|
||||
@@ -1,74 +0,0 @@
|
||||
import{S as Se,i as he,s as Re,e as r,w as P,b as v,c as ve,f as b,g as d,h as n,m as we,x as K,N as me,P as Ne,k as Oe,Q as Ce,n as We,t as Z,a as x,o as f,d as Pe,T as $e,C as Ee,p as Te,r as U,u as ge,M as Ae}from"./index-9e84ac8a.js";import{S as De}from"./SdkTabs-821a18db.js";function ue(o,s,l){const a=o.slice();return a[5]=s[l],a}function be(o,s,l){const a=o.slice();return a[5]=s[l],a}function _e(o,s){let l,a=s[5].code+"",_,u,i,p;function m(){return s[4](s[5])}return{key:o,first:null,c(){l=r("button"),_=P(a),u=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(S,h){d(S,l,h),n(l,_),n(l,u),i||(p=ge(l,"click",m),i=!0)},p(S,h){s=S,h&4&&a!==(a=s[5].code+"")&&K(_,a),h&6&&U(l,"active",s[1]===s[5].code)},d(S){S&&f(l),i=!1,p()}}}function ke(o,s){let l,a,_,u;return a=new Ae({props:{content:s[5].body}}),{key:o,first:null,c(){l=r("div"),ve(a.$$.fragment),_=v(),b(l,"class","tab-item"),U(l,"active",s[1]===s[5].code),this.first=l},m(i,p){d(i,l,p),we(a,l,null),n(l,_),u=!0},p(i,p){s=i;const m={};p&4&&(m.content=s[5].body),a.$set(m),(!u||p&6)&&U(l,"active",s[1]===s[5].code)},i(i){u||(Z(a.$$.fragment,i),u=!0)},o(i){x(a.$$.fragment,i),u=!1},d(i){i&&f(l),Pe(a)}}}function ye(o){var re,de;let s,l,a=o[0].name+"",_,u,i,p,m,S,h,M=o[0].name+"",j,ee,H,R,L,W,Q,N,q,te,B,$,se,z,F=o[0].name+"",G,le,J,E,V,T,X,g,Y,O,A,w=[],ae=new Map,oe,D,k=[],ne=new Map,C;R=new De({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(re=o[0])==null?void 0:re.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(de=o[0])==null?void 0:de.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`}});let I=o[2];const ie=e=>e[5].code;for(let e=0;e<I.length;e+=1){let t=be(o,I,e),c=ie(t);ae.set(c,w[e]=_e(c,t))}let y=o[2];const ce=e=>e[5].code;for(let e=0;e<y.length;e+=1){let t=ue(o,y,e),c=ce(t);ne.set(c,k[e]=ke(c,t))}return{c(){s=r("h3"),l=P("Confirm password reset ("),_=P(a),u=P(")"),i=v(),p=r("div"),m=r("p"),S=P("Confirms "),h=r("strong"),j=P(M),ee=P(" password reset request and sets a new password."),H=v(),ve(R.$$.fragment),L=v(),W=r("h6"),W.textContent="API details",Q=v(),N=r("div"),q=r("strong"),q.textContent="POST",te=v(),B=r("div"),$=r("p"),se=P("/api/collections/"),z=r("strong"),G=P(F),le=P("/confirm-password-reset"),J=v(),E=r("div"),E.textContent="Body Parameters",V=v(),T=r("table"),T.innerHTML=`<thead><tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr></thead>
|
||||
<tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<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>`,X=v(),g=r("div"),g.textContent="Responses",Y=v(),O=r("div"),A=r("div");for(let e=0;e<w.length;e+=1)w[e].c();oe=v(),D=r("div");for(let e=0;e<k.length;e+=1)k[e].c();b(s,"class","m-b-sm"),b(p,"class","content txt-lg m-b-sm"),b(W,"class","m-b-xs"),b(q,"class","label label-primary"),b(B,"class","content"),b(N,"class","alert alert-success"),b(E,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(g,"class","section-title"),b(A,"class","tabs-header compact left"),b(D,"class","tabs-content"),b(O,"class","tabs")},m(e,t){d(e,s,t),n(s,l),n(s,_),n(s,u),d(e,i,t),d(e,p,t),n(p,m),n(m,S),n(m,h),n(h,j),n(m,ee),d(e,H,t),we(R,e,t),d(e,L,t),d(e,W,t),d(e,Q,t),d(e,N,t),n(N,q),n(N,te),n(N,B),n(B,$),n($,se),n($,z),n(z,G),n($,le),d(e,J,t),d(e,E,t),d(e,V,t),d(e,T,t),d(e,X,t),d(e,g,t),d(e,Y,t),d(e,O,t),n(O,A);for(let c=0;c<w.length;c+=1)w[c]&&w[c].m(A,null);n(O,oe),n(O,D);for(let c=0;c<k.length;c+=1)k[c]&&k[c].m(D,null);C=!0},p(e,[t]){var fe,pe;(!C||t&1)&&a!==(a=e[0].name+"")&&K(_,a),(!C||t&1)&&M!==(M=e[0].name+"")&&K(j,M);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=e[0])==null?void 0:fe.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(pe=e[0])==null?void 0:pe.name}').confirmPasswordReset(
|
||||
'TOKEN',
|
||||
'NEW_PASSWORD',
|
||||
'NEW_PASSWORD_CONFIRM',
|
||||
);
|
||||
`),R.$set(c),(!C||t&1)&&F!==(F=e[0].name+"")&&K(G,F),t&6&&(I=e[2],w=me(w,t,ie,1,e,I,ae,A,Ne,_e,null,be)),t&6&&(y=e[2],Oe(),k=me(k,t,ce,1,e,y,ne,D,Ce,ke,null,ue),We())},i(e){if(!C){Z(R.$$.fragment,e);for(let t=0;t<y.length;t+=1)Z(k[t]);C=!0}},o(e){x(R.$$.fragment,e);for(let t=0;t<k.length;t+=1)x(k[t]);C=!1},d(e){e&&f(s),e&&f(i),e&&f(p),e&&f(H),Pe(R,e),e&&f(L),e&&f(W),e&&f(Q),e&&f(N),e&&f(J),e&&f(E),e&&f(V),e&&f(T),e&&f(X),e&&f(g),e&&f(Y),e&&f(O);for(let t=0;t<w.length;t+=1)w[t].d();for(let t=0;t<k.length;t+=1)k[t].d()}}}function Me(o,s,l){let a,{collection:_=new $e}=s,u=204,i=[];const p=m=>l(1,u=m.code);return o.$$set=m=>{"collection"in m&&l(0,_=m.collection)},l(3,a=Ee.getApiExampleUrl(Te.baseUrl)),l(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[_,u,i,a,p]}class Fe extends Se{constructor(s){super(),he(this,s,Me,ye,Re,{collection:0})}}export{Fe as default};
|
||||
@@ -0,0 +1,56 @@
|
||||
import{S as Se,i as Te,s as Be,O as D,e as r,w as g,b as k,c as ye,f as h,g as f,h as n,m as Ce,x as H,P as ke,Q as Re,k as qe,R as Oe,n as Ee,t as x,a as ee,o as d,d as Pe,C as Ne,p as Ve,r as F,u as Ke,N as Me}from"./index-44098792.js";import{S as Ae}from"./SdkTabs-f73340a3.js";function ve(o,l,s){const a=o.slice();return a[5]=l[s],a}function ge(o,l,s){const a=o.slice();return a[5]=l[s],a}function we(o,l){let s,a=l[5].code+"",b,m,i,p;function u(){return l[4](l[5])}return{key:o,first:null,c(){s=r("button"),b=g(a),m=k(),h(s,"class","tab-item"),F(s,"active",l[1]===l[5].code),this.first=s},m(w,$){f(w,s,$),n(s,b),n(s,m),i||(p=Ke(s,"click",u),i=!0)},p(w,$){l=w,$&4&&a!==(a=l[5].code+"")&&H(b,a),$&6&&F(s,"active",l[1]===l[5].code)},d(w){w&&d(s),i=!1,p()}}}function $e(o,l){let s,a,b,m;return a=new Me({props:{content:l[5].body}}),{key:o,first:null,c(){s=r("div"),ye(a.$$.fragment),b=k(),h(s,"class","tab-item"),F(s,"active",l[1]===l[5].code),this.first=s},m(i,p){f(i,s,p),Ce(a,s,null),n(s,b),m=!0},p(i,p){l=i;const u={};p&4&&(u.content=l[5].body),a.$set(u),(!m||p&6)&&F(s,"active",l[1]===l[5].code)},i(i){m||(x(a.$$.fragment,i),m=!0)},o(i){ee(a.$$.fragment,i),m=!1},d(i){i&&d(s),Pe(a)}}}function Ue(o){var fe,de,pe,ue;let l,s,a=o[0].name+"",b,m,i,p,u,w,$,K=o[0].name+"",I,te,L,y,Q,T,z,C,M,le,A,B,se,G,U=o[0].name+"",J,ae,W,R,X,q,Y,O,Z,P,E,v=[],oe=new Map,ne,N,_=[],ie=new Map,S;y=new Ae({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=o[0])==null?void 0:fe.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(de=o[0])==null?void 0:de.name}').authRefresh();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(pe=o[0])==null?void 0:pe.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(ue=o[0])==null?void 0:ue.name}').authRefresh();
|
||||
`}});let j=D(o[2]);const ce=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=ge(o,j,e),c=ce(t);oe.set(c,v[e]=we(c,t))}let V=D(o[2]);const re=e=>e[5].code;for(let e=0;e<V.length;e+=1){let t=ve(o,V,e),c=re(t);ie.set(c,_[e]=$e(c,t))}return{c(){l=r("h3"),s=g("Confirm verification ("),b=g(a),m=g(")"),i=k(),p=r("div"),u=r("p"),w=g("Confirms "),$=r("strong"),I=g(K),te=g(" account verification request."),L=k(),ye(y.$$.fragment),Q=k(),T=r("h6"),T.textContent="API details",z=k(),C=r("div"),M=r("strong"),M.textContent="POST",le=k(),A=r("div"),B=r("p"),se=g("/api/collections/"),G=r("strong"),J=g(U),ae=g("/confirm-verification"),W=k(),R=r("div"),R.textContent="Body Parameters",X=k(),q=r("table"),q.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>',Y=k(),O=r("div"),O.textContent="Responses",Z=k(),P=r("div"),E=r("div");for(let e=0;e<v.length;e+=1)v[e].c();ne=k(),N=r("div");for(let e=0;e<_.length;e+=1)_[e].c();h(l,"class","m-b-sm"),h(p,"class","content txt-lg m-b-sm"),h(T,"class","m-b-xs"),h(M,"class","label label-primary"),h(A,"class","content"),h(C,"class","alert alert-success"),h(R,"class","section-title"),h(q,"class","table-compact table-border m-b-base"),h(O,"class","section-title"),h(E,"class","tabs-header compact combined left"),h(N,"class","tabs-content"),h(P,"class","tabs")},m(e,t){f(e,l,t),n(l,s),n(l,b),n(l,m),f(e,i,t),f(e,p,t),n(p,u),n(u,w),n(u,$),n($,I),n(u,te),f(e,L,t),Ce(y,e,t),f(e,Q,t),f(e,T,t),f(e,z,t),f(e,C,t),n(C,M),n(C,le),n(C,A),n(A,B),n(B,se),n(B,G),n(G,J),n(B,ae),f(e,W,t),f(e,R,t),f(e,X,t),f(e,q,t),f(e,Y,t),f(e,O,t),f(e,Z,t),f(e,P,t),n(P,E);for(let c=0;c<v.length;c+=1)v[c]&&v[c].m(E,null);n(P,ne),n(P,N);for(let c=0;c<_.length;c+=1)_[c]&&_[c].m(N,null);S=!0},p(e,[t]){var me,he,be,_e;(!S||t&1)&&a!==(a=e[0].name+"")&&H(b,a),(!S||t&1)&&K!==(K=e[0].name+"")&&H(I,K);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=e[0])==null?void 0:me.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(he=e[0])==null?void 0:he.name}').authRefresh();
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(be=e[0])==null?void 0:be.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(_e=e[0])==null?void 0:_e.name}').authRefresh();
|
||||
`),y.$set(c),(!S||t&1)&&U!==(U=e[0].name+"")&&H(J,U),t&6&&(j=D(e[2]),v=ke(v,t,ce,1,e,j,oe,E,Re,we,null,ge)),t&6&&(V=D(e[2]),qe(),_=ke(_,t,re,1,e,V,ie,N,Oe,$e,null,ve),Ee())},i(e){if(!S){x(y.$$.fragment,e);for(let t=0;t<V.length;t+=1)x(_[t]);S=!0}},o(e){ee(y.$$.fragment,e);for(let t=0;t<_.length;t+=1)ee(_[t]);S=!1},d(e){e&&(d(l),d(i),d(p),d(L),d(Q),d(T),d(z),d(C),d(W),d(R),d(X),d(q),d(Y),d(O),d(Z),d(P)),Pe(y,e);for(let t=0;t<v.length;t+=1)v[t].d();for(let t=0;t<_.length;t+=1)_[t].d()}}}function je(o,l,s){let a,{collection:b}=l,m=204,i=[];const p=u=>s(1,m=u.code);return o.$$set=u=>{"collection"in u&&s(0,b=u.collection)},s(3,a=Ne.getApiExampleUrl(Ve.baseUrl)),s(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[b,m,i,a,p]}class Fe extends Se{constructor(l){super(),Te(this,l,je,Ue,Be,{collection:0})}}export{Fe as default};
|
||||
@@ -1,62 +0,0 @@
|
||||
import{S as Pe,i as Te,s as Se,e as r,w,b as k,c as ge,f as b,g as f,h as i,m as ye,x as D,N as _e,P as Be,k as qe,Q as Re,n as Ee,t as Z,a as x,o as p,d as Ce,T as Me,C as Ne,p as Oe,r as H,u as Ve,M as Ke}from"./index-9e84ac8a.js";import{S as Ae}from"./SdkTabs-821a18db.js";function ke(a,l,s){const o=a.slice();return o[5]=l[s],o}function ve(a,l,s){const o=a.slice();return o[5]=l[s],o}function we(a,l){let s,o=l[5].code+"",h,d,n,u;function m(){return l[4](l[5])}return{key:a,first:null,c(){s=r("button"),h=w(o),d=k(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m($,g){f($,s,g),i(s,h),i(s,d),n||(u=Ve(s,"click",m),n=!0)},p($,g){l=$,g&4&&o!==(o=l[5].code+"")&&D(h,o),g&6&&H(s,"active",l[1]===l[5].code)},d($){$&&p(s),n=!1,u()}}}function $e(a,l){let s,o,h,d;return o=new Ke({props:{content:l[5].body}}),{key:a,first:null,c(){s=r("div"),ge(o.$$.fragment),h=k(),b(s,"class","tab-item"),H(s,"active",l[1]===l[5].code),this.first=s},m(n,u){f(n,s,u),ye(o,s,null),i(s,h),d=!0},p(n,u){l=n;const m={};u&4&&(m.content=l[5].body),o.$set(m),(!d||u&6)&&H(s,"active",l[1]===l[5].code)},i(n){d||(Z(o.$$.fragment,n),d=!0)},o(n){x(o.$$.fragment,n),d=!1},d(n){n&&p(s),Ce(o)}}}function Ue(a){var re,fe,pe,ue;let l,s,o=a[0].name+"",h,d,n,u,m,$,g,V=a[0].name+"",F,ee,I,y,L,S,Q,C,K,te,A,B,le,z,U=a[0].name+"",G,se,J,q,W,R,X,E,Y,P,M,v=[],oe=new Map,ae,N,_=[],ie=new Map,T;y=new Ae({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(re=a[0])==null?void 0:re.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(fe=a[0])==null?void 0:fe.name}').authRefresh();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${a[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(pe=a[0])==null?void 0:pe.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(ue=a[0])==null?void 0:ue.name}').authRefresh();
|
||||
`}});let j=a[2];const ne=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=ve(a,j,e),c=ne(t);oe.set(c,v[e]=we(c,t))}let O=a[2];const ce=e=>e[5].code;for(let e=0;e<O.length;e+=1){let t=ke(a,O,e),c=ce(t);ie.set(c,_[e]=$e(c,t))}return{c(){l=r("h3"),s=w("Confirm verification ("),h=w(o),d=w(")"),n=k(),u=r("div"),m=r("p"),$=w("Confirms "),g=r("strong"),F=w(V),ee=w(" account verification request."),I=k(),ge(y.$$.fragment),L=k(),S=r("h6"),S.textContent="API details",Q=k(),C=r("div"),K=r("strong"),K.textContent="POST",te=k(),A=r("div"),B=r("p"),le=w("/api/collections/"),z=r("strong"),G=w(U),se=w("/confirm-verification"),J=k(),q=r("div"),q.textContent="Body Parameters",W=k(),R=r("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>`,X=k(),E=r("div"),E.textContent="Responses",Y=k(),P=r("div"),M=r("div");for(let e=0;e<v.length;e+=1)v[e].c();ae=k(),N=r("div");for(let e=0;e<_.length;e+=1)_[e].c();b(l,"class","m-b-sm"),b(u,"class","content txt-lg m-b-sm"),b(S,"class","m-b-xs"),b(K,"class","label label-primary"),b(A,"class","content"),b(C,"class","alert alert-success"),b(q,"class","section-title"),b(R,"class","table-compact table-border m-b-base"),b(E,"class","section-title"),b(M,"class","tabs-header compact left"),b(N,"class","tabs-content"),b(P,"class","tabs")},m(e,t){f(e,l,t),i(l,s),i(l,h),i(l,d),f(e,n,t),f(e,u,t),i(u,m),i(m,$),i(m,g),i(g,F),i(m,ee),f(e,I,t),ye(y,e,t),f(e,L,t),f(e,S,t),f(e,Q,t),f(e,C,t),i(C,K),i(C,te),i(C,A),i(A,B),i(B,le),i(B,z),i(z,G),i(B,se),f(e,J,t),f(e,q,t),f(e,W,t),f(e,R,t),f(e,X,t),f(e,E,t),f(e,Y,t),f(e,P,t),i(P,M);for(let c=0;c<v.length;c+=1)v[c]&&v[c].m(M,null);i(P,ae),i(P,N);for(let c=0;c<_.length;c+=1)_[c]&&_[c].m(N,null);T=!0},p(e,[t]){var me,de,be,he;(!T||t&1)&&o!==(o=e[0].name+"")&&D(h,o),(!T||t&1)&&V!==(V=e[0].name+"")&&D(F,V);const c={};t&9&&(c.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=e[0])==null?void 0:me.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(de=e[0])==null?void 0:de.name}').authRefresh();
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(be=e[0])==null?void 0:be.name}').confirmVerification('TOKEN');
|
||||
|
||||
// optionally refresh the previous authStore state with the latest record changes
|
||||
await pb.collection('${(he=e[0])==null?void 0:he.name}').authRefresh();
|
||||
`),y.$set(c),(!T||t&1)&&U!==(U=e[0].name+"")&&D(G,U),t&6&&(j=e[2],v=_e(v,t,ne,1,e,j,oe,M,Be,we,null,ve)),t&6&&(O=e[2],qe(),_=_e(_,t,ce,1,e,O,ie,N,Re,$e,null,ke),Ee())},i(e){if(!T){Z(y.$$.fragment,e);for(let t=0;t<O.length;t+=1)Z(_[t]);T=!0}},o(e){x(y.$$.fragment,e);for(let t=0;t<_.length;t+=1)x(_[t]);T=!1},d(e){e&&p(l),e&&p(n),e&&p(u),e&&p(I),Ce(y,e),e&&p(L),e&&p(S),e&&p(Q),e&&p(C),e&&p(J),e&&p(q),e&&p(W),e&&p(R),e&&p(X),e&&p(E),e&&p(Y),e&&p(P);for(let t=0;t<v.length;t+=1)v[t].d();for(let t=0;t<_.length;t+=1)_[t].d()}}}function je(a,l,s){let o,{collection:h=new Me}=l,d=204,n=[];const u=m=>s(1,d=m.code);return a.$$set=m=>{"collection"in m&&s(0,h=m.collection)},s(3,o=Ne.getApiExampleUrl(Oe.baseUrl)),s(2,n=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[h,d,n,o,u]}class Fe extends Pe{constructor(l){super(),Te(this,l,je,Ue,Se,{collection:0})}}export{Fe as default};
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
import{S as qt,i as Ot,s as Mt,C as Q,O as ne,N as Tt,e as i,w as _,b as u,c as _e,f as v,g as r,h as n,m as he,x,P as Be,Q as ht,k as Ht,R as Lt,n as Pt,t as ue,a as fe,o as d,d as ke,p as Ft,r as ye,u as Rt,y as ae}from"./index-44098792.js";import{S as At}from"./SdkTabs-f73340a3.js";import{F as Bt}from"./FieldsQueryParam-eb4f33e4.js";function kt(o,e,t){const a=o.slice();return a[8]=e[t],a}function yt(o,e,t){const a=o.slice();return a[8]=e[t],a}function vt(o,e,t){const a=o.slice();return a[13]=e[t],a}function gt(o){let e;return{c(){e=i("p"),e.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",v(e,"class","txt-hint txt-sm txt-right")},m(t,a){r(t,e,a)},d(t){t&&d(e)}}}function wt(o){let e,t,a,f,m,c,p,y,S,T,w,H,D,E,P,I,j,B,$,N,q,g,b;function O(h,C){var ee,K;return(K=(ee=h[0])==null?void 0:ee.options)!=null&&K.requireEmail?Dt:jt}let z=O(o),F=z(o);return{c(){e=i("tr"),e.innerHTML='<td colspan="3" class="txt-hint">Auth fields</td>',t=u(),a=i("tr"),a.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>username</span></div></td> <td><span class="label">String</span></td> <td>The username of the auth record.
|
||||
<br/>
|
||||
If not set, it will be auto generated.</td>`,f=u(),m=i("tr"),c=i("td"),p=i("div"),F.c(),y=u(),S=i("span"),S.textContent="email",T=u(),w=i("td"),w.innerHTML='<span class="label">String</span>',H=u(),D=i("td"),D.textContent="Auth record email address.",E=u(),P=i("tr"),P.innerHTML='<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>emailVisibility</span></div></td> <td><span class="label">Boolean</span></td> <td>Whether to show/hide the auth record email when fetching the record data.</td>',I=u(),j=i("tr"),j.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>Auth record password.</td>',B=u(),$=i("tr"),$.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>passwordConfirm</span></div></td> <td><span class="label">String</span></td> <td>Auth record password confirmation.</td>',N=u(),q=i("tr"),q.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 admins or auth records with "Manage" access.</td>`,g=u(),b=i("tr"),b.innerHTML='<td colspan="3" class="txt-hint">Schema fields</td>',v(p,"class","inline-flex")},m(h,C){r(h,e,C),r(h,t,C),r(h,a,C),r(h,f,C),r(h,m,C),n(m,c),n(c,p),F.m(p,null),n(p,y),n(p,S),n(m,T),n(m,w),n(m,H),n(m,D),r(h,E,C),r(h,P,C),r(h,I,C),r(h,j,C),r(h,B,C),r(h,$,C),r(h,N,C),r(h,q,C),r(h,g,C),r(h,b,C)},p(h,C){z!==(z=O(h))&&(F.d(1),F=z(h),F&&(F.c(),F.m(p,y)))},d(h){h&&(d(e),d(t),d(a),d(f),d(m),d(E),d(P),d(I),d(j),d(B),d($),d(N),d(q),d(g),d(b)),F.d()}}}function jt(o){let e;return{c(){e=i("span"),e.textContent="Optional",v(e,"class","label label-warning")},m(t,a){r(t,e,a)},d(t){t&&d(e)}}}function Dt(o){let e;return{c(){e=i("span"),e.textContent="Required",v(e,"class","label label-success")},m(t,a){r(t,e,a)},d(t){t&&d(e)}}}function Nt(o){let e;return{c(){e=i("span"),e.textContent="Optional",v(e,"class","label label-warning")},m(t,a){r(t,e,a)},d(t){t&&d(e)}}}function Vt(o){let e;return{c(){e=i("span"),e.textContent="Required",v(e,"class","label label-success")},m(t,a){r(t,e,a)},d(t){t&&d(e)}}}function Jt(o){var m;let e,t=((m=o[13].options)==null?void 0:m.maxSelect)===1?"id":"ids",a,f;return{c(){e=_("Relation record "),a=_(t),f=_(".")},m(c,p){r(c,e,p),r(c,a,p),r(c,f,p)},p(c,p){var y;p&1&&t!==(t=((y=c[13].options)==null?void 0:y.maxSelect)===1?"id":"ids")&&x(a,t)},d(c){c&&(d(e),d(a),d(f))}}}function Et(o){let e,t,a,f,m;return{c(){e=_("File object."),t=i("br"),a=_(`
|
||||
Set to `),f=i("code"),f.textContent="null",m=_(" to delete already uploaded file(s).")},m(c,p){r(c,e,p),r(c,t,p),r(c,a,p),r(c,f,p),r(c,m,p)},p:ae,d(c){c&&(d(e),d(t),d(a),d(f),d(m))}}}function It(o){let e;return{c(){e=_("URL address.")},m(t,a){r(t,e,a)},p:ae,d(t){t&&d(e)}}}function Ut(o){let e;return{c(){e=_("Email address.")},m(t,a){r(t,e,a)},p:ae,d(t){t&&d(e)}}}function Qt(o){let e;return{c(){e=_("JSON array or object.")},m(t,a){r(t,e,a)},p:ae,d(t){t&&d(e)}}}function zt(o){let e;return{c(){e=_("Number value.")},m(t,a){r(t,e,a)},p:ae,d(t){t&&d(e)}}}function Kt(o){let e;return{c(){e=_("Plain text value.")},m(t,a){r(t,e,a)},p:ae,d(t){t&&d(e)}}}function $t(o,e){let t,a,f,m,c,p=e[13].name+"",y,S,T,w,H=Q.getFieldValueType(e[13])+"",D,E,P,I;function j(b,O){return b[13].required?Vt:Nt}let B=j(e),$=B(e);function N(b,O){if(b[13].type==="text")return Kt;if(b[13].type==="number")return zt;if(b[13].type==="json")return Qt;if(b[13].type==="email")return Ut;if(b[13].type==="url")return It;if(b[13].type==="file")return Et;if(b[13].type==="relation")return Jt}let q=N(e),g=q&&q(e);return{key:o,first:null,c(){t=i("tr"),a=i("td"),f=i("div"),$.c(),m=u(),c=i("span"),y=_(p),S=u(),T=i("td"),w=i("span"),D=_(H),E=u(),P=i("td"),g&&g.c(),I=u(),v(f,"class","inline-flex"),v(w,"class","label"),this.first=t},m(b,O){r(b,t,O),n(t,a),n(a,f),$.m(f,null),n(f,m),n(f,c),n(c,y),n(t,S),n(t,T),n(T,w),n(w,D),n(t,E),n(t,P),g&&g.m(P,null),n(t,I)},p(b,O){e=b,B!==(B=j(e))&&($.d(1),$=B(e),$&&($.c(),$.m(f,m))),O&1&&p!==(p=e[13].name+"")&&x(y,p),O&1&&H!==(H=Q.getFieldValueType(e[13])+"")&&x(D,H),q===(q=N(e))&&g?g.p(e,O):(g&&g.d(1),g=q&&q(e),g&&(g.c(),g.m(P,null)))},d(b){b&&d(t),$.d(),g&&g.d()}}}function Ct(o,e){let t,a=e[8].code+"",f,m,c,p;function y(){return e[7](e[8])}return{key:o,first:null,c(){t=i("button"),f=_(a),m=u(),v(t,"class","tab-item"),ye(t,"active",e[2]===e[8].code),this.first=t},m(S,T){r(S,t,T),n(t,f),n(t,m),c||(p=Rt(t,"click",y),c=!0)},p(S,T){e=S,T&8&&a!==(a=e[8].code+"")&&x(f,a),T&12&&ye(t,"active",e[2]===e[8].code)},d(S){S&&d(t),c=!1,p()}}}function St(o,e){let t,a,f,m;return a=new Tt({props:{content:e[8].body}}),{key:o,first:null,c(){t=i("div"),_e(a.$$.fragment),f=u(),v(t,"class","tab-item"),ye(t,"active",e[2]===e[8].code),this.first=t},m(c,p){r(c,t,p),he(a,t,null),n(t,f),m=!0},p(c,p){e=c;const y={};p&8&&(y.content=e[8].body),a.$set(y),(!m||p&12)&&ye(t,"active",e[2]===e[8].code)},i(c){m||(ue(a.$$.fragment,c),m=!0)},o(c){fe(a.$$.fragment,c),m=!1},d(c){c&&d(t),ke(a)}}}function Wt(o){var ot,rt,dt,ct,pt;let e,t,a=o[0].name+"",f,m,c,p,y,S,T,w=o[0].name+"",H,D,E,P,I,j,B,$,N,q,g,b,O,z,F,h,C,ee,K=o[0].name+"",ve,je,De,ge,se,we,W,$e,Ne,U,Ce,Ve,Se,V=[],Je=new Map,Te,ie,qe,Y,Oe,Ee,oe,G,Me,Ie,He,Ue,M,Qe,te,ze,Ke,We,Le,Ye,Pe,Ge,Xe,Ze,Fe,xe,et,le,Re,re,Ae,X,de,J=[],tt=new Map,lt,ce,R=[],nt=new Map,Z;$=new At({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(Object.assign({},o[4],Q.dummyCollectionSchemaData(o[0])),null,4)};
|
||||
|
||||
const record = await pb.collection('${(ot=o[0])==null?void 0:ot.name}').create(data);
|
||||
`+(o[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(rt=o[0])==null?void 0:rt.name}').requestVerification('test@example.com');
|
||||
`:""),dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},o[4],Q.dummyCollectionSchemaData(o[0])),null,2)};
|
||||
|
||||
final record = await pb.collection('${(dt=o[0])==null?void 0:dt.name}').create(body: body);
|
||||
`+(o[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ct=o[0])==null?void 0:ct.name}').requestVerification('test@example.com');
|
||||
`:"")}});let A=o[6]&>(),L=o[1]&&wt(o),me=ne((pt=o[0])==null?void 0:pt.schema);const at=l=>l[13].name;for(let l=0;l<me.length;l+=1){let s=vt(o,me,l),k=at(s);Je.set(k,V[l]=$t(k,s))}te=new Tt({props:{content:"?expand=relField1,relField2.subRelField"}}),le=new Bt({});let be=ne(o[3]);const st=l=>l[8].code;for(let l=0;l<be.length;l+=1){let s=yt(o,be,l),k=st(s);tt.set(k,J[l]=Ct(k,s))}let pe=ne(o[3]);const it=l=>l[8].code;for(let l=0;l<pe.length;l+=1){let s=kt(o,pe,l),k=it(s);nt.set(k,R[l]=St(k,s))}return{c(){e=i("h3"),t=_("Create ("),f=_(a),m=_(")"),c=u(),p=i("div"),y=i("p"),S=_("Create a new "),T=i("strong"),H=_(w),D=_(" record."),E=u(),P=i("p"),P.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,I=u(),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>.`,B=u(),_e($.$$.fragment),N=u(),q=i("h6"),q.textContent="API details",g=u(),b=i("div"),O=i("strong"),O.textContent="POST",z=u(),F=i("div"),h=i("p"),C=_("/api/collections/"),ee=i("strong"),ve=_(K),je=_("/records"),De=u(),A&&A.c(),ge=u(),se=i("div"),se.textContent="Body Parameters",we=u(),W=i("table"),$e=i("thead"),$e.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',Ne=u(),U=i("tbody"),Ce=i("tr"),Ce.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>id</span></div></td> <td><span class="label">String</span></td> <td><strong>15 characters string</strong> to store as record ID.
|
||||
<br/>
|
||||
If not set, it will be auto generated.</td>`,Ve=u(),L&&L.c(),Se=u();for(let l=0;l<V.length;l+=1)V[l].c();Te=u(),ie=i("div"),ie.textContent="Query parameters",qe=u(),Y=i("table"),Oe=i("thead"),Oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',Ee=u(),oe=i("tbody"),G=i("tr"),Me=i("td"),Me.textContent="expand",Ie=u(),He=i("td"),He.innerHTML='<span class="label">String</span>',Ue=u(),M=i("td"),Qe=_(`Auto expand relations when returning the created record. Ex.:
|
||||
`),_e(te.$$.fragment),ze=_(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Ke=i("br"),We=_(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),Le=i("code"),Le.textContent="expand",Ye=_(" property (eg. "),Pe=i("code"),Pe.textContent='"expand": {"relField1": {...}, ...}',Ge=_(`).
|
||||
`),Xe=i("br"),Ze=_(`
|
||||
Only the relations to which the request user has permissions to `),Fe=i("strong"),Fe.textContent="view",xe=_(" will be expanded."),et=u(),_e(le.$$.fragment),Re=u(),re=i("div"),re.textContent="Responses",Ae=u(),X=i("div"),de=i("div");for(let l=0;l<J.length;l+=1)J[l].c();lt=u(),ce=i("div");for(let l=0;l<R.length;l+=1)R[l].c();v(e,"class","m-b-sm"),v(p,"class","content txt-lg m-b-sm"),v(q,"class","m-b-xs"),v(O,"class","label label-primary"),v(F,"class","content"),v(b,"class","alert alert-success"),v(se,"class","section-title"),v(W,"class","table-compact table-border m-b-base"),v(ie,"class","section-title"),v(Y,"class","table-compact table-border m-b-base"),v(re,"class","section-title"),v(de,"class","tabs-header compact combined left"),v(ce,"class","tabs-content"),v(X,"class","tabs")},m(l,s){r(l,e,s),n(e,t),n(e,f),n(e,m),r(l,c,s),r(l,p,s),n(p,y),n(y,S),n(y,T),n(T,H),n(y,D),n(p,E),n(p,P),n(p,I),n(p,j),r(l,B,s),he($,l,s),r(l,N,s),r(l,q,s),r(l,g,s),r(l,b,s),n(b,O),n(b,z),n(b,F),n(F,h),n(h,C),n(h,ee),n(ee,ve),n(h,je),n(b,De),A&&A.m(b,null),r(l,ge,s),r(l,se,s),r(l,we,s),r(l,W,s),n(W,$e),n(W,Ne),n(W,U),n(U,Ce),n(U,Ve),L&&L.m(U,null),n(U,Se);for(let k=0;k<V.length;k+=1)V[k]&&V[k].m(U,null);r(l,Te,s),r(l,ie,s),r(l,qe,s),r(l,Y,s),n(Y,Oe),n(Y,Ee),n(Y,oe),n(oe,G),n(G,Me),n(G,Ie),n(G,He),n(G,Ue),n(G,M),n(M,Qe),he(te,M,null),n(M,ze),n(M,Ke),n(M,We),n(M,Le),n(M,Ye),n(M,Pe),n(M,Ge),n(M,Xe),n(M,Ze),n(M,Fe),n(M,xe),n(oe,et),he(le,oe,null),r(l,Re,s),r(l,re,s),r(l,Ae,s),r(l,X,s),n(X,de);for(let k=0;k<J.length;k+=1)J[k]&&J[k].m(de,null);n(X,lt),n(X,ce);for(let k=0;k<R.length;k+=1)R[k]&&R[k].m(ce,null);Z=!0},p(l,[s]){var ut,ft,mt,bt,_t;(!Z||s&1)&&a!==(a=l[0].name+"")&&x(f,a),(!Z||s&1)&&w!==(w=l[0].name+"")&&x(H,w);const k={};s&51&&(k.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${l[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(Object.assign({},l[4],Q.dummyCollectionSchemaData(l[0])),null,4)};
|
||||
|
||||
const record = await pb.collection('${(ut=l[0])==null?void 0:ut.name}').create(data);
|
||||
`+(l[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ft=l[0])==null?void 0:ft.name}').requestVerification('test@example.com');
|
||||
`:"")),s&51&&(k.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${l[5]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},l[4],Q.dummyCollectionSchemaData(l[0])),null,2)};
|
||||
|
||||
final record = await pb.collection('${(mt=l[0])==null?void 0:mt.name}').create(body: body);
|
||||
`+(l[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(bt=l[0])==null?void 0:bt.name}').requestVerification('test@example.com');
|
||||
`:"")),$.$set(k),(!Z||s&1)&&K!==(K=l[0].name+"")&&x(ve,K),l[6]?A||(A=gt(),A.c(),A.m(b,null)):A&&(A.d(1),A=null),l[1]?L?L.p(l,s):(L=wt(l),L.c(),L.m(U,Se)):L&&(L.d(1),L=null),s&1&&(me=ne((_t=l[0])==null?void 0:_t.schema),V=Be(V,s,at,1,l,me,Je,U,ht,$t,null,vt)),s&12&&(be=ne(l[3]),J=Be(J,s,st,1,l,be,tt,de,ht,Ct,null,yt)),s&12&&(pe=ne(l[3]),Ht(),R=Be(R,s,it,1,l,pe,nt,ce,Lt,St,null,kt),Pt())},i(l){if(!Z){ue($.$$.fragment,l),ue(te.$$.fragment,l),ue(le.$$.fragment,l);for(let s=0;s<pe.length;s+=1)ue(R[s]);Z=!0}},o(l){fe($.$$.fragment,l),fe(te.$$.fragment,l),fe(le.$$.fragment,l);for(let s=0;s<R.length;s+=1)fe(R[s]);Z=!1},d(l){l&&(d(e),d(c),d(p),d(B),d(N),d(q),d(g),d(b),d(ge),d(se),d(we),d(W),d(Te),d(ie),d(qe),d(Y),d(Re),d(re),d(Ae),d(X)),ke($,l),A&&A.d(),L&&L.d();for(let s=0;s<V.length;s+=1)V[s].d();ke(te),ke(le);for(let s=0;s<J.length;s+=1)J[s].d();for(let s=0;s<R.length;s+=1)R[s].d()}}}function Yt(o,e,t){let a,f,m,{collection:c}=e,p=200,y=[],S={};const T=w=>t(2,p=w.code);return o.$$set=w=>{"collection"in w&&t(0,c=w.collection)},o.$$.update=()=>{var w,H;o.$$.dirty&1&&t(1,a=c.type==="auth"),o.$$.dirty&1&&t(6,f=(c==null?void 0:c.createRule)===null),o.$$.dirty&1&&t(3,y=[{code:200,body:JSON.stringify(Q.dummyCollectionRecord(c),null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to create record.",
|
||||
"data": {
|
||||
"${(H=(w=c==null?void 0:c.schema)==null?void 0:w[0])==null?void 0:H.name}": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "You are not allowed to perform this request.",
|
||||
"data": {}
|
||||
}
|
||||
`}]),o.$$.dirty&2&&(a?t(4,S={username:"test_username",email:"test@example.com",emailVisibility:!0,password:"12345678",passwordConfirm:"12345678"}):t(4,S={}))},t(5,m=Q.getApiExampleUrl(Ft.baseUrl)),[c,a,p,y,S,m,f,T]}class xt extends qt{constructor(e){super(),Ot(this,e,Yt,Wt,Mt,{collection:0})}}export{xt as default};
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
import{S as Pt,i as gt,s as Ft,C as Q,M as Lt,e as a,w as k,b as m,c as be,f as h,g as r,h as n,m as _e,x,N as Be,P as $t,k as Bt,Q as Rt,n as jt,t as fe,a as pe,o as d,d as ke,T as Dt,p as Nt,r as ye,u as Vt,y as ne}from"./index-9e84ac8a.js";import{S as Jt}from"./SdkTabs-821a18db.js";import{F as Et}from"./FieldsQueryParam-664da4c8.js";function Ct(o,e,l){const s=o.slice();return s[7]=e[l],s}function St(o,e,l){const s=o.slice();return s[7]=e[l],s}function Tt(o,e,l){const s=o.slice();return s[12]=e[l],s}function qt(o){let e;return{c(){e=a("p"),e.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",h(e,"class","txt-hint txt-sm txt-right")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Mt(o){let e,l,s,b,p,c,f,v,T,w,M,R,D,E,L,I,j,B,C,N,q,$,_;function O(u,S){var ee,K;return(K=(ee=u[0])==null?void 0:ee.options)!=null&&K.requireEmail?Ut:It}let z=O(o),P=z(o);return{c(){e=a("tr"),e.innerHTML='<td colspan="3" class="txt-hint">Auth fields</td>',l=m(),s=a("tr"),s.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<span>username</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>The username of the auth record.
|
||||
<br/>
|
||||
If not set, it will be auto generated.</td>`,b=m(),p=a("tr"),c=a("td"),f=a("div"),P.c(),v=m(),T=a("span"),T.textContent="email",w=m(),M=a("td"),M.innerHTML='<span class="label">String</span>',R=m(),D=a("td"),D.textContent="Auth record email address.",E=m(),L=a("tr"),L.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<span>emailVisibility</span></div></td>
|
||||
<td><span class="label">Boolean</span></td>
|
||||
<td>Whether to show/hide the auth record email when fetching the record data.</td>`,I=m(),j=a("tr"),j.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>password</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>Auth record password.</td>`,B=m(),C=a("tr"),C.innerHTML=`<td><div class="inline-flex"><span class="label label-success">Required</span>
|
||||
<span>passwordConfirm</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>Auth record password confirmation.</td>`,N=m(),q=a("tr"),q.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<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 admins or auth records with "Manage" access.</td>`,$=m(),_=a("tr"),_.innerHTML='<td colspan="3" class="txt-hint">Schema fields</td>',h(f,"class","inline-flex")},m(u,S){r(u,e,S),r(u,l,S),r(u,s,S),r(u,b,S),r(u,p,S),n(p,c),n(c,f),P.m(f,null),n(f,v),n(f,T),n(p,w),n(p,M),n(p,R),n(p,D),r(u,E,S),r(u,L,S),r(u,I,S),r(u,j,S),r(u,B,S),r(u,C,S),r(u,N,S),r(u,q,S),r(u,$,S),r(u,_,S)},p(u,S){z!==(z=O(u))&&(P.d(1),P=z(u),P&&(P.c(),P.m(f,v)))},d(u){u&&d(e),u&&d(l),u&&d(s),u&&d(b),u&&d(p),P.d(),u&&d(E),u&&d(L),u&&d(I),u&&d(j),u&&d(B),u&&d(C),u&&d(N),u&&d(q),u&&d($),u&&d(_)}}}function It(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Ut(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Qt(o){let e;return{c(){e=a("span"),e.textContent="Optional",h(e,"class","label label-warning")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function zt(o){let e;return{c(){e=a("span"),e.textContent="Required",h(e,"class","label label-success")},m(l,s){r(l,e,s)},d(l){l&&d(e)}}}function Kt(o){var p;let e,l=((p=o[12].options)==null?void 0:p.maxSelect)===1?"id":"ids",s,b;return{c(){e=k("Relation record "),s=k(l),b=k(".")},m(c,f){r(c,e,f),r(c,s,f),r(c,b,f)},p(c,f){var v;f&1&&l!==(l=((v=c[12].options)==null?void 0:v.maxSelect)===1?"id":"ids")&&x(s,l)},d(c){c&&d(e),c&&d(s),c&&d(b)}}}function Wt(o){let e,l,s,b,p;return{c(){e=k("File object."),l=a("br"),s=k(`
|
||||
Set to `),b=a("code"),b.textContent="null",p=k(" to delete already uploaded file(s).")},m(c,f){r(c,e,f),r(c,l,f),r(c,s,f),r(c,b,f),r(c,p,f)},p:ne,d(c){c&&d(e),c&&d(l),c&&d(s),c&&d(b),c&&d(p)}}}function Yt(o){let e;return{c(){e=k("URL address.")},m(l,s){r(l,e,s)},p:ne,d(l){l&&d(e)}}}function Gt(o){let e;return{c(){e=k("Email address.")},m(l,s){r(l,e,s)},p:ne,d(l){l&&d(e)}}}function Xt(o){let e;return{c(){e=k("JSON array or object.")},m(l,s){r(l,e,s)},p:ne,d(l){l&&d(e)}}}function Zt(o){let e;return{c(){e=k("Number value.")},m(l,s){r(l,e,s)},p:ne,d(l){l&&d(e)}}}function xt(o){let e;return{c(){e=k("Plain text value.")},m(l,s){r(l,e,s)},p:ne,d(l){l&&d(e)}}}function Ot(o,e){let l,s,b,p,c,f=e[12].name+"",v,T,w,M,R=Q.getFieldValueType(e[12])+"",D,E,L,I;function j(_,O){return _[12].required?zt:Qt}let B=j(e),C=B(e);function N(_,O){if(_[12].type==="text")return xt;if(_[12].type==="number")return Zt;if(_[12].type==="json")return Xt;if(_[12].type==="email")return Gt;if(_[12].type==="url")return Yt;if(_[12].type==="file")return Wt;if(_[12].type==="relation")return Kt}let q=N(e),$=q&&q(e);return{key:o,first:null,c(){l=a("tr"),s=a("td"),b=a("div"),C.c(),p=m(),c=a("span"),v=k(f),T=m(),w=a("td"),M=a("span"),D=k(R),E=m(),L=a("td"),$&&$.c(),I=m(),h(b,"class","inline-flex"),h(M,"class","label"),this.first=l},m(_,O){r(_,l,O),n(l,s),n(s,b),C.m(b,null),n(b,p),n(b,c),n(c,v),n(l,T),n(l,w),n(w,M),n(M,D),n(l,E),n(l,L),$&&$.m(L,null),n(l,I)},p(_,O){e=_,B!==(B=j(e))&&(C.d(1),C=B(e),C&&(C.c(),C.m(b,p))),O&1&&f!==(f=e[12].name+"")&&x(v,f),O&1&&R!==(R=Q.getFieldValueType(e[12])+"")&&x(D,R),q===(q=N(e))&&$?$.p(e,O):($&&$.d(1),$=q&&q(e),$&&($.c(),$.m(L,null)))},d(_){_&&d(l),C.d(),$&&$.d()}}}function At(o,e){let l,s=e[7].code+"",b,p,c,f;function v(){return e[6](e[7])}return{key:o,first:null,c(){l=a("button"),b=k(s),p=m(),h(l,"class","tab-item"),ye(l,"active",e[1]===e[7].code),this.first=l},m(T,w){r(T,l,w),n(l,b),n(l,p),c||(f=Vt(l,"click",v),c=!0)},p(T,w){e=T,w&4&&s!==(s=e[7].code+"")&&x(b,s),w&6&&ye(l,"active",e[1]===e[7].code)},d(T){T&&d(l),c=!1,f()}}}function Ht(o,e){let l,s,b,p;return s=new Lt({props:{content:e[7].body}}),{key:o,first:null,c(){l=a("div"),be(s.$$.fragment),b=m(),h(l,"class","tab-item"),ye(l,"active",e[1]===e[7].code),this.first=l},m(c,f){r(c,l,f),_e(s,l,null),n(l,b),p=!0},p(c,f){e=c;const v={};f&4&&(v.content=e[7].body),s.$set(v),(!p||f&6)&&ye(l,"active",e[1]===e[7].code)},i(c){p||(fe(s.$$.fragment,c),p=!0)},o(c){pe(s.$$.fragment,c),p=!1},d(c){c&&d(l),ke(s)}}}function el(o){var at,ot,rt,dt,ct,ft,pt,ut;let e,l,s=o[0].name+"",b,p,c,f,v,T,w,M=o[0].name+"",R,D,E,L,I,j,B,C,N,q,$,_,O,z,P,u,S,ee,K=o[0].name+"",ve,Re,je,he,se,we,W,$e,De,U,Ce,Ne,Se,V=[],Ve=new Map,Te,ie,qe,Y,Me,Je,ae,G,Oe,Ee,Ae,Ie,A,Ue,te,Qe,ze,Ke,He,We,Le,Ye,Ge,Xe,Pe,Ze,xe,le,ge,oe,Fe,X,re,J=[],et=new Map,tt,de,g=[],lt=new Map,Z;C=new Jt({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(Object.assign({},o[3],Q.dummyCollectionSchemaData(o[0])),null,4)};
|
||||
|
||||
const record = await pb.collection('${(at=o[0])==null?void 0:at.name}').create(data);
|
||||
`+((ot=o[0])!=null&&ot.isAuth?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(rt=o[0])==null?void 0:rt.name}').requestVerification('test@example.com');
|
||||
`:""),dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},o[3],Q.dummyCollectionSchemaData(o[0])),null,2)};
|
||||
|
||||
final record = await pb.collection('${(dt=o[0])==null?void 0:dt.name}').create(body: body);
|
||||
`+((ct=o[0])!=null&&ct.isAuth?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ft=o[0])==null?void 0:ft.name}').requestVerification('test@example.com');
|
||||
`:"")}});let F=o[5]&&qt(),H=((pt=o[0])==null?void 0:pt.isAuth)&&Mt(o),ue=(ut=o[0])==null?void 0:ut.schema;const nt=t=>t[12].name;for(let t=0;t<ue.length;t+=1){let i=Tt(o,ue,t),y=nt(i);Ve.set(y,V[t]=Ot(y,i))}te=new Lt({props:{content:"?expand=relField1,relField2.subRelField"}}),le=new Et({});let me=o[2];const st=t=>t[7].code;for(let t=0;t<me.length;t+=1){let i=St(o,me,t),y=st(i);et.set(y,J[t]=At(y,i))}let ce=o[2];const it=t=>t[7].code;for(let t=0;t<ce.length;t+=1){let i=Ct(o,ce,t),y=it(i);lt.set(y,g[t]=Ht(y,i))}return{c(){e=a("h3"),l=k("Create ("),b=k(s),p=k(")"),c=m(),f=a("div"),v=a("p"),T=k("Create a new "),w=a("strong"),R=k(M),D=k(" record."),E=m(),L=a("p"),L.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,I=m(),j=a("p"),j.innerHTML=`File upload is supported only via <code>multipart/form-data</code>.
|
||||
<br/>
|
||||
For more info and examples you could check the detailed
|
||||
<a href="https://pocketbase.io/docs/files-handling/" target="_blank" rel="noopener noreferrer">Files upload and handling docs
|
||||
</a>.`,B=m(),be(C.$$.fragment),N=m(),q=a("h6"),q.textContent="API details",$=m(),_=a("div"),O=a("strong"),O.textContent="POST",z=m(),P=a("div"),u=a("p"),S=k("/api/collections/"),ee=a("strong"),ve=k(K),Re=k("/records"),je=m(),F&&F.c(),he=m(),se=a("div"),se.textContent="Body Parameters",we=m(),W=a("table"),$e=a("thead"),$e.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="50%">Description</th></tr>`,De=m(),U=a("tbody"),Ce=a("tr"),Ce.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span>
|
||||
<span>id</span></div></td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td><strong>15 characters string</strong> to store as record ID.
|
||||
<br/>
|
||||
If not set, it will be auto generated.</td>`,Ne=m(),H&&H.c(),Se=m();for(let t=0;t<V.length;t+=1)V[t].c();Te=m(),ie=a("div"),ie.textContent="Query parameters",qe=m(),Y=a("table"),Me=a("thead"),Me.innerHTML=`<tr><th>Param</th>
|
||||
<th>Type</th>
|
||||
<th width="60%">Description</th></tr>`,Je=m(),ae=a("tbody"),G=a("tr"),Oe=a("td"),Oe.textContent="expand",Ee=m(),Ae=a("td"),Ae.innerHTML='<span class="label">String</span>',Ie=m(),A=a("td"),Ue=k(`Auto expand relations when returning the created record. Ex.:
|
||||
`),be(te.$$.fragment),Qe=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),ze=a("br"),Ke=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),He=a("code"),He.textContent="expand",We=k(" property (eg. "),Le=a("code"),Le.textContent='"expand": {"relField1": {...}, ...}',Ye=k(`).
|
||||
`),Ge=a("br"),Xe=k(`
|
||||
Only the relations to which the request user has permissions to `),Pe=a("strong"),Pe.textContent="view",Ze=k(" will be expanded."),xe=m(),be(le.$$.fragment),ge=m(),oe=a("div"),oe.textContent="Responses",Fe=m(),X=a("div"),re=a("div");for(let t=0;t<J.length;t+=1)J[t].c();tt=m(),de=a("div");for(let t=0;t<g.length;t+=1)g[t].c();h(e,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(q,"class","m-b-xs"),h(O,"class","label label-primary"),h(P,"class","content"),h(_,"class","alert alert-success"),h(se,"class","section-title"),h(W,"class","table-compact table-border m-b-base"),h(ie,"class","section-title"),h(Y,"class","table-compact table-border m-b-base"),h(oe,"class","section-title"),h(re,"class","tabs-header compact left"),h(de,"class","tabs-content"),h(X,"class","tabs")},m(t,i){r(t,e,i),n(e,l),n(e,b),n(e,p),r(t,c,i),r(t,f,i),n(f,v),n(v,T),n(v,w),n(w,R),n(v,D),n(f,E),n(f,L),n(f,I),n(f,j),r(t,B,i),_e(C,t,i),r(t,N,i),r(t,q,i),r(t,$,i),r(t,_,i),n(_,O),n(_,z),n(_,P),n(P,u),n(u,S),n(u,ee),n(ee,ve),n(u,Re),n(_,je),F&&F.m(_,null),r(t,he,i),r(t,se,i),r(t,we,i),r(t,W,i),n(W,$e),n(W,De),n(W,U),n(U,Ce),n(U,Ne),H&&H.m(U,null),n(U,Se);for(let y=0;y<V.length;y+=1)V[y]&&V[y].m(U,null);r(t,Te,i),r(t,ie,i),r(t,qe,i),r(t,Y,i),n(Y,Me),n(Y,Je),n(Y,ae),n(ae,G),n(G,Oe),n(G,Ee),n(G,Ae),n(G,Ie),n(G,A),n(A,Ue),_e(te,A,null),n(A,Qe),n(A,ze),n(A,Ke),n(A,He),n(A,We),n(A,Le),n(A,Ye),n(A,Ge),n(A,Xe),n(A,Pe),n(A,Ze),n(ae,xe),_e(le,ae,null),r(t,ge,i),r(t,oe,i),r(t,Fe,i),r(t,X,i),n(X,re);for(let y=0;y<J.length;y+=1)J[y]&&J[y].m(re,null);n(X,tt),n(X,de);for(let y=0;y<g.length;y+=1)g[y]&&g[y].m(de,null);Z=!0},p(t,[i]){var mt,bt,_t,kt,yt,vt,ht,wt;(!Z||i&1)&&s!==(s=t[0].name+"")&&x(b,s),(!Z||i&1)&&M!==(M=t[0].name+"")&&x(R,M);const y={};i&25&&(y.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${t[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create data
|
||||
const data = ${JSON.stringify(Object.assign({},t[3],Q.dummyCollectionSchemaData(t[0])),null,4)};
|
||||
|
||||
const record = await pb.collection('${(mt=t[0])==null?void 0:mt.name}').create(data);
|
||||
`+((bt=t[0])!=null&&bt.isAuth?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(_t=t[0])==null?void 0:_t.name}').requestVerification('test@example.com');
|
||||
`:"")),i&25&&(y.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${t[4]}');
|
||||
|
||||
...
|
||||
|
||||
// example create body
|
||||
final body = <String, dynamic>${JSON.stringify(Object.assign({},t[3],Q.dummyCollectionSchemaData(t[0])),null,2)};
|
||||
|
||||
final record = await pb.collection('${(kt=t[0])==null?void 0:kt.name}').create(body: body);
|
||||
`+((yt=t[0])!=null&&yt.isAuth?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(vt=t[0])==null?void 0:vt.name}').requestVerification('test@example.com');
|
||||
`:"")),C.$set(y),(!Z||i&1)&&K!==(K=t[0].name+"")&&x(ve,K),t[5]?F||(F=qt(),F.c(),F.m(_,null)):F&&(F.d(1),F=null),(ht=t[0])!=null&&ht.isAuth?H?H.p(t,i):(H=Mt(t),H.c(),H.m(U,Se)):H&&(H.d(1),H=null),i&1&&(ue=(wt=t[0])==null?void 0:wt.schema,V=Be(V,i,nt,1,t,ue,Ve,U,$t,Ot,null,Tt)),i&6&&(me=t[2],J=Be(J,i,st,1,t,me,et,re,$t,At,null,St)),i&6&&(ce=t[2],Bt(),g=Be(g,i,it,1,t,ce,lt,de,Rt,Ht,null,Ct),jt())},i(t){if(!Z){fe(C.$$.fragment,t),fe(te.$$.fragment,t),fe(le.$$.fragment,t);for(let i=0;i<ce.length;i+=1)fe(g[i]);Z=!0}},o(t){pe(C.$$.fragment,t),pe(te.$$.fragment,t),pe(le.$$.fragment,t);for(let i=0;i<g.length;i+=1)pe(g[i]);Z=!1},d(t){t&&d(e),t&&d(c),t&&d(f),t&&d(B),ke(C,t),t&&d(N),t&&d(q),t&&d($),t&&d(_),F&&F.d(),t&&d(he),t&&d(se),t&&d(we),t&&d(W),H&&H.d();for(let i=0;i<V.length;i+=1)V[i].d();t&&d(Te),t&&d(ie),t&&d(qe),t&&d(Y),ke(te),ke(le),t&&d(ge),t&&d(oe),t&&d(Fe),t&&d(X);for(let i=0;i<J.length;i+=1)J[i].d();for(let i=0;i<g.length;i+=1)g[i].d()}}}function tl(o,e,l){let s,b,{collection:p=new Dt}=e,c=200,f=[],v={};const T=w=>l(1,c=w.code);return o.$$set=w=>{"collection"in w&&l(0,p=w.collection)},o.$$.update=()=>{var w,M;o.$$.dirty&1&&l(5,s=(p==null?void 0:p.createRule)===null),o.$$.dirty&1&&l(2,f=[{code:200,body:JSON.stringify(Q.dummyCollectionRecord(p),null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to create record.",
|
||||
"data": {
|
||||
"${(M=(w=p==null?void 0:p.schema)==null?void 0:w[0])==null?void 0:M.name}": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "You are not allowed to perform this request.",
|
||||
"data": {}
|
||||
}
|
||||
`}]),o.$$.dirty&1&&(p.$isAuth?l(3,v={username:"test_username",email:"test@example.com",emailVisibility:!0,password:"12345678",passwordConfirm:"12345678"}):l(3,v={}))},l(4,b=Q.getApiExampleUrl(Nt.baseUrl)),[p,c,f,v,b,s,T]}class il extends Pt{constructor(e){super(),gt(this,e,tl,el,Ft,{collection:0})}}export{il as default};
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import{S as Re,i as Pe,s as Ee,O as j,e as c,w as y,b as k,c as De,f as m,g as p,h as i,m as Ce,x as ee,P as he,Q as Oe,k as Te,R as Be,n as Ie,t as te,a as le,o as u,d as we,C as Ae,p as Me,r as N,u as Se,N as qe}from"./index-44098792.js";import{S as He}from"./SdkTabs-f73340a3.js";function ke(a,l,s){const o=a.slice();return o[6]=l[s],o}function ge(a,l,s){const o=a.slice();return o[6]=l[s],o}function ve(a){let l;return{c(){l=c("p"),l.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,o){p(s,l,o)},d(s){s&&u(l)}}}function ye(a,l){let s,o,h;function d(){return l[5](l[6])}return{key:a,first:null,c(){s=c("button"),s.textContent=`${l[6].code} `,m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(n,r){p(n,s,r),o||(h=Se(s,"click",d),o=!0)},p(n,r){l=n,r&20&&N(s,"active",l[2]===l[6].code)},d(n){n&&u(s),o=!1,h()}}}function $e(a,l){let s,o,h,d;return o=new qe({props:{content:l[6].body}}),{key:a,first:null,c(){s=c("div"),De(o.$$.fragment),h=k(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(n,r){p(n,s,r),Ce(o,s,null),i(s,h),d=!0},p(n,r){l=n,(!d||r&20)&&N(s,"active",l[2]===l[6].code)},i(n){d||(te(o.$$.fragment,n),d=!0)},o(n){le(o.$$.fragment,n),d=!1},d(n){n&&u(s),we(o)}}}function Le(a){var ue,me;let l,s,o=a[0].name+"",h,d,n,r,$,D,z,S=a[0].name+"",F,se,K,C,Q,E,G,g,q,ae,H,P,oe,J,L=a[0].name+"",V,ne,W,ie,X,O,Y,T,Z,B,x,w,I,v=[],ce=new Map,de,A,b=[],re=new Map,R;C=new He({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(ue=a[0])==null?void 0:ue.name}').delete('RECORD_ID');
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${a[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=a[0])==null?void 0:me.name}').delete('RECORD_ID');
|
||||
`}});let _=a[1]&&ve(),U=j(a[4]);const fe=e=>e[6].code;for(let e=0;e<U.length;e+=1){let t=ge(a,U,e),f=fe(t);ce.set(f,v[e]=ye(f,t))}let M=j(a[4]);const pe=e=>e[6].code;for(let e=0;e<M.length;e+=1){let t=ke(a,M,e),f=pe(t);re.set(f,b[e]=$e(f,t))}return{c(){l=c("h3"),s=y("Delete ("),h=y(o),d=y(")"),n=k(),r=c("div"),$=c("p"),D=y("Delete a single "),z=c("strong"),F=y(S),se=y(" record."),K=k(),De(C.$$.fragment),Q=k(),E=c("h6"),E.textContent="API details",G=k(),g=c("div"),q=c("strong"),q.textContent="DELETE",ae=k(),H=c("div"),P=c("p"),oe=y("/api/collections/"),J=c("strong"),V=y(L),ne=y("/records/"),W=c("strong"),W.textContent=":id",ie=k(),_&&_.c(),X=k(),O=c("div"),O.textContent="Path parameters",Y=k(),T=c("table"),T.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 delete.</td></tr></tbody>',Z=k(),B=c("div"),B.textContent="Responses",x=k(),w=c("div"),I=c("div");for(let e=0;e<v.length;e+=1)v[e].c();de=k(),A=c("div");for(let e=0;e<b.length;e+=1)b[e].c();m(l,"class","m-b-sm"),m(r,"class","content txt-lg m-b-sm"),m(E,"class","m-b-xs"),m(q,"class","label label-primary"),m(H,"class","content"),m(g,"class","alert alert-danger"),m(O,"class","section-title"),m(T,"class","table-compact table-border m-b-base"),m(B,"class","section-title"),m(I,"class","tabs-header compact combined left"),m(A,"class","tabs-content"),m(w,"class","tabs")},m(e,t){p(e,l,t),i(l,s),i(l,h),i(l,d),p(e,n,t),p(e,r,t),i(r,$),i($,D),i($,z),i(z,F),i($,se),p(e,K,t),Ce(C,e,t),p(e,Q,t),p(e,E,t),p(e,G,t),p(e,g,t),i(g,q),i(g,ae),i(g,H),i(H,P),i(P,oe),i(P,J),i(J,V),i(P,ne),i(P,W),i(g,ie),_&&_.m(g,null),p(e,X,t),p(e,O,t),p(e,Y,t),p(e,T,t),p(e,Z,t),p(e,B,t),p(e,x,t),p(e,w,t),i(w,I);for(let f=0;f<v.length;f+=1)v[f]&&v[f].m(I,null);i(w,de),i(w,A);for(let f=0;f<b.length;f+=1)b[f]&&b[f].m(A,null);R=!0},p(e,[t]){var be,_e;(!R||t&1)&&o!==(o=e[0].name+"")&&ee(h,o),(!R||t&1)&&S!==(S=e[0].name+"")&&ee(F,S);const f={};t&9&&(f.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(be=e[0])==null?void 0:be.name}').delete('RECORD_ID');
|
||||
`),t&9&&(f.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(_e=e[0])==null?void 0:_e.name}').delete('RECORD_ID');
|
||||
`),C.$set(f),(!R||t&1)&&L!==(L=e[0].name+"")&&ee(V,L),e[1]?_||(_=ve(),_.c(),_.m(g,null)):_&&(_.d(1),_=null),t&20&&(U=j(e[4]),v=he(v,t,fe,1,e,U,ce,I,Oe,ye,null,ge)),t&20&&(M=j(e[4]),Te(),b=he(b,t,pe,1,e,M,re,A,Be,$e,null,ke),Ie())},i(e){if(!R){te(C.$$.fragment,e);for(let t=0;t<M.length;t+=1)te(b[t]);R=!0}},o(e){le(C.$$.fragment,e);for(let t=0;t<b.length;t+=1)le(b[t]);R=!1},d(e){e&&(u(l),u(n),u(r),u(K),u(Q),u(E),u(G),u(g),u(X),u(O),u(Y),u(T),u(Z),u(B),u(x),u(w)),we(C,e),_&&_.d();for(let t=0;t<v.length;t+=1)v[t].d();for(let t=0;t<b.length;t+=1)b[t].d()}}}function Ue(a,l,s){let o,h,{collection:d}=l,n=204,r=[];const $=D=>s(2,n=D.code);return a.$$set=D=>{"collection"in D&&s(0,d=D.collection)},a.$$.update=()=>{a.$$.dirty&1&&s(1,o=(d==null?void 0:d.deleteRule)===null),a.$$.dirty&3&&d!=null&&d.id&&(r.push({code:204,body:`
|
||||
null
|
||||
`}),r.push({code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to delete record. Make sure that the record is not part of a required relation reference.",
|
||||
"data": {}
|
||||
}
|
||||
`}),o&&r.push({code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Only admins can access this action.",
|
||||
"data": {}
|
||||
}
|
||||
`}),r.push({code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},s(3,h=Ae.getApiExampleUrl(Me.baseUrl)),[d,o,n,h,r,$]}class ze extends Re{constructor(l){super(),Pe(this,l,Ue,Le,Ee,{collection:0})}}export{ze as default};
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
import{S as Ce,i as Re,s as Pe,e as c,w as $,b as h,c as $e,f as m,g as f,h as n,m as we,x,N as _e,P as Ee,k as Te,Q as Be,n as Oe,t as ee,a as te,o as u,d as ge,T as Ie,C as Me,p as Ae,r as N,u as Se,M as qe}from"./index-9e84ac8a.js";import{S as He}from"./SdkTabs-821a18db.js";function ke(o,l,s){const a=o.slice();return a[6]=l[s],a}function he(o,l,s){const a=o.slice();return a[6]=l[s],a}function ve(o){let l;return{c(){l=c("p"),l.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,a){f(s,l,a)},d(s){s&&u(l)}}}function ye(o,l){let s,a=l[6].code+"",v,i,r,p;function w(){return l[5](l[6])}return{key:o,first:null,c(){s=c("button"),v=$(a),i=h(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(b,g){f(b,s,g),n(s,v),n(s,i),r||(p=Se(s,"click",w),r=!0)},p(b,g){l=b,g&20&&N(s,"active",l[2]===l[6].code)},d(b){b&&u(s),r=!1,p()}}}function De(o,l){let s,a,v,i;return a=new qe({props:{content:l[6].body}}),{key:o,first:null,c(){s=c("div"),$e(a.$$.fragment),v=h(),m(s,"class","tab-item"),N(s,"active",l[2]===l[6].code),this.first=s},m(r,p){f(r,s,p),we(a,s,null),n(s,v),i=!0},p(r,p){l=r,(!i||p&20)&&N(s,"active",l[2]===l[6].code)},i(r){i||(ee(a.$$.fragment,r),i=!0)},o(r){te(a.$$.fragment,r),i=!1},d(r){r&&u(s),ge(a)}}}function Le(o){var ue,pe;let l,s,a=o[0].name+"",v,i,r,p,w,b,g,q=o[0].name+"",z,le,F,C,K,T,Q,y,H,se,L,E,oe,G,U=o[0].name+"",J,ae,V,ne,W,B,X,O,Y,I,Z,R,M,D=[],ie=new Map,re,A,_=[],ce=new Map,P;C=new He({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(ue=o[0])==null?void 0:ue.name}').delete('RECORD_ID');
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(pe=o[0])==null?void 0:pe.name}').delete('RECORD_ID');
|
||||
`}});let k=o[1]&&ve(),j=o[4];const de=e=>e[6].code;for(let e=0;e<j.length;e+=1){let t=he(o,j,e),d=de(t);ie.set(d,D[e]=ye(d,t))}let S=o[4];const fe=e=>e[6].code;for(let e=0;e<S.length;e+=1){let t=ke(o,S,e),d=fe(t);ce.set(d,_[e]=De(d,t))}return{c(){l=c("h3"),s=$("Delete ("),v=$(a),i=$(")"),r=h(),p=c("div"),w=c("p"),b=$("Delete a single "),g=c("strong"),z=$(q),le=$(" record."),F=h(),$e(C.$$.fragment),K=h(),T=c("h6"),T.textContent="API details",Q=h(),y=c("div"),H=c("strong"),H.textContent="DELETE",se=h(),L=c("div"),E=c("p"),oe=$("/api/collections/"),G=c("strong"),J=$(U),ae=$("/records/"),V=c("strong"),V.textContent=":id",ne=h(),k&&k.c(),W=h(),B=c("div"),B.textContent="Path parameters",X=h(),O=c("table"),O.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 delete.</td></tr></tbody>`,Y=h(),I=c("div"),I.textContent="Responses",Z=h(),R=c("div"),M=c("div");for(let e=0;e<D.length;e+=1)D[e].c();re=h(),A=c("div");for(let e=0;e<_.length;e+=1)_[e].c();m(l,"class","m-b-sm"),m(p,"class","content txt-lg m-b-sm"),m(T,"class","m-b-xs"),m(H,"class","label label-primary"),m(L,"class","content"),m(y,"class","alert alert-danger"),m(B,"class","section-title"),m(O,"class","table-compact table-border m-b-base"),m(I,"class","section-title"),m(M,"class","tabs-header compact left"),m(A,"class","tabs-content"),m(R,"class","tabs")},m(e,t){f(e,l,t),n(l,s),n(l,v),n(l,i),f(e,r,t),f(e,p,t),n(p,w),n(w,b),n(w,g),n(g,z),n(w,le),f(e,F,t),we(C,e,t),f(e,K,t),f(e,T,t),f(e,Q,t),f(e,y,t),n(y,H),n(y,se),n(y,L),n(L,E),n(E,oe),n(E,G),n(G,J),n(E,ae),n(E,V),n(y,ne),k&&k.m(y,null),f(e,W,t),f(e,B,t),f(e,X,t),f(e,O,t),f(e,Y,t),f(e,I,t),f(e,Z,t),f(e,R,t),n(R,M);for(let d=0;d<D.length;d+=1)D[d]&&D[d].m(M,null);n(R,re),n(R,A);for(let d=0;d<_.length;d+=1)_[d]&&_[d].m(A,null);P=!0},p(e,[t]){var me,be;(!P||t&1)&&a!==(a=e[0].name+"")&&x(v,a),(!P||t&1)&&q!==(q=e[0].name+"")&&x(z,q);const d={};t&9&&(d.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=e[0])==null?void 0:me.name}').delete('RECORD_ID');
|
||||
`),t&9&&(d.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(be=e[0])==null?void 0:be.name}').delete('RECORD_ID');
|
||||
`),C.$set(d),(!P||t&1)&&U!==(U=e[0].name+"")&&x(J,U),e[1]?k||(k=ve(),k.c(),k.m(y,null)):k&&(k.d(1),k=null),t&20&&(j=e[4],D=_e(D,t,de,1,e,j,ie,M,Ee,ye,null,he)),t&20&&(S=e[4],Te(),_=_e(_,t,fe,1,e,S,ce,A,Be,De,null,ke),Oe())},i(e){if(!P){ee(C.$$.fragment,e);for(let t=0;t<S.length;t+=1)ee(_[t]);P=!0}},o(e){te(C.$$.fragment,e);for(let t=0;t<_.length;t+=1)te(_[t]);P=!1},d(e){e&&u(l),e&&u(r),e&&u(p),e&&u(F),ge(C,e),e&&u(K),e&&u(T),e&&u(Q),e&&u(y),k&&k.d(),e&&u(W),e&&u(B),e&&u(X),e&&u(O),e&&u(Y),e&&u(I),e&&u(Z),e&&u(R);for(let t=0;t<D.length;t+=1)D[t].d();for(let t=0;t<_.length;t+=1)_[t].d()}}}function Ue(o,l,s){let a,v,{collection:i=new Ie}=l,r=204,p=[];const w=b=>s(2,r=b.code);return o.$$set=b=>{"collection"in b&&s(0,i=b.collection)},o.$$.update=()=>{o.$$.dirty&1&&s(1,a=(i==null?void 0:i.deleteRule)===null),o.$$.dirty&3&&i!=null&&i.id&&(p.push({code:204,body:`
|
||||
null
|
||||
`}),p.push({code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to delete record. Make sure that the record is not part of a required relation reference.",
|
||||
"data": {}
|
||||
}
|
||||
`}),a&&p.push({code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Only admins can access this action.",
|
||||
"data": {}
|
||||
}
|
||||
`}),p.push({code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},s(3,v=Me.getApiExampleUrl(Ae.baseUrl)),[i,a,r,v,p,w]}class ze extends Ce{constructor(l){super(),Re(this,l,Ue,Le,Pe,{collection:0})}}export{ze as default};
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
import{S as d,i as n,s as i,e as l,g as o,y as s,o as p}from"./index-9e84ac8a.js";function c(a){let e;return{c(){e=l("tr"),e.innerHTML=`<td id="query-page">fields</td>
|
||||
<td><span class="label">String</span></td>
|
||||
<td>Comma separated string of the fields to return in the JSON response
|
||||
<em>(by default returns all fields)</em>. For example:
|
||||
<br/>
|
||||
<code>?fields=id,expand.relField.id,expand.relField.created</code></td>`},m(t,r){o(t,e,r)},p:s,i:s,o:s,d(t){t&&p(e)}}}class f extends d{constructor(e){super(),n(this,e,null,c,i,{})}}export{f as F};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import{S as d,i as n,s as i,e as l,g as o,y as s,o as p}from"./index-44098792.js";function c(a){let e;return{c(){e=l("tr"),e.innerHTML=`<td id="query-page">fields</td> <td><span class="label">String</span></td> <td>Comma separated string of the fields to return in the JSON response
|
||||
<em>(by default returns all fields)</em>. For example:
|
||||
<br/> <code>?fields=id,expand.relField.id,expand.relField.created</code></td>`},m(t,r){o(t,e,r)},p:s,i:s,o:s,d(t){t&&p(e)}}}class f extends d{constructor(e){super(),n(this,e,null,c,i,{})}}export{f as F};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+141
@@ -0,0 +1,141 @@
|
||||
import{S as Ze,i as tl,s as el,e,b as s,E as sl,f as a,g as u,u as ll,y as Ue,o as m,w as _,h as t,N as Fe,O as te,c as ee,m as le,x as ke,P as je,Q as nl,k as ol,R as al,n as il,t as Bt,a as Gt,d as se,T as rl,C as ve,p as cl,r as Le}from"./index-44098792.js";import{S as dl}from"./SdkTabs-f73340a3.js";function pl(d){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,h){u(f,n,h),u(f,o,h),u(f,i,h)},d(f){f&&(m(n),m(o),m(i))}}}function fl(d){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,h){u(f,n,h),u(f,o,h),u(f,i,h)},d(f){f&&(m(n),m(o),m(i))}}}function ze(d){let n,o,i,f,h,r,b,C,$,g,p,Z,Ct,Ut,E,jt,M,it,S,tt,ne,G,U,oe,rt,$t,et,kt,ae,ct,dt,lt,N,zt,yt,y,st,vt,Jt,Ft,j,nt,Lt,Kt,At,F,pt,Tt,ie,ft,re,D,Pt,ot,St,O,ut,ce,z,Ot,Qt,Rt,de,q,Vt,J,mt,pe,I,fe,B,ue,P,Et,K,ht,me,bt,he,w,Nt,at,qt,be,Ht,Wt,Q,gt,ge,Mt,_e,_t,xe,V,xt,we,wt,Xt,W,Yt,L,X,R,Dt,Ce,Y,v,It;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`,h=s(),r=e("li"),b=e("code"),b.textContent="OPERATOR",C=_(` - is one of:
|
||||
`),$=e("br"),g=s(),p=e("ul"),Z=e("li"),Ct=e("code"),Ct.textContent="=",Ut=s(),E=e("span"),E.textContent="Equal",jt=s(),M=e("li"),it=e("code"),it.textContent="!=",S=s(),tt=e("span"),tt.textContent="NOT equal",ne=s(),G=e("li"),U=e("code"),U.textContent=">",oe=s(),rt=e("span"),rt.textContent="Greater than",$t=s(),et=e("li"),kt=e("code"),kt.textContent=">=",ae=s(),ct=e("span"),ct.textContent="Greater than or equal",dt=s(),lt=e("li"),N=e("code"),N.textContent="<",zt=s(),yt=e("span"),yt.textContent="Less than",y=s(),st=e("li"),vt=e("code"),vt.textContent="<=",Jt=s(),Ft=e("span"),Ft.textContent="Less than or equal",j=s(),nt=e("li"),Lt=e("code"),Lt.textContent="~",Kt=s(),At=e("span"),At.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,F=s(),pt=e("li"),Tt=e("code"),Tt.textContent="!~",ie=s(),ft=e("span"),ft.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,re=s(),D=e("li"),Pt=e("code"),Pt.textContent="?=",ot=s(),St=e("em"),St.textContent="Any/At least one of",O=s(),ut=e("span"),ut.textContent="Equal",ce=s(),z=e("li"),Ot=e("code"),Ot.textContent="?!=",Qt=s(),Rt=e("em"),Rt.textContent="Any/At least one of",de=s(),q=e("span"),q.textContent="NOT equal",Vt=s(),J=e("li"),mt=e("code"),mt.textContent="?>",pe=s(),I=e("em"),I.textContent="Any/At least one of",fe=s(),B=e("span"),B.textContent="Greater than",ue=s(),P=e("li"),Et=e("code"),Et.textContent="?>=",K=s(),ht=e("em"),ht.textContent="Any/At least one of",me=s(),bt=e("span"),bt.textContent="Greater than or equal",he=s(),w=e("li"),Nt=e("code"),Nt.textContent="?<",at=s(),qt=e("em"),qt.textContent="Any/At least one of",be=s(),Ht=e("span"),Ht.textContent="Less than",Wt=s(),Q=e("li"),gt=e("code"),gt.textContent="?<=",ge=s(),Mt=e("em"),Mt.textContent="Any/At least one of",_e=s(),_t=e("span"),_t.textContent="Less than or equal",xe=s(),V=e("li"),xt=e("code"),xt.textContent="?~",we=s(),wt=e("em"),wt.textContent="Any/At least one of",Xt=s(),W=e("span"),W.textContent=`Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,Yt=s(),L=e("li"),X=e("code"),X.textContent="?!~",R=s(),Dt=e("em"),Dt.textContent="Any/At least one of",Ce=s(),Y=e("span"),Y.textContent=`NOT Like/Contains (if not specified auto wraps the right string OPERAND in a "%" for
|
||||
wildcard match)`,v=s(),It=e("p"),It.innerHTML=`To group and combine several expressions you could use brackets
|
||||
<code>(...)</code>, <code>&&</code> (AND) and <code>||</code> (OR) tokens.`,a(b,"class","txt-danger"),a(Ct,"class","filter-op svelte-1w7s5nw"),a(E,"class","txt"),a(it,"class","filter-op svelte-1w7s5nw"),a(tt,"class","txt"),a(U,"class","filter-op svelte-1w7s5nw"),a(rt,"class","txt"),a(kt,"class","filter-op svelte-1w7s5nw"),a(ct,"class","txt"),a(N,"class","filter-op svelte-1w7s5nw"),a(yt,"class","txt"),a(vt,"class","filter-op svelte-1w7s5nw"),a(Ft,"class","txt"),a(Lt,"class","filter-op svelte-1w7s5nw"),a(At,"class","txt"),a(Tt,"class","filter-op svelte-1w7s5nw"),a(ft,"class","txt"),a(Pt,"class","filter-op svelte-1w7s5nw"),a(St,"class","txt-hint"),a(ut,"class","txt"),a(Ot,"class","filter-op svelte-1w7s5nw"),a(Rt,"class","txt-hint"),a(q,"class","txt"),a(mt,"class","filter-op svelte-1w7s5nw"),a(I,"class","txt-hint"),a(B,"class","txt"),a(Et,"class","filter-op svelte-1w7s5nw"),a(ht,"class","txt-hint"),a(bt,"class","txt"),a(Nt,"class","filter-op svelte-1w7s5nw"),a(qt,"class","txt-hint"),a(Ht,"class","txt"),a(gt,"class","filter-op svelte-1w7s5nw"),a(Mt,"class","txt-hint"),a(_t,"class","txt"),a(xt,"class","filter-op svelte-1w7s5nw"),a(wt,"class","txt-hint"),a(W,"class","txt"),a(X,"class","filter-op svelte-1w7s5nw"),a(Dt,"class","txt-hint"),a(Y,"class","txt")},m(A,k){u(A,n,k),u(A,o,k),u(A,i,k),t(i,f),t(i,h),t(i,r),t(r,b),t(r,C),t(r,$),t(r,g),t(r,p),t(p,Z),t(Z,Ct),t(Z,Ut),t(Z,E),t(p,jt),t(p,M),t(M,it),t(M,S),t(M,tt),t(p,ne),t(p,G),t(G,U),t(G,oe),t(G,rt),t(p,$t),t(p,et),t(et,kt),t(et,ae),t(et,ct),t(p,dt),t(p,lt),t(lt,N),t(lt,zt),t(lt,yt),t(p,y),t(p,st),t(st,vt),t(st,Jt),t(st,Ft),t(p,j),t(p,nt),t(nt,Lt),t(nt,Kt),t(nt,At),t(p,F),t(p,pt),t(pt,Tt),t(pt,ie),t(pt,ft),t(p,re),t(p,D),t(D,Pt),t(D,ot),t(D,St),t(D,O),t(D,ut),t(p,ce),t(p,z),t(z,Ot),t(z,Qt),t(z,Rt),t(z,de),t(z,q),t(p,Vt),t(p,J),t(J,mt),t(J,pe),t(J,I),t(J,fe),t(J,B),t(p,ue),t(p,P),t(P,Et),t(P,K),t(P,ht),t(P,me),t(P,bt),t(p,he),t(p,w),t(w,Nt),t(w,at),t(w,qt),t(w,be),t(w,Ht),t(p,Wt),t(p,Q),t(Q,gt),t(Q,ge),t(Q,Mt),t(Q,_e),t(Q,_t),t(p,xe),t(p,V),t(V,xt),t(V,we),t(V,wt),t(V,Xt),t(V,W),t(p,Yt),t(p,L),t(L,X),t(L,R),t(L,Dt),t(L,Ce),t(L,Y),u(A,v,k),u(A,It,k)},d(A){A&&(m(n),m(o),m(i),m(v),m(It))}}}function ul(d){let n,o,i,f,h;function r(g,p){return g[0]?fl:pl}let b=r(d),C=b(d),$=d[0]&&ze();return{c(){n=e("button"),C.c(),o=s(),$&&$.c(),i=sl(),a(n,"class","btn btn-sm btn-secondary m-t-10")},m(g,p){u(g,n,p),C.m(n,null),u(g,o,p),$&&$.m(g,p),u(g,i,p),f||(h=ll(n,"click",d[1]),f=!0)},p(g,[p]){b!==(b=r(g))&&(C.d(1),C=b(g),C&&(C.c(),C.m(n,null))),g[0]?$||($=ze(),$.c(),$.m(i.parentNode,i)):$&&($.d(1),$=null)},i:Ue,o:Ue,d(g){g&&(m(n),m(o),m(i)),C.d(),$&&$.d(g),f=!1,h()}}}function ml(d,n,o){let i=!1;function f(){o(0,i=!i)}return[i,f]}class hl extends Ze{constructor(n){super(),tl(this,n,ml,ul,el,{})}}function Je(d,n,o){const i=d.slice();return i[7]=n[o],i}function Ke(d,n,o){const i=d.slice();return i[7]=n[o],i}function Qe(d,n,o){const i=d.slice();return i[12]=n[o],i[14]=o,i}function Ve(d){let n;return{c(){n=e("p"),n.innerHTML="Requires admin <code>Authorization:TOKEN</code> header",a(n,"class","txt-hint txt-sm txt-right")},m(o,i){u(o,n,i)},d(o){o&&m(n)}}}function We(d){let n,o=d[12]+"",i,f=d[14]<d[4].length-1?", ":"",h;return{c(){n=e("code"),i=_(o),h=_(f)},m(r,b){u(r,n,b),t(n,i),u(r,h,b)},p(r,b){b&16&&o!==(o=r[12]+"")&&ke(i,o),b&16&&f!==(f=r[14]<r[4].length-1?", ":"")&&ke(h,f)},d(r){r&&(m(n),m(h))}}}function Xe(d,n){let o,i,f;function h(){return n[6](n[7])}return{key:d,first:null,c(){o=e("button"),o.textContent=`${n[7].code} `,a(o,"type","button"),a(o,"class","tab-item"),Le(o,"active",n[2]===n[7].code),this.first=o},m(r,b){u(r,o,b),i||(f=ll(o,"click",h),i=!0)},p(r,b){n=r,b&36&&Le(o,"active",n[2]===n[7].code)},d(r){r&&m(o),i=!1,f()}}}function Ye(d,n){let o,i,f,h;return i=new Fe({props:{content:n[7].body}}),{key:d,first:null,c(){o=e("div"),ee(i.$$.fragment),f=s(),a(o,"class","tab-item"),Le(o,"active",n[2]===n[7].code),this.first=o},m(r,b){u(r,o,b),le(i,o,null),t(o,f),h=!0},p(r,b){n=r,(!h||b&36)&&Le(o,"active",n[2]===n[7].code)},i(r){h||(Bt(i.$$.fragment,r),h=!0)},o(r){Gt(i.$$.fragment,r),h=!1},d(r){r&&m(o),se(i)}}}function bl(d){var Pe,Se,Oe,Re,Ee,Ne;let n,o,i=d[0].name+"",f,h,r,b,C,$,g,p=d[0].name+"",Z,Ct,Ut,E,jt,M,it,S,tt,ne,G,U,oe,rt,$t=d[0].name+"",et,kt,ae,ct,dt,lt,N,zt,yt,y,st,vt,Jt,Ft,j,nt,Lt,Kt,At,F,pt,Tt,ie,ft,re,D,Pt,ot,St,O,ut,ce,z,Ot,Qt,Rt,de,q,Vt,J,mt,pe,I,fe,B,ue,P,Et,K,ht,me,bt,he,w,Nt,at,qt,be,Ht,Wt,Q,gt,ge,Mt,_e,_t,xe,V,xt,we,wt,Xt,W,Yt,L,X,R=[],Dt=new Map,Ce,Y,v=[],It=new Map,A;E=new dl({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${d[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
const resultList = await pb.collection('${(Pe=d[0])==null?void 0:Pe.name}').getList(1, 50, {
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
});
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
const records = await pb.collection('${(Se=d[0])==null?void 0:Se.name}').getFullList({
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
const record = await pb.collection('${(Oe=d[0])==null?void 0:Oe.name}').getFirstListItem('someField="test"', {
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
});
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${d[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
final resultList = await pb.collection('${(Re=d[0])==null?void 0:Re.name}').getList(
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
);
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
final records = await pb.collection('${(Ee=d[0])==null?void 0:Ee.name}').getFullList(
|
||||
sort: '-created',
|
||||
);
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
final record = await pb.collection('${(Ne=d[0])==null?void 0:Ne.name}').getFirstListItem(
|
||||
'someField="test"',
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
);
|
||||
`}});let k=d[1]&&Ve();ot=new Fe({props:{content:`
|
||||
// DESC by created and ASC by id
|
||||
?sort=-created,id
|
||||
`}});let Zt=te(d[4]),T=[];for(let l=0;l<Zt.length;l+=1)T[l]=We(Qe(d,Zt,l));B=new Fe({props:{content:`
|
||||
?filter=(id='abc' && created>'2022-01-01')
|
||||
`}}),P=new hl({}),at=new Fe({props:{content:"?expand=relField1,relField2.subRelField"}});let ye=te(d[5]);const Ae=l=>l[7].code;for(let l=0;l<ye.length;l+=1){let c=Ke(d,ye,l),x=Ae(c);Dt.set(x,R[l]=Xe(x,c))}let $e=te(d[5]);const Te=l=>l[7].code;for(let l=0;l<$e.length;l+=1){let c=Je(d,$e,l),x=Te(c);It.set(x,v[l]=Ye(x,c))}return{c(){n=e("h3"),o=_("List/Search ("),f=_(i),h=_(")"),r=s(),b=e("div"),C=e("p"),$=_("Fetch a paginated "),g=e("strong"),Z=_(p),Ct=_(" records list, supporting sorting and filtering."),Ut=s(),ee(E.$$.fragment),jt=s(),M=e("h6"),M.textContent="API details",it=s(),S=e("div"),tt=e("strong"),tt.textContent="GET",ne=s(),G=e("div"),U=e("p"),oe=_("/api/collections/"),rt=e("strong"),et=_($t),kt=_("/records"),ae=s(),k&&k.c(),ct=s(),dt=e("div"),dt.textContent="Query parameters",lt=s(),N=e("table"),zt=e("thead"),zt.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',yt=s(),y=e("tbody"),st=e("tr"),st.innerHTML='<td>page</td> <td><span class="label">Number</span></td> <td>The page (aka. offset) of the paginated list (default to 1).</td>',vt=s(),Jt=e("tr"),Jt.innerHTML='<td>perPage</td> <td><span class="label">Number</span></td> <td>Specify the max returned records per page (default to 30).</td>',Ft=s(),j=e("tr"),nt=e("td"),nt.textContent="sort",Lt=s(),Kt=e("td"),Kt.innerHTML='<span class="label">String</span>',At=s(),F=e("td"),pt=_("Specify the records order attribute(s). "),Tt=e("br"),ie=_(`
|
||||
Add `),ft=e("code"),ft.textContent="-",re=_(" / "),D=e("code"),D.textContent="+",Pt=_(` (default) in front of the attribute for DESC / ASC order.
|
||||
Ex.:
|
||||
`),ee(ot.$$.fragment),St=s(),O=e("p"),ut=e("strong"),ut.textContent="Supported record sort fields:",ce=s(),z=e("br"),Ot=s(),Qt=e("code"),Qt.textContent="@random",Rt=_(`,
|
||||
`);for(let l=0;l<T.length;l+=1)T[l].c();de=s(),q=e("tr"),Vt=e("td"),Vt.textContent="filter",J=s(),mt=e("td"),mt.innerHTML='<span class="label">String</span>',pe=s(),I=e("td"),fe=_(`Filter the returned records. Ex.:
|
||||
`),ee(B.$$.fragment),ue=s(),ee(P.$$.fragment),Et=s(),K=e("tr"),ht=e("td"),ht.textContent="expand",me=s(),bt=e("td"),bt.innerHTML='<span class="label">String</span>',he=s(),w=e("td"),Nt=_(`Auto expand record relations. Ex.:
|
||||
`),ee(at.$$.fragment),qt=_(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),be=e("br"),Ht=_(`
|
||||
The expanded relations will be appended to each individual record under the
|
||||
`),Wt=e("code"),Wt.textContent="expand",Q=_(" property (eg. "),gt=e("code"),gt.textContent='"expand": {"relField1": {...}, ...}',ge=_(`).
|
||||
`),Mt=e("br"),_e=_(`
|
||||
Only the relations to which the request user has permissions to `),_t=e("strong"),_t.textContent="view",xe=_(" will be expanded."),V=s(),xt=e("tr"),xt.innerHTML=`<td id="query-page">fields</td> <td><span class="label">String</span></td> <td>Comma separated string of the fields to return in the JSON response
|
||||
<em>(by default returns all fields)</em>.
|
||||
<br/>
|
||||
Use <code>*</code> to target all keys from the specific depth level.
|
||||
<br/>
|
||||
For example: <code>?fields=*,expand.author.name</code></td>`,we=s(),wt=e("tr"),wt.innerHTML=`<td id="query-page">skipTotal</td> <td><span class="label">Boolean</span></td> <td>If it is set the total counts query will be skipped and the response fields
|
||||
<code>totalItems</code> and <code>totalPages</code> will have <code>-1</code> value.
|
||||
<br/>
|
||||
This could drastically speed up the search queries when the total counters are not needed or cursor
|
||||
based pagination is used.
|
||||
<br/>
|
||||
For optimization purposes, it is set by default for the
|
||||
<code>getFirstListItem()</code>
|
||||
and
|
||||
<code>getFullList()</code> SDKs methods.</td>`,Xt=s(),W=e("div"),W.textContent="Responses",Yt=s(),L=e("div"),X=e("div");for(let l=0;l<R.length;l+=1)R[l].c();Ce=s(),Y=e("div");for(let l=0;l<v.length;l+=1)v[l].c();a(n,"class","m-b-sm"),a(b,"class","content txt-lg m-b-sm"),a(M,"class","m-b-xs"),a(tt,"class","label label-primary"),a(G,"class","content"),a(S,"class","alert alert-info"),a(dt,"class","section-title"),a(N,"class","table-compact table-border m-b-base"),a(W,"class","section-title"),a(X,"class","tabs-header compact combined left"),a(Y,"class","tabs-content"),a(L,"class","tabs")},m(l,c){u(l,n,c),t(n,o),t(n,f),t(n,h),u(l,r,c),u(l,b,c),t(b,C),t(C,$),t(C,g),t(g,Z),t(C,Ct),u(l,Ut,c),le(E,l,c),u(l,jt,c),u(l,M,c),u(l,it,c),u(l,S,c),t(S,tt),t(S,ne),t(S,G),t(G,U),t(U,oe),t(U,rt),t(rt,et),t(U,kt),t(S,ae),k&&k.m(S,null),u(l,ct,c),u(l,dt,c),u(l,lt,c),u(l,N,c),t(N,zt),t(N,yt),t(N,y),t(y,st),t(y,vt),t(y,Jt),t(y,Ft),t(y,j),t(j,nt),t(j,Lt),t(j,Kt),t(j,At),t(j,F),t(F,pt),t(F,Tt),t(F,ie),t(F,ft),t(F,re),t(F,D),t(F,Pt),le(ot,F,null),t(F,St),t(F,O),t(O,ut),t(O,ce),t(O,z),t(O,Ot),t(O,Qt),t(O,Rt);for(let x=0;x<T.length;x+=1)T[x]&&T[x].m(O,null);t(y,de),t(y,q),t(q,Vt),t(q,J),t(q,mt),t(q,pe),t(q,I),t(I,fe),le(B,I,null),t(I,ue),le(P,I,null),t(y,Et),t(y,K),t(K,ht),t(K,me),t(K,bt),t(K,he),t(K,w),t(w,Nt),le(at,w,null),t(w,qt),t(w,be),t(w,Ht),t(w,Wt),t(w,Q),t(w,gt),t(w,ge),t(w,Mt),t(w,_e),t(w,_t),t(w,xe),t(y,V),t(y,xt),t(y,we),t(y,wt),u(l,Xt,c),u(l,W,c),u(l,Yt,c),u(l,L,c),t(L,X);for(let x=0;x<R.length;x+=1)R[x]&&R[x].m(X,null);t(L,Ce),t(L,Y);for(let x=0;x<v.length;x+=1)v[x]&&v[x].m(Y,null);A=!0},p(l,[c]){var qe,He,Me,De,Ie,Be;(!A||c&1)&&i!==(i=l[0].name+"")&&ke(f,i),(!A||c&1)&&p!==(p=l[0].name+"")&&ke(Z,p);const x={};if(c&9&&(x.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${l[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
const resultList = await pb.collection('${(qe=l[0])==null?void 0:qe.name}').getList(1, 50, {
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
});
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
const records = await pb.collection('${(He=l[0])==null?void 0:He.name}').getFullList({
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
const record = await pb.collection('${(Me=l[0])==null?void 0:Me.name}').getFirstListItem('someField="test"', {
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
});
|
||||
`),c&9&&(x.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${l[3]}');
|
||||
|
||||
...
|
||||
|
||||
// fetch a paginated records list
|
||||
final resultList = await pb.collection('${(De=l[0])==null?void 0:De.name}').getList(
|
||||
page: 1,
|
||||
perPage: 50,
|
||||
filter: 'created >= "2022-01-01 00:00:00" && someField1 != someField2',
|
||||
);
|
||||
|
||||
// you can also fetch all records at once via getFullList
|
||||
final records = await pb.collection('${(Ie=l[0])==null?void 0:Ie.name}').getFullList(
|
||||
sort: '-created',
|
||||
);
|
||||
|
||||
// or fetch only the first record that matches the specified filter
|
||||
final record = await pb.collection('${(Be=l[0])==null?void 0:Be.name}').getFirstListItem(
|
||||
'someField="test"',
|
||||
expand: 'relField1,relField2.subRelField',
|
||||
);
|
||||
`),E.$set(x),(!A||c&1)&&$t!==($t=l[0].name+"")&&ke(et,$t),l[1]?k||(k=Ve(),k.c(),k.m(S,null)):k&&(k.d(1),k=null),c&16){Zt=te(l[4]);let H;for(H=0;H<Zt.length;H+=1){const Ge=Qe(l,Zt,H);T[H]?T[H].p(Ge,c):(T[H]=We(Ge),T[H].c(),T[H].m(O,null))}for(;H<T.length;H+=1)T[H].d(1);T.length=Zt.length}c&36&&(ye=te(l[5]),R=je(R,c,Ae,1,l,ye,Dt,X,nl,Xe,null,Ke)),c&36&&($e=te(l[5]),ol(),v=je(v,c,Te,1,l,$e,It,Y,al,Ye,null,Je),il())},i(l){if(!A){Bt(E.$$.fragment,l),Bt(ot.$$.fragment,l),Bt(B.$$.fragment,l),Bt(P.$$.fragment,l),Bt(at.$$.fragment,l);for(let c=0;c<$e.length;c+=1)Bt(v[c]);A=!0}},o(l){Gt(E.$$.fragment,l),Gt(ot.$$.fragment,l),Gt(B.$$.fragment,l),Gt(P.$$.fragment,l),Gt(at.$$.fragment,l);for(let c=0;c<v.length;c+=1)Gt(v[c]);A=!1},d(l){l&&(m(n),m(r),m(b),m(Ut),m(jt),m(M),m(it),m(S),m(ct),m(dt),m(lt),m(N),m(Xt),m(W),m(Yt),m(L)),se(E,l),k&&k.d(),se(ot),rl(T,l),se(B),se(P),se(at);for(let c=0;c<R.length;c+=1)R[c].d();for(let c=0;c<v.length;c+=1)v[c].d()}}}function gl(d,n,o){let i,f,h,{collection:r}=n,b=200,C=[];const $=g=>o(2,b=g.code);return d.$$set=g=>{"collection"in g&&o(0,r=g.collection)},d.$$.update=()=>{d.$$.dirty&1&&o(4,i=ve.getAllCollectionIdentifiers(r)),d.$$.dirty&1&&o(1,f=(r==null?void 0:r.listRule)===null),d.$$.dirty&3&&r!=null&&r.id&&(C.push({code:200,body:JSON.stringify({page:1,perPage:30,totalPages:1,totalItems:2,items:[ve.dummyCollectionRecord(r),ve.dummyCollectionRecord(r)]},null,2)}),C.push({code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Something went wrong while processing your request. Invalid filter.",
|
||||
"data": {}
|
||||
}
|
||||
`}),f&&C.push({code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Only admins can access this action.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},o(3,h=ve.getApiExampleUrl(cl.baseUrl)),[r,f,b,h,i,C,$]}class wl extends Ze{constructor(n){super(),tl(this,n,gl,bl,el,{collection:0})}}export{wl as default};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user