mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-09-20 13:30:50 +02:00
Compare commits
47
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
957ba6266d | ||
|
|
e1472d922a | ||
|
|
3653e70a6d | ||
|
|
7f2fcc0046 | ||
|
|
32e0eadc2f | ||
|
|
18a7549e50 | ||
|
|
e53c30ca4d | ||
|
|
4adc4b28d2 | ||
|
|
c5554e22e0 | ||
|
|
846136dcfb | ||
|
|
bc378d33f6 | ||
|
|
cc833ad643 | ||
|
|
396aa0f97c | ||
|
|
9f606bdeca | ||
|
|
10a5c685ab | ||
|
|
1ca90e5e8b | ||
|
|
db57572a54 | ||
|
|
0af8f3cc66 | ||
|
|
438ecd88a0 | ||
|
|
45628a919f | ||
|
|
fc133d8665 | ||
|
|
5e6d4d2126 | ||
|
|
c38e7c36a6 | ||
|
|
a89960db71 | ||
|
|
7119ff4716 | ||
|
|
339399b0a4 | ||
|
|
9cb6adab4d | ||
|
|
f6aef4471d | ||
|
|
0e56521e8a | ||
|
|
4f8f320280 | ||
|
|
0daa584461 | ||
|
|
57f615a58c | ||
|
|
1bd0baf328 | ||
|
|
6694215909 | ||
|
|
241a81e1fc | ||
|
|
bed45beb13 | ||
|
|
e4cd6810ab | ||
|
|
f38700982c | ||
|
|
9506669095 | ||
|
|
4f67dba6cb | ||
|
|
47354f5aa9 | ||
|
|
2f35ef29fe | ||
|
|
755149c915 | ||
|
|
f9a2d6c6ae | ||
|
|
9e70c77736 | ||
|
|
b3d88349d7 | ||
|
|
8d0e4a0460 |
@@ -1,3 +1,85 @@
|
||||
## v0.23.0-rc14
|
||||
|
||||
> [!CAUTION]
|
||||
> **This is a prerelease intended for test and experimental purposes only!**
|
||||
|
||||
- Allow changing collate, sort and partial constraints for indexes on system fields.
|
||||
|
||||
- Added system cron to run once a day `PRAGMA wal_checkpoint(TRUNCATE)` as a fallback to assist in high traffic applications where the autocheckpoint may not be able to take its turn.
|
||||
_Similar to the `PRAGMA optimize`, its execution is optional and even if it is not supported by a custom driver it will result only in a WARN app log._
|
||||
|
||||
---
|
||||
|
||||
_Unless something else show up, this is expected to be the last release candidate before the final v0.23.0, planned to be released in the next 1-2 weeks.
|
||||
Because there were several experiments and changes in the rc versions regarding the migrations, if you had a v0.22.x app that you've migrated already to some of the other rc versions and have a full collections snapshot generated for it, note that you may have to regenerate the snapshot again (hopefully this would be the last time)._
|
||||
|
||||
|
||||
## v0.23.0-rc13
|
||||
|
||||
> [!CAUTION]
|
||||
> **This is a prerelease intended for test and experimental purposes only!**
|
||||
|
||||
- Added WakaTime OAuth2 provider ([#5829](https://github.com/pocketbase/pocketbase/pull/5829); thanks @tigawanna).
|
||||
|
||||
- Added `superuser otp EMAIL` command as fallback for generating superuser OTPs from the command line in case OTP has been enabled for the `_superusers` but the SMTP server has deliverability issues.
|
||||
|
||||
- ⚠️ Changed `OnRecordRequestOTPRequest` hook to be triggered even if there is no record matching the email (aka. `e.Record` could be `nil`), allowing you to manually create a new user with the OTP request and assigning it to `e.Record`.
|
||||
|
||||
- Added new `sentTo` system field to the `_otps` collection (it is managed programmatically or by superusers; it could be anything - email, phone, messanger app id, etc.).
|
||||
By default when the OTP is submitted via email it is automatically populated with the user email address (via the `OnMailerRecordOTPSend` hook's finalizer).
|
||||
This allow us on valid `auth-with-otp` request to automatically mark the user email as verified.
|
||||
|
||||
- Added `RateLimitRule.Audience` optional field for restricting a rate limit rule for `"@guest"`-only, `"@auth"`-only, `""`-any (default).
|
||||
|
||||
- Added default max limits for the expressions count and length of the search filter and sort params.
|
||||
_This is just an extra measure mostly for the case when the filter and sort parameters are resolved outside of the request context since the request size limits won't apply._
|
||||
|
||||
- Other minor changes (better error in case of duplicated rate limit rule, fixed typos, changed field id and name length validator to max 100, updated Go deps, etc.).
|
||||
|
||||
|
||||
## v0.23.0-rc12
|
||||
|
||||
> [!CAUTION]
|
||||
> **This is a prerelease intended for test and experimental purposes only!**
|
||||
|
||||
- Fixed validation error caused by the id change in v0.23.0-rc10 migration ([#5820](https://github.com/pocketbase/pocketbase/discussions/5820)).
|
||||
|
||||
|
||||
## v0.23.0-rc11
|
||||
|
||||
> [!CAUTION]
|
||||
> **This is a prerelease intended for test and experimental purposes only!**
|
||||
|
||||
- Fixed JSVM types errors ([#5797](https://github.com/pocketbase/pocketbase/issues/5797)).
|
||||
|
||||
- Skip the default `loadAuthToken` middleware if `e.Auth` is already loaded ([#5800](https://github.com/pocketbase/pocketbase/discussions/5800)).
|
||||
|
||||
- Restored the API rules for the default initial "users" collection migration to be the same as in <= v0.22.x.
|
||||
|
||||
- ⚠️ Replaced the old Instagram provider with a new one that is compatible with the new Instagram Login APIs ([#5588](https://github.com/pocketbase/pocketbase/pull/5588); thanks @pnmcosta).
|
||||
_The provider key is `instagram2` to prevent conflicts with existing linked users._
|
||||
|
||||
- ⚠️ Changes regarding the first superuser creation based on [#5814](https://github.com/pocketbase/pocketbase/discussions/5814).
|
||||
|
||||
- ⚠️ Removed `apis.RequireSuperuserAuthOnlyIfAny()` middleware.
|
||||
|
||||
- ⚠️ Removed `RequestEvent.UnsafeRealIP()` to prevent misuse and confusion with `RequestEvent.RealIP()` (_the latter is considered safer because it checks the trusted proxy headers settings_).
|
||||
|
||||
|
||||
## v0.23.0-rc10
|
||||
|
||||
> [!CAUTION]
|
||||
> **This is a prerelease intended for test and experimental purposes only!**
|
||||
|
||||
- Restore the CRC32 checksum autogeneration for the collection/field ids in order to maintain deterministic default identifier value and minimize conflicts between custom migrations and full collections snapshots.
|
||||
_There is a system migration that will attempt to normalize existing system collections ids, but if you already migrated to v0.23.0-rc and have generated a full collections snapshot migration, you have to delete it and regenerate a new one._
|
||||
|
||||
- Change the behavior of the default generated collections snapshot migration to act as "extend" instead of "replace" to prevent accidental data deletion.
|
||||
_I think this would be rare but if you want the old behaviour you can edit the generated snapshot file and replace the second argument (`deleteMissing`) of `App.ImportCollection/App.ImportCollectionsByMarshaledJSON` from `false` to `true`._
|
||||
|
||||
- Added `app.SubscriptionsBroker().TotalClients()` helper method to return the total registered realtime clients ([#5793](https://github.com/pocketbase/pocketbase/issues/5793)).
|
||||
|
||||
|
||||
## v0.23.0-rc9
|
||||
|
||||
> [!CAUTION]
|
||||
@@ -299,6 +381,8 @@ For upgrading to PocketBase v0.23.0, please refer to:
|
||||
|
||||
- New `POST /api/collections/{collection}/impersonate/{id}` endpoint.
|
||||
|
||||
- ⚠️ Removed `/api/admins/*` endpoints because admins are converted to `_superusers` auth collection records.
|
||||
|
||||
- ⚠️ Previously when uploading new files to a multiple `file` field, new files were automatically appended to the existing field values.
|
||||
This behaviour has changed with v0.23+ and for consistency with the other multi-valued fields when uploading new files they will replace the old ones. If you want to prepend or append new files to an existing multiple `file` field value you can use the `+` prefix or suffix:
|
||||
```js
|
||||
|
||||
@@ -2,6 +2,32 @@
|
||||
> For the most recent versions, please refer to [CHANGELOG.md](./CHANGELOG.md)
|
||||
---
|
||||
|
||||
## v0.22.26
|
||||
|
||||
- (_Backported from v0.23.0-rc_) Added manual WAL checkpoints before creating the zip backup to minimize copying unnecessary data.
|
||||
|
||||
|
||||
## v0.22.25
|
||||
|
||||
- Refresh the old collections state in the Import UI after successful import submission ([#5861](https://github.com/pocketbase/pocketbase/issues/5861)).
|
||||
|
||||
- Added randomized throttle on failed filter list requests as a very rudimentary measure since some security researches raised concern regarding the possibity of eventual side-channel attacks.
|
||||
|
||||
|
||||
## v0.22.24
|
||||
|
||||
- Delete new uploaded record files in case of DB persist error ([#5845](https://github.com/pocketbase/pocketbase/issues/5845)).
|
||||
|
||||
|
||||
## v0.22.23
|
||||
|
||||
- Updated the hooks watcher to account for the case when hooksDir is a symlink ([#5789](https://github.com/pocketbase/pocketbase/issues/5789)).
|
||||
|
||||
- _(Backported from v0.23.0-rc)_ Registered a default `http.Server.ErrorLog` handler to report general server connection errors as app Debug level logs (e.g. invalid TLS handshakes caused by bots trying to access your server via its IP or other similar errors).
|
||||
|
||||
- Other minor fixes (updated npm dev deps to fix the vulnerabilities warning, added more user friendly realtime topic length error, regenerated JSVM types, etc.)
|
||||
|
||||
|
||||
## v0.22.22
|
||||
|
||||
- Added deprecation log in case Instagram OAuth2 is used (_related to [#5652](https://github.com/pocketbase/pocketbase/discussions/5652)_).
|
||||
|
||||
+2
-2
@@ -18,10 +18,10 @@ func bindBackupApi(app core.App, rg *router.RouterGroup[*core.RequestEvent]) {
|
||||
sub := rg.Group("/backups")
|
||||
sub.GET("", backupsList).Bind(RequireSuperuserAuth())
|
||||
sub.POST("", backupCreate).Bind(RequireSuperuserAuth())
|
||||
sub.POST("/upload", backupUpload).Bind(RequireSuperuserAuthOnlyIfAny())
|
||||
sub.POST("/upload", backupUpload).Bind(RequireSuperuserAuth())
|
||||
sub.GET("/{key}", backupDownload) // relies on superuser file token
|
||||
sub.DELETE("/{key}", backupDelete).Bind(RequireSuperuserAuth())
|
||||
sub.POST("/{key}/restore", backupRestore).Bind(RequireSuperuserAuthOnlyIfAny())
|
||||
sub.POST("/{key}/restore", backupRestore).Bind(RequireSuperuserAuth())
|
||||
}
|
||||
|
||||
type backupFileInfo struct {
|
||||
|
||||
+21
-64
@@ -53,7 +53,7 @@ func TestBackupsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{`[]`},
|
||||
@@ -64,7 +64,7 @@ func TestBackupsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -120,7 +120,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.Store().Set(core.StoreKeyActiveBackup, "")
|
||||
@@ -137,7 +137,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
files, err := getBackupFiles(app)
|
||||
@@ -166,7 +166,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
URL: "/api/backups",
|
||||
Body: strings.NewReader(`{"name":"!test.zip"}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
@@ -184,7 +184,7 @@ func TestBackupsCreate(t *testing.T) {
|
||||
URL: "/api/backups",
|
||||
Body: strings.NewReader(`{"name":"test.zip"}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
files, err := getBackupFiles(app)
|
||||
@@ -289,7 +289,7 @@ func TestBackupUpload(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups/upload",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
ensureNoBackups(t, app)
|
||||
@@ -305,7 +305,7 @@ func TestBackupUpload(t *testing.T) {
|
||||
Body: bodies[3].buffer,
|
||||
Headers: map[string]string{
|
||||
"Content-Type": bodies[3].contentType,
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
fsys, err := app.NewBackupsFilesystem()
|
||||
@@ -335,31 +335,7 @@ func TestBackupUpload(t *testing.T) {
|
||||
Body: bodies[4].buffer,
|
||||
Headers: map[string]string{
|
||||
"Content-Type": bodies[4].contentType,
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
files, _ := getBackupFiles(app)
|
||||
if total := len(files); total != 1 {
|
||||
t.Fatalf("Expected %d backup file, got %d", 1, total)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "unauthorized with 0 superusers (valid file)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups/upload",
|
||||
Body: bodies[5].buffer,
|
||||
Headers: map[string]string{
|
||||
"Content-Type": bodies[5].contentType,
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
files, _ := getBackupFiles(app)
|
||||
@@ -415,7 +391,7 @@ func TestBackupsDownload(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups/test1.zip",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -468,7 +444,7 @@ func TestBackupsDownload(t *testing.T) {
|
||||
{
|
||||
Name: "with valid superuser auth token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups/test1.zip?token=eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
URL: "/api/backups/test1.zip?token=eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -481,7 +457,7 @@ func TestBackupsDownload(t *testing.T) {
|
||||
{
|
||||
Name: "with expired superuser file token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups/test1.zip?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MTY0MDk5MTY2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJfcGJjXzMzMjM4NjYzMzkifQ.hTNDzikwJdcoWrLnRnp7xbaifZ2vuYZ0oOYRHtJfnk4",
|
||||
URL: "/api/backups/test1.zip?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MTY0MDk5MTY2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyJ9.nqqtqpPhxU0045F4XP_ruAkzAidYBc5oPy9ErN3XBq0",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -494,7 +470,7 @@ func TestBackupsDownload(t *testing.T) {
|
||||
{
|
||||
Name: "with valid superuser file token but missing backup name",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups/missing?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJfcGJjXzMzMjM4NjYzMzkifQ.C8m3aRZNOxUDhMiuZuDTRIIjRl7wsOyzoxs8EjvKNgY",
|
||||
URL: "/api/backups/missing?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyJ9.Lupz541xRvrktwkrl55p5pPCF77T69ZRsohsIcb2dxc",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -507,7 +483,7 @@ func TestBackupsDownload(t *testing.T) {
|
||||
{
|
||||
Name: "with valid superuser file token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups/test1.zip?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJfcGJjXzMzMjM4NjYzMzkifQ.C8m3aRZNOxUDhMiuZuDTRIIjRl7wsOyzoxs8EjvKNgY",
|
||||
URL: "/api/backups/test1.zip?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyJ9.Lupz541xRvrktwkrl55p5pPCF77T69ZRsohsIcb2dxc",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -524,7 +500,7 @@ func TestBackupsDownload(t *testing.T) {
|
||||
{
|
||||
Name: "with valid superuser file token and backup name with escaped char",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/backups/%40test4.zip?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJfcGJjXzMzMjM4NjYzMzkifQ.C8m3aRZNOxUDhMiuZuDTRIIjRl7wsOyzoxs8EjvKNgY",
|
||||
URL: "/api/backups/%40test4.zip?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyJ9.Lupz541xRvrktwkrl55p5pPCF77T69ZRsohsIcb2dxc",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -601,7 +577,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/backups/missing.zip",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -620,7 +596,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/backups/test1.zip",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -642,7 +618,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/backups/test1.zip",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -678,7 +654,7 @@ func TestBackupsDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/backups/%40test4.zip",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -751,7 +727,7 @@ func TestBackupsRestore(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups/missing.zip/restore",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -767,7 +743,7 @@ func TestBackupsRestore(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups/test1.zip/restore",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := createTestBackups(app); err != nil {
|
||||
@@ -780,25 +756,6 @@ func TestBackupsRestore(t *testing.T) {
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "unauthorized with no superusers (checks only access)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/backups/missing.zip/restore",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := createTestBackups(app); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
|
||||
+10
-10
@@ -360,8 +360,8 @@ func TestBatchRequest(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/batch",
|
||||
Headers: map[string]string{
|
||||
// test@example.com, superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
// test@example.com, _superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: strings.NewReader(`{
|
||||
"requests": [
|
||||
@@ -431,8 +431,8 @@ func TestBatchRequest(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/batch",
|
||||
Headers: map[string]string{
|
||||
// test@example.com, superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
// test@example.com, _superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: strings.NewReader(`{
|
||||
"requests": [
|
||||
@@ -494,8 +494,8 @@ func TestBatchRequest(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
// test@example.com, superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
// test@example.com, _superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.Settings().Batch.Timeout = 1
|
||||
@@ -620,8 +620,8 @@ func TestBatchRequest(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/batch",
|
||||
Headers: map[string]string{
|
||||
// test@example.com, superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
// test@example.com, _superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: strings.NewReader(`{
|
||||
"requests": [
|
||||
@@ -667,8 +667,8 @@ func TestBatchRequest(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/batch",
|
||||
Headers: map[string]string{
|
||||
// test@example.com, superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
// test@example.com, _superusers
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: strings.NewReader(`{
|
||||
"requests": [
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
URL: "/api/collections/import",
|
||||
Body: strings.NewReader(`{"collections":[]}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -79,7 +79,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -145,7 +145,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -199,7 +199,7 @@ func TestCollectionsImport(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
|
||||
+46
-46
@@ -42,7 +42,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -80,7 +80,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections?page=2&perPage=2&sort=-created",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -100,7 +100,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections?filter=invalidfield~'demo2'",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -111,7 +111,7 @@ func TestCollectionsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections?filter=name~'demo'",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -165,7 +165,7 @@ func TestCollectionView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/missing",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 404,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -176,7 +176,7 @@ func TestCollectionView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/demo1",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -193,7 +193,7 @@ func TestCollectionView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/wsmn24bux7wo113",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -249,7 +249,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/missing",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 404,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -260,7 +260,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/demo5",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Delay: 100 * time.Millisecond,
|
||||
ExpectedStatus: 204,
|
||||
@@ -283,7 +283,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/9n89pl5vkct6330",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Delay: 100 * time.Millisecond,
|
||||
ExpectedStatus: 204,
|
||||
@@ -306,7 +306,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/" + core.CollectionNameMFAs,
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -326,7 +326,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/demo2",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -346,7 +346,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/view2",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -365,7 +365,7 @@ func TestCollectionDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/view2",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.OnCollectionDeleteRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
|
||||
@@ -416,7 +416,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
URL: "/api/collections",
|
||||
Body: strings.NewReader(``),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -443,7 +443,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
URL: "/api/collections",
|
||||
Body: strings.NewReader(`{"name":"demo1","type":"base","fields":[{"type":"text","name":""}]}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -469,7 +469,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
URL: "/api/collections",
|
||||
Body: strings.NewReader(`{"name":"new","type":"base","fields":[{"type":"text","id":"12345789","name":"test"}]}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -507,7 +507,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -567,7 +567,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -603,7 +603,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -636,7 +636,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -661,7 +661,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
URL: "/api/collections",
|
||||
Body: strings.NewReader(`{"name":"new","type":"base"}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.OnCollectionCreateRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
|
||||
@@ -689,7 +689,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
"viewQuery":"invalid"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -718,7 +718,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
"viewQuery": "select 1 as id from ` + core.CollectionNameSuperusers + `"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -758,7 +758,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -793,7 +793,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
demo1, err := app.FindCollectionByNameOrId("demo1")
|
||||
@@ -835,7 +835,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -870,7 +870,7 @@ func TestCollectionCreate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -945,7 +945,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
URL: "/api/collections/missing",
|
||||
Body: strings.NewReader(`{}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 404,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -957,7 +957,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
URL: "/api/collections/demo1",
|
||||
Body: strings.NewReader(`{}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -983,7 +983,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
URL: "/api/collections/demo1",
|
||||
Body: strings.NewReader(`{}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.OnCollectionUpdateRequest().BindFunc(func(e *core.CollectionRequestEvent) error {
|
||||
@@ -1006,7 +1006,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
"type":"auth"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1031,7 +1031,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
URL: "/api/collections/demo1",
|
||||
Body: strings.NewReader(`{"name":"new"}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1073,7 +1073,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1109,7 +1109,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1135,7 +1135,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
"passwordAuth":{"identityFields": ["missing"]}
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1165,7 +1165,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
"viewQuery":"invalid"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1193,7 +1193,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
"viewQuery": "select 2 as id, created, updated, email from ` + core.CollectionNameSuperusers + `"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1232,7 +1232,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1261,7 +1261,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
demo1, err := app.FindCollectionByNameOrId("demo1")
|
||||
@@ -1301,7 +1301,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1332,7 +1332,7 @@ func TestCollectionUpdate(t *testing.T) {
|
||||
]
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1405,7 +1405,7 @@ func TestCollectionScaffolds(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/meta/scaffolds",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1457,7 +1457,7 @@ func TestCollectionTruncate(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/demo5/truncate",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -1475,7 +1475,7 @@ func TestCollectionTruncate(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/demo3/truncate",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -1500,7 +1500,7 @@ func TestCollectionTruncate(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/view2/truncate",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/router"
|
||||
)
|
||||
|
||||
const installerParam = "pbinstal"
|
||||
|
||||
var wildcardPlaceholderRegex = regexp.MustCompile(`/{.+\.\.\.}$`)
|
||||
|
||||
func stripWildcard(pattern string) string {
|
||||
return wildcardPlaceholderRegex.ReplaceAllString(pattern, "")
|
||||
}
|
||||
|
||||
// installerRedirect redirects the user to the installer dashboard UI page
|
||||
// when the application needs some preliminary configurations to be done.
|
||||
func installerRedirect(app core.App, cpPath string) func(*core.RequestEvent) error {
|
||||
// note: to avoid locks contention it is not concurrent safe but it
|
||||
// is expected to be updated only once during initialization
|
||||
var hasSuperuser bool
|
||||
|
||||
// strip named wildcard
|
||||
cpPath = stripWildcard(cpPath)
|
||||
|
||||
updateHasSuperuser := func(app core.App) error {
|
||||
total, err := app.CountRecords(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasSuperuser = total > 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// load initial state on app init
|
||||
app.OnBootstrap().BindFunc(func(e *core.BootstrapEvent) error {
|
||||
err := e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = updateHasSuperuser(e.App)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check for existing superuser: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
// update on superuser create
|
||||
app.OnRecordCreateRequest(core.CollectionNameSuperusers).BindFunc(func(e *core.RecordRequestEvent) error {
|
||||
err := e.Next()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !hasSuperuser {
|
||||
hasSuperuser = true
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return func(e *core.RequestEvent) error {
|
||||
if hasSuperuser {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
isAPI := strings.HasPrefix(e.Request.URL.Path, "/api/")
|
||||
isControlPanel := strings.HasPrefix(e.Request.URL.Path, cpPath)
|
||||
wildcard := e.Request.PathValue(StaticWildcardParam)
|
||||
|
||||
// skip redirect checks for API and non-root level dashboard index.html requests (css, images, etc.)
|
||||
if isAPI || (isControlPanel && wildcard != "" && wildcard != router.IndexPage) {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
// check again in case the superuser was created by some other process
|
||||
if err := updateHasSuperuser(e.App); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hasSuperuser {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
_, hasInstallerParam := e.Request.URL.Query()[installerParam]
|
||||
|
||||
// redirect to the installer page
|
||||
if !hasInstallerParam {
|
||||
return e.Redirect(http.StatusTemporaryRedirect, cpPath+"?"+installerParam+"#")
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// dashboardRemoveInstallerParam redirects to a non-installer
|
||||
// query param in case there is already a superuser created.
|
||||
//
|
||||
// Note: intended to be registered only for the dashboard route
|
||||
// to prevent excessive checks for every other route in installerRedirect.
|
||||
func dashboardRemoveInstallerParam() func(*core.RequestEvent) error {
|
||||
return func(e *core.RequestEvent) error {
|
||||
_, hasInstallerParam := e.Request.URL.Query()[installerParam]
|
||||
if !hasInstallerParam {
|
||||
return e.Next() // nothing to remove
|
||||
}
|
||||
|
||||
// clear installer param
|
||||
total, _ := e.App.CountRecords(core.CollectionNameSuperusers)
|
||||
if total > 0 {
|
||||
return e.Redirect(http.StatusTemporaryRedirect, "?")
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// dashboardCacheControl adds default Cache-Control header for all
|
||||
// dashboard UI resources (ignoring the root index.html path)
|
||||
func dashboardCacheControl() func(*core.RequestEvent) error {
|
||||
return func(e *core.RequestEvent) error {
|
||||
if e.Request.PathValue(StaticWildcardParam) != "" {
|
||||
e.Response.Header().Set("Cache-Control", "max-age=1209600, stale-while-revalidate=86400")
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -49,7 +49,7 @@ func TestFileToken(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/files/token",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -65,7 +65,7 @@ func TestFileToken(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/files/token",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.OnFileTokenRequest().BindFunc(func(e *core.FileTokenRequestEvent) error {
|
||||
@@ -273,7 +273,7 @@ func TestFileDownload(t *testing.T) {
|
||||
{
|
||||
Name: "protected file - superuser with expired file token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/files/demo1/al1h9ijdeojtsjy/300_Jsjq7RdBgA.png?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MTY0MDk5MTY2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJfcGJjXzMzMjM4NjYzMzkifQ.hTNDzikwJdcoWrLnRnp7xbaifZ2vuYZ0oOYRHtJfnk4",
|
||||
URL: "/api/files/demo1/al1h9ijdeojtsjy/300_Jsjq7RdBgA.png?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MTY0MDk5MTY2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyJ9.nqqtqpPhxU0045F4XP_ruAkzAidYBc5oPy9ErN3XBq0",
|
||||
ExpectedStatus: 404,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
@@ -281,7 +281,7 @@ func TestFileDownload(t *testing.T) {
|
||||
{
|
||||
Name: "protected file - superuser with valid file token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/files/demo1/al1h9ijdeojtsjy/300_Jsjq7RdBgA.png?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJfcGJjXzMzMjM4NjYzMzkifQ.C8m3aRZNOxUDhMiuZuDTRIIjRl7wsOyzoxs8EjvKNgY",
|
||||
URL: "/api/files/demo1/al1h9ijdeojtsjy/300_Jsjq7RdBgA.png?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyJ9.Lupz541xRvrktwkrl55p5pPCF77T69ZRsohsIcb2dxc",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"PNG"},
|
||||
ExpectedEvents: map[string]int{
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ func TestHealthAPI(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/health",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
)
|
||||
|
||||
// @todo consider combining with the installer specific hooks after refactoring cmd
|
||||
func loadInstaller(app core.App, dashboardURL string) error {
|
||||
if !needInstallerSuperuser(app) {
|
||||
return nil
|
||||
}
|
||||
|
||||
installerRecord, err := findOrCreateInstallerSuperuser(app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token, err := installerRecord.NewStaticAuthToken(30 * time.Minute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// launch url (ignore errors and always print a help text as fallback)
|
||||
url := fmt.Sprintf("%s/#/pbinstal/%s", strings.TrimRight(dashboardURL, "/"), token)
|
||||
_ = launchURL(url)
|
||||
color.Magenta("\n(!) Launch the URL below in the browser if it hasn't been open already to create your first superuser account:")
|
||||
color.New(color.Bold).Add(color.FgCyan).Println(url)
|
||||
color.New(color.FgHiBlack, color.Italic).Printf("(you can also create your first superuser by running: %s superuser upsert EMAIL PASS)\n\n", os.Args[0])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func needInstallerSuperuser(app core.App) bool {
|
||||
total, err := app.CountRecords(core.CollectionNameSuperusers, dbx.Not(dbx.HashExp{
|
||||
"email": core.DefaultInstallerEmail,
|
||||
}))
|
||||
|
||||
return err == nil && total == 0
|
||||
}
|
||||
|
||||
func findOrCreateInstallerSuperuser(app core.App) (*core.Record, error) {
|
||||
col, err := app.FindCachedCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record, err := app.FindAuthRecordByEmail(col, core.DefaultInstallerEmail)
|
||||
if err != nil {
|
||||
if !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record = core.NewRecord(col)
|
||||
record.SetEmail(core.DefaultInstallerEmail)
|
||||
record.SetPassword(security.RandomString(30))
|
||||
|
||||
err = app.Save(record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func launchURL(url string) error {
|
||||
if err := is.URL.Validate(url); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return exec.Command("open", url).Start()
|
||||
case "windows":
|
||||
// not sure if this is the best command but seems to be the most reliable based on the comments in
|
||||
// https://stackoverflow.com/questions/3739327/launching-a-website-via-the-windows-commandline#answer-49115945
|
||||
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
default: // linux, freebsd, etc.
|
||||
return exec.Command("xdg-open", url).Start()
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -36,7 +36,7 @@ func TestLogsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/logs",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubLogsData(app); err != nil {
|
||||
@@ -59,7 +59,7 @@ func TestLogsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/logs?filter=data.status>200",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubLogsData(app); err != nil {
|
||||
@@ -111,7 +111,7 @@ func TestLogView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/logs/missing1-9f38-44fb-bf82-c8f53b310d91",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubLogsData(app); err != nil {
|
||||
@@ -127,7 +127,7 @@ func TestLogView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/logs/873f2133-9f38-44fb-bf82-c8f53b310d91",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubLogsData(app); err != nil {
|
||||
@@ -175,7 +175,7 @@ func TestLogsStats(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/logs/stats",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubLogsData(app); err != nil {
|
||||
@@ -192,7 +192,7 @@ func TestLogsStats(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/logs/stats?filter=data.status>200",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubLogsData(app); err != nil {
|
||||
|
||||
+9
-36
@@ -48,7 +48,6 @@ const (
|
||||
DefaultRequireGuestOnlyMiddlewareId = "pbRequireGuestOnly"
|
||||
DefaultRequireAuthMiddlewareId = "pbRequireAuth"
|
||||
DefaultRequireSuperuserAuthMiddlewareId = "pbRequireSuperuserAuth"
|
||||
DefaultRequireSuperuserAuthOnlyIfAnyMiddlewareId = "pbRequireSuperuserAuthOnlyIfAny"
|
||||
DefaultRequireSuperuserOrOwnerAuthMiddlewareId = "pbRequireSuperuserOrOwnerAuth"
|
||||
DefaultRequireSameCollectionContextAuthMiddlewareId = "pbRequireSameCollectionContextAuth"
|
||||
)
|
||||
@@ -110,31 +109,6 @@ func RequireSuperuserAuth() *hook.Handler[*core.RequestEvent] {
|
||||
}
|
||||
}
|
||||
|
||||
// RequireSuperuserAuthOnlyIfAny middleware requires a request to have
|
||||
// a valid superuser Authorization header ONLY if the application has
|
||||
// at least 1 existing superuser.
|
||||
func RequireSuperuserAuthOnlyIfAny() *hook.Handler[*core.RequestEvent] {
|
||||
return &hook.Handler[*core.RequestEvent]{
|
||||
Id: DefaultRequireSuperuserAuthOnlyIfAnyMiddlewareId,
|
||||
Func: func(e *core.RequestEvent) error {
|
||||
if e.HasSuperuserAuth() {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
totalSuperusers, err := e.App.CountRecords(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return e.InternalServerError("Failed to fetch superusers info.", err)
|
||||
}
|
||||
|
||||
if totalSuperusers == 0 {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
return requireAuth(core.CollectionNameSuperusers)(e)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// RequireSuperuserOrOwnerAuth middleware requires a request to have
|
||||
// a valid superuser or regular record owner Authorization header set.
|
||||
//
|
||||
@@ -196,7 +170,9 @@ func RequireSameCollectionContextAuth(collectionPathParam string) *hook.Handler[
|
||||
|
||||
// loadAuthToken attempts to load the auth context based on the "Authorization: TOKEN" header value.
|
||||
//
|
||||
// This middleware does nothing in case of missing, invalid or expired token.
|
||||
// This middleware does nothing in case of:
|
||||
// - missing, invalid or expired token
|
||||
// - e.Auth is already loaded by another middleware
|
||||
//
|
||||
// This middleware is registered by default for all routes.
|
||||
//
|
||||
@@ -207,6 +183,11 @@ func loadAuthToken() *hook.Handler[*core.RequestEvent] {
|
||||
Id: DefaultLoadAuthTokenMiddlewareId,
|
||||
Priority: DefaultLoadAuthTokenMiddlewarePriority,
|
||||
Func: func(e *core.RequestEvent) error {
|
||||
// already loaded by another middleware
|
||||
if e.Auth != nil {
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
token := getAuthTokenFromRequest(e)
|
||||
if token == "" {
|
||||
return e.Next()
|
||||
@@ -416,17 +397,9 @@ func logRequest(event *core.RequestEvent, err error) {
|
||||
}
|
||||
|
||||
if event.App.Settings().Logs.LogIP {
|
||||
var userIP string
|
||||
if len(event.App.Settings().TrustedProxy.Headers) > 0 {
|
||||
userIP = event.RealIP()
|
||||
} else {
|
||||
// fallback to the legacy behavior (it is "safe" since it is only for log purposes)
|
||||
userIP = cutStr(event.UnsafeRealIP(), 50)
|
||||
}
|
||||
|
||||
attrs = append(
|
||||
attrs,
|
||||
slog.String("userIP", userIP),
|
||||
slog.String("userIP", event.RealIP()),
|
||||
slog.String("remoteIP", event.RemoteIP()),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -104,8 +104,43 @@ func checkCollectionRateLimit(e *core.RequestEvent, collection *core.Collection,
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// @todo consider exporting as RateLimit helper?
|
||||
// @todo consider exporting as helper?
|
||||
//
|
||||
//nolint:unused
|
||||
func isClientRateLimited(e *core.RequestEvent, rtId string) bool {
|
||||
rateLimiters, ok := e.App.Store().Get(rateLimitersStoreKey).(*store.Store[*rateLimiter])
|
||||
if !ok || rateLimiters == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
rt, ok := rateLimiters.GetOk(rtId)
|
||||
if !ok || rt == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
client, ok := rt.getClient(e.RealIP())
|
||||
if !ok || client == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return client.available <= 0 && time.Now().Unix()-client.lastConsume < client.interval
|
||||
}
|
||||
|
||||
// @todo consider exporting as helper?
|
||||
func checkRateLimit(e *core.RequestEvent, rtId string, rule core.RateLimitRule) error {
|
||||
switch rule.Audience {
|
||||
case core.RateLimitRuleAudienceAll:
|
||||
// valid for both guest and regular users
|
||||
case core.RateLimitRuleAudienceGuest:
|
||||
if e.Auth != nil {
|
||||
return nil
|
||||
}
|
||||
case core.RateLimitRuleAudienceAuth:
|
||||
if e.Auth == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
rateLimiters := e.App.Store().GetOrSet(rateLimitersStoreKey, func() any {
|
||||
return initRateLimitersStore(e.App)
|
||||
}).(*store.Store[*rateLimiter])
|
||||
@@ -199,6 +234,15 @@ type rateLimiter struct {
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (rt *rateLimiter) getClient(key string) (*fixedWindow, bool) {
|
||||
rt.RLock()
|
||||
client, ok := rt.clients[key]
|
||||
rt.RUnlock()
|
||||
|
||||
return client, ok
|
||||
}
|
||||
|
||||
func (rt *rateLimiter) isAllowed(key string) bool {
|
||||
// lock only reads to minimize locks contention
|
||||
rt.RLock()
|
||||
|
||||
@@ -31,6 +31,18 @@ func TestDefaultRateLimitMiddleware(t *testing.T) {
|
||||
MaxRequests: 1,
|
||||
Duration: 1,
|
||||
},
|
||||
{
|
||||
Label: "/rate/guest",
|
||||
MaxRequests: 1,
|
||||
Duration: 1,
|
||||
Audience: core.RateLimitRuleAudienceGuest,
|
||||
},
|
||||
{
|
||||
Label: "/rate/auth",
|
||||
MaxRequests: 1,
|
||||
Duration: 1,
|
||||
Audience: core.RateLimitRuleAudienceAuth,
|
||||
},
|
||||
}
|
||||
|
||||
pbRouter, err := apis.NewRouter(app)
|
||||
@@ -48,6 +60,12 @@ func TestDefaultRateLimitMiddleware(t *testing.T) {
|
||||
pbRouter.GET("/rate/b", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "b")
|
||||
})
|
||||
pbRouter.GET("/rate/guest", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "guest")
|
||||
})
|
||||
pbRouter.GET("/rate/auth", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "auth")
|
||||
})
|
||||
|
||||
mux, err := pbRouter.BuildMux()
|
||||
if err != nil {
|
||||
@@ -57,30 +75,53 @@ func TestDefaultRateLimitMiddleware(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
url string
|
||||
wait float64
|
||||
authenticated bool
|
||||
expectedStatus int
|
||||
}{
|
||||
{"/norate", 0, 200},
|
||||
{"/norate", 0, 200},
|
||||
{"/norate", 0, 200},
|
||||
{"/norate", 0, 200},
|
||||
{"/norate", 0, 200},
|
||||
{"/norate", 0, false, 200},
|
||||
{"/norate", 0, false, 200},
|
||||
{"/norate", 0, false, 200},
|
||||
{"/norate", 0, false, 200},
|
||||
{"/norate", 0, false, 200},
|
||||
|
||||
{"/rate/a", 0, 200},
|
||||
{"/rate/a", 0, 200},
|
||||
{"/rate/a", 0, 429},
|
||||
{"/rate/a", 0, 429},
|
||||
{"/rate/a", 1.1, 200},
|
||||
{"/rate/a", 0, 200},
|
||||
{"/rate/a", 0, 429},
|
||||
{"/rate/a", 0, false, 200},
|
||||
{"/rate/a", 0, false, 200},
|
||||
{"/rate/a", 0, false, 429},
|
||||
{"/rate/a", 0, false, 429},
|
||||
{"/rate/a", 1.1, false, 200},
|
||||
{"/rate/a", 0, false, 200},
|
||||
{"/rate/a", 0, false, 429},
|
||||
|
||||
{"/rate/b", 0, 200},
|
||||
{"/rate/b", 0, 200},
|
||||
{"/rate/b", 0, 200},
|
||||
{"/rate/b", 0, 429},
|
||||
{"/rate/b", 1.1, 200},
|
||||
{"/rate/b", 0, 200},
|
||||
{"/rate/b", 0, 200},
|
||||
{"/rate/b", 0, 429},
|
||||
{"/rate/b", 0, false, 200},
|
||||
{"/rate/b", 0, false, 200},
|
||||
{"/rate/b", 0, false, 200},
|
||||
{"/rate/b", 0, false, 429},
|
||||
{"/rate/b", 1.1, false, 200},
|
||||
{"/rate/b", 0, false, 200},
|
||||
{"/rate/b", 0, false, 200},
|
||||
{"/rate/b", 0, false, 429},
|
||||
|
||||
// "auth" with guest (should be ignored)
|
||||
{"/rate/auth", 0, false, 200},
|
||||
{"/rate/auth", 0, false, 200},
|
||||
{"/rate/auth", 0, false, 200},
|
||||
{"/rate/auth", 0, false, 200},
|
||||
|
||||
// "auth" rule with regular user
|
||||
{"/rate/auth", 0, true, 200},
|
||||
{"/rate/auth", 0, true, 429},
|
||||
{"/rate/auth", 0, true, 429},
|
||||
|
||||
// "guest" with guest
|
||||
{"/rate/guest", 0, false, 200},
|
||||
{"/rate/guest", 0, false, 429},
|
||||
{"/rate/guest", 0, false, 429},
|
||||
|
||||
// "guest" rule with regular user (should be ignored)
|
||||
{"/rate/guest", 0, true, 200},
|
||||
{"/rate/guest", 0, true, 200},
|
||||
{"/rate/guest", 0, true, 200},
|
||||
{"/rate/guest", 0, true, 200},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
@@ -91,6 +132,21 @@ func TestDefaultRateLimitMiddleware(t *testing.T) {
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", s.url, nil)
|
||||
|
||||
if s.authenticated {
|
||||
auth, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
token, err := auth.NewAuthToken()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", token)
|
||||
}
|
||||
|
||||
mux.ServeHTTP(rec, req)
|
||||
|
||||
result := rec.Result()
|
||||
|
||||
@@ -75,7 +75,7 @@ func TestRequireGuestOnly(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: beforeTestFunc,
|
||||
ExpectedStatus: 400,
|
||||
@@ -148,7 +148,7 @@ func TestRequireAuth(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJfcGJjXzMzMjM4NjYzMzkifQ.C8m3aRZNOxUDhMiuZuDTRIIjRl7wsOyzoxs8EjvKNgY",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6ImZpbGUiLCJjb2xsZWN0aW9uSWQiOiJwYmNfMzE0MjYzNTgyMyJ9.Lupz541xRvrktwkrl55p5pPCF77T69ZRsohsIcb2dxc",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
@@ -197,7 +197,7 @@ func TestRequireAuth(t *testing.T) {
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
// superuser
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
@@ -214,7 +214,7 @@ func TestRequireAuth(t *testing.T) {
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
// superuser
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
@@ -253,7 +253,7 @@ func TestRequireSuperuserAuth(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoxNjQwOTkxNjYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.a668tes0bS6FU-OOlXMoRrdd57a_oldIPd5b0Gv_RYw",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjE2NDA5OTE2NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.0pDcBPGDpL2Khh76ivlRi7ugiLBSYvasct3qpHV3rfs",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
@@ -285,7 +285,7 @@ func TestRequireSuperuserAuth(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
@@ -302,95 +302,6 @@ func TestRequireSuperuserAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireSuperuserAuthOnlyIfAny(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []tests.ApiScenario{
|
||||
{
|
||||
Name: "guest (while having at least 1 existing superuser)",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "guest (while having 0 existing superusers)",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"test123"},
|
||||
},
|
||||
{
|
||||
Name: "expired/invalid token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoxNjQwOTkxNjYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.a668tes0bS6FU-OOlXMoRrdd57a_oldIPd5b0Gv_RYw",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "valid regular user token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.ZT3F0Z3iM-xbGgSG3LEKiEzHrPHr8t8IuHLZGGNuxLo",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "valid superuser auth token",
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test", func(e *core.RequestEvent) error {
|
||||
return e.String(200, "test123")
|
||||
}).Bind(apis.RequireSuperuserAuthOnlyIfAny())
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{"test123"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario.Test(t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequireSuperuserOrOwnerAuth(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -413,7 +324,7 @@ func TestRequireSuperuserOrOwnerAuth(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test/4q1xlclmfloku33",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoxNjQwOTkxNjYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.a668tes0bS6FU-OOlXMoRrdd57a_oldIPd5b0Gv_RYw",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjE2NDA5OTE2NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.0pDcBPGDpL2Khh76ivlRi7ugiLBSYvasct3qpHV3rfs",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test/{id}", func(e *core.RequestEvent) error {
|
||||
@@ -491,7 +402,7 @@ func TestRequireSuperuserOrOwnerAuth(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test/4q1xlclmfloku33",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test/{id}", func(e *core.RequestEvent) error {
|
||||
@@ -609,7 +520,7 @@ func TestRequireSameCollectionContextAuth(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/my/test/_pb_users_auth_",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
e.Router.GET("/my/test/{collection}", func(e *core.RequestEvent) error {
|
||||
|
||||
@@ -286,7 +286,7 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
URL: "/api/realtime",
|
||||
Body: strings.NewReader(`{"clientId":"` + client.Id() + `","subscriptions":["test1", "test2"]}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
|
||||
@@ -49,7 +49,7 @@ func TestRecordRequestEmailChange(t *testing.T) {
|
||||
URL: "/api/collections/users/request-email-change",
|
||||
Body: strings.NewReader(`{"newEmail":"change@example.com"}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestRecordAuthImpersonate(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/impersonate/4q1xlclmfloku33",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -71,7 +71,7 @@ func TestRecordAuthImpersonate(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/impersonate/4q1xlclmfloku33",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: strings.NewReader(`{"duration":-1}`),
|
||||
ExpectedStatus: 400,
|
||||
@@ -86,7 +86,7 @@ func TestRecordAuthImpersonate(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/impersonate/4q1xlclmfloku33",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: strings.NewReader(`{"duration":100}`),
|
||||
ExpectedStatus: 200,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -32,12 +33,10 @@ func recordRequestOTP(e *core.RequestEvent) error {
|
||||
}
|
||||
|
||||
record, err := e.App.FindAuthRecordByEmail(collection, form.Email)
|
||||
if err != nil {
|
||||
// eagerly write a dummy 200 response as a very rudimentary user emails enumeration protection
|
||||
e.JSON(http.StatusOK, map[string]string{
|
||||
"otpId": core.GenerateDefaultRandomId(),
|
||||
})
|
||||
return fmt.Errorf("failed to fetch %s record with email %s: %w", collection.Name, form.Email, err)
|
||||
|
||||
// ignore not found errors to allow custom record find implementations
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return e.InternalServerError("", err)
|
||||
}
|
||||
|
||||
event := new(core.RecordCreateOTPRequestEvent)
|
||||
@@ -46,7 +45,18 @@ func recordRequestOTP(e *core.RequestEvent) error {
|
||||
event.Collection = collection
|
||||
event.Record = record
|
||||
|
||||
originalApp := e.App
|
||||
|
||||
return e.App.OnRecordRequestOTPRequest().Trigger(event, func(e *core.RecordCreateOTPRequestEvent) error {
|
||||
if e.Record == nil {
|
||||
// write a dummy 200 response as a very rudimentary user emails enumeration protection
|
||||
e.JSON(http.StatusOK, map[string]string{
|
||||
"otpId": core.GenerateDefaultRandomId(),
|
||||
})
|
||||
|
||||
return fmt.Errorf("failed to fetch %s record with email %s: %w", collection.Name, form.Email, err)
|
||||
}
|
||||
|
||||
var otp *core.OTP
|
||||
|
||||
// limit the new OTP creations for a single user
|
||||
@@ -90,11 +100,10 @@ func recordRequestOTP(e *core.RequestEvent) error {
|
||||
// send OTP email
|
||||
// (in the background as a very basic timing attacks and emails enumeration protection)
|
||||
// ---
|
||||
app := e.App
|
||||
routine.FireAndForget(func() {
|
||||
err = mails.SendRecordOTP(app, e.Record, otp.Id, e.Password)
|
||||
err = mails.SendRecordOTP(originalApp, e.Record, otp.Id, e.Password)
|
||||
if err != nil {
|
||||
app.Logger().Error("Failed to send OTP email", "error", errors.Join(err, e.App.Delete(otp)))
|
||||
originalApp.Logger().Error("Failed to send OTP email", "error", errors.Join(err, originalApp.Delete(otp)))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,7 +86,10 @@ func TestRecordRequestOTP(t *testing.T) {
|
||||
ExpectedContent: []string{
|
||||
`"otpId":"`, // some fake random generated string
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordRequestOTPRequest": 1,
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend() != 0 {
|
||||
t.Fatalf("Expected zero emails, got %d", app.TestMailer.TotalSend())
|
||||
@@ -137,6 +140,57 @@ func TestRecordRequestOTP(t *testing.T) {
|
||||
"OnModelCreate": 1,
|
||||
"OnModelCreateExecute": 1,
|
||||
"OnModelAfterCreateSuccess": 1,
|
||||
"OnModelValidate": 2, // + 1 for the OTP update after the email send
|
||||
"OnRecordCreate": 1,
|
||||
"OnRecordCreateExecute": 1,
|
||||
"OnRecordAfterCreateSuccess": 1,
|
||||
"OnRecordValidate": 2,
|
||||
// OTP update
|
||||
"OnModelUpdate": 1,
|
||||
"OnModelUpdateExecute": 1,
|
||||
"OnModelAfterUpdateSuccess": 1,
|
||||
"OnRecordUpdate": 1,
|
||||
"OnRecordUpdateExecute": 1,
|
||||
"OnRecordAfterUpdateSuccess": 1,
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend() != 1 {
|
||||
t.Fatalf("Expected 1 email, got %d", app.TestMailer.TotalSend())
|
||||
}
|
||||
|
||||
// ensure that sentTo is set
|
||||
otps, err := app.FindRecordsByFilter(core.CollectionNameOTPs, "sentTo='test@example.com'", "", 0, 0)
|
||||
if err != nil || len(otps) != 1 {
|
||||
t.Fatalf("Expected to find 1 OTP with sentTo %q, found %d", "test@example.com", len(otps))
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "existing auth record with intercepted email (with < 9 non-expired)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/request-otp",
|
||||
Body: strings.NewReader(`{"email":"test@example.com"}`),
|
||||
Delay: 100 * time.Millisecond,
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// prevent email sent
|
||||
app.OnMailerRecordOTPSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
|
||||
return nil
|
||||
})
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"otpId":"`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
`"otpId":"otp_`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordRequestOTPRequest": 1,
|
||||
"OnMailerRecordOTPSend": 1,
|
||||
"OnModelCreate": 1,
|
||||
"OnModelCreateExecute": 1,
|
||||
"OnModelAfterCreateSuccess": 1,
|
||||
"OnModelValidate": 1,
|
||||
"OnRecordCreate": 1,
|
||||
"OnRecordCreateExecute": 1,
|
||||
@@ -144,8 +198,14 @@ func TestRecordRequestOTP(t *testing.T) {
|
||||
"OnRecordValidate": 1,
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend() != 1 {
|
||||
t.Fatalf("Expected 1 email, got %d", app.TestMailer.TotalSend())
|
||||
if app.TestMailer.TotalSend() != 0 {
|
||||
t.Fatalf("Expected 0 emails, got %d", app.TestMailer.TotalSend())
|
||||
}
|
||||
|
||||
// ensure that there is no OTP with user email as sentTo
|
||||
otps, err := app.FindRecordsByFilter(core.CollectionNameOTPs, "sentTo='test@example.com'", "", 0, 0)
|
||||
if err != nil || len(otps) != 0 {
|
||||
t.Fatalf("Expected to find 0 OTPs with sentTo %q, found %d", "test@example.com", len(otps))
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestRecordAuthRefresh(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/auth-refresh",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
|
||||
@@ -51,8 +51,8 @@ func recordAuthWithOTP(e *core.RequestEvent) error {
|
||||
return e.BadRequestError("Invalid or expired OTP", fmt.Errorf("missing auth record: %w", err))
|
||||
}
|
||||
|
||||
// since otps are usually simple digit numbers we enforce an extra rate limit rule to prevent enumerations
|
||||
err = checkRateLimit(e, "@pb_otp_"+event.OTP.Id+event.Record.Id, core.RateLimitRule{MaxRequests: 4, Duration: 180})
|
||||
// since otps are usually simple digit numbers, enforce an extra rate limit rule as basic enumaration protection
|
||||
err = checkRateLimit(e, "@pb_otp_"+event.Record.Id, core.RateLimitRule{MaxRequests: 5, Duration: 180})
|
||||
if err != nil {
|
||||
return e.TooManyRequestsError("Too many attempts, please try again later with a new OTP.", nil)
|
||||
}
|
||||
@@ -63,23 +63,33 @@ func recordAuthWithOTP(e *core.RequestEvent) error {
|
||||
// ---
|
||||
|
||||
return e.App.OnRecordAuthWithOTPRequest().Trigger(event, func(e *core.RecordAuthWithOTPRequestEvent) error {
|
||||
// update the user email verified state in case the OTP originate from an email address matching the current record one
|
||||
//
|
||||
// note: don't wait for success auth response (it could fail because of MFA) and because we already validated the OTP above
|
||||
otpSentTo := e.OTP.SentTo()
|
||||
if !e.Record.Verified() && otpSentTo != "" && e.Record.Email() == otpSentTo {
|
||||
e.Record.SetVerified(true)
|
||||
err = e.App.Save(e.Record)
|
||||
if err != nil {
|
||||
e.App.Logger().Error("Failed to update record verified state after successful OTP validation",
|
||||
"error", err,
|
||||
"otpId", e.OTP.Id,
|
||||
"recordId", e.Record.Id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
err = RecordAuthResponse(e.RequestEvent, e.Record, core.MFAMethodOTP, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try to delete the used otp
|
||||
if e.OTP != nil {
|
||||
err = e.App.Delete(e.OTP)
|
||||
if err != nil {
|
||||
e.App.Logger().Error("Failed to delete used OTP", "error", err, "otpId", e.OTP.Id)
|
||||
}
|
||||
err = e.App.Delete(e.OTP)
|
||||
if err != nil {
|
||||
e.App.Logger().Error("Failed to delete used OTP", "error", err, "otpId", e.OTP.Id)
|
||||
}
|
||||
|
||||
// note: we don't update the user verified state the same way as in the password reset confirmation
|
||||
// at the moment because it is not clear whether the otp confirmation came from the user email
|
||||
// (e.g. it could be from an sms or some other channel)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ func TestRecordAuthWithOTP(t *testing.T) {
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "valid otp with valid password",
|
||||
Name: "valid otp with valid password (enabled MFA)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/auth-with-otp",
|
||||
Body: strings.NewReader(`{
|
||||
@@ -236,7 +236,7 @@ func TestRecordAuthWithOTP(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "valid otp with valid password (disabled MFA)",
|
||||
Name: "valid otp with valid password and empty sentTo (disabled MFA)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/auth-with-otp",
|
||||
Body: strings.NewReader(`{
|
||||
@@ -249,8 +249,15 @@ func TestRecordAuthWithOTP(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// ensure that the user is unverified
|
||||
user.SetVerified(false)
|
||||
if err = app.Save(user); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// disable MFA
|
||||
user.Collection().MFA.Enabled = false
|
||||
if err := app.Save(user.Collection()); err != nil {
|
||||
if err = app.Save(user.Collection()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -297,6 +304,106 @@ func TestRecordAuthWithOTP(t *testing.T) {
|
||||
"OnRecordDeleteExecute": 1,
|
||||
"OnRecordAfterDeleteSuccess": 1,
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if user.Verified() {
|
||||
t.Fatal("Expected the user to remain unverified because sentTo != email")
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "valid otp with valid password and nonempty sentTo=email (disabled MFA)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/auth-with-otp",
|
||||
Body: strings.NewReader(`{
|
||||
"otpId":"` + strings.Repeat("a", 15) + `",
|
||||
"password":"123456"
|
||||
}`),
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// ensure that the user is unverified
|
||||
user.SetVerified(false)
|
||||
if err = app.Save(user); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// disable MFA
|
||||
user.Collection().MFA.Enabled = false
|
||||
if err = app.Save(user.Collection()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
otp := core.NewOTP(app)
|
||||
otp.Id = strings.Repeat("a", 15)
|
||||
otp.SetCollectionRef(user.Collection().Id)
|
||||
otp.SetRecordRef(user.Id)
|
||||
otp.SetPassword("123456")
|
||||
otp.SetSentTo(user.Email())
|
||||
if err := app.Save(otp); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"token":"`,
|
||||
`"record":{`,
|
||||
`"email":"test@example.com"`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
`"meta":`,
|
||||
// hidden fields
|
||||
`"tokenKey"`,
|
||||
`"password"`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordAuthWithOTPRequest": 1,
|
||||
"OnRecordAuthRequest": 1,
|
||||
"OnRecordEnrich": 1,
|
||||
// ---
|
||||
"OnModelValidate": 2, // +1 because of the verified user update
|
||||
// authOrigin create
|
||||
"OnModelCreate": 1,
|
||||
"OnModelCreateExecute": 1,
|
||||
"OnModelAfterCreateSuccess": 1,
|
||||
// OTP delete
|
||||
"OnModelDelete": 1,
|
||||
"OnModelDeleteExecute": 1,
|
||||
"OnModelAfterDeleteSuccess": 1,
|
||||
// user verified update
|
||||
"OnModelUpdate": 1,
|
||||
"OnModelUpdateExecute": 1,
|
||||
"OnModelAfterUpdateSuccess": 1,
|
||||
// ---
|
||||
"OnRecordValidate": 2,
|
||||
"OnRecordCreate": 1,
|
||||
"OnRecordCreateExecute": 1,
|
||||
"OnRecordAfterCreateSuccess": 1,
|
||||
"OnRecordDelete": 1,
|
||||
"OnRecordDeleteExecute": 1,
|
||||
"OnRecordAfterDeleteSuccess": 1,
|
||||
"OnRecordUpdate": 1,
|
||||
"OnRecordUpdateExecute": 1,
|
||||
"OnRecordAfterUpdateSuccess": 1,
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !user.Verified() {
|
||||
t.Fatal("Expected the user to be marked as verified")
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// rate limit checks
|
||||
@@ -387,11 +494,13 @@ func TestRecordAuthWithOTPManualRateLimiterCheck(t *testing.T) {
|
||||
}{
|
||||
{otpAId, "12345", 400},
|
||||
{otpAId, "12345", 400},
|
||||
{otpAId, "12345", 400},
|
||||
{otpAId, "12345", 400},
|
||||
{otpAId, "123456", 429},
|
||||
{otpBId, "12345", 400},
|
||||
{otpBId, "123456", 200},
|
||||
{otpBId, "12345", 400},
|
||||
{otpBId, "12345", 400},
|
||||
{otpAId, "12345", 429},
|
||||
{otpAId, "123456", 429}, // reject even if it is correct
|
||||
{otpAId, "123456", 429},
|
||||
{otpBId, "123456", 429},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
+39
-12
@@ -1,10 +1,13 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
cryptoRand "crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
@@ -86,10 +89,44 @@ func recordsList(e *core.RequestEvent) error {
|
||||
return firstApiError(err, e.InternalServerError("Failed to enrich records", err))
|
||||
}
|
||||
|
||||
// Add a randomized throttle in case of too many empty search filter attempts.
|
||||
//
|
||||
// This is just for extra precaution since security researches raised concern regarding the possibity of eventual
|
||||
// timing attacks because the List API rule acts also as filter and executes in a single run with the client-side filters.
|
||||
// This is by design and it is an accepted tradeoff between performance, usability and correctness.
|
||||
//
|
||||
// While technically the below doesn't fully guarantee protection against filter timing attacks, in practice combined with the network latency it makes them even less feasible.
|
||||
// A properly configured rate limiter or individual fields Hidden checks are better suited if you are really concerned about eventual information disclosure by side-channel attacks.
|
||||
//
|
||||
// In all cases it doesn't really matter that much because it doesn't affect the builtin PocketBase security sensitive fields (e.g. password and tokenKey) since they
|
||||
// are not client-side filterable and in the few places where they need to be compared against an external value, a constant time check is used.
|
||||
if !e.HasSuperuserAuth() &&
|
||||
(collection.ListRule != nil && *collection.ListRule != "") &&
|
||||
(requestInfo.Query["filter"] != "") &&
|
||||
len(e.Records) == 0 &&
|
||||
checkRateLimit(e.RequestEvent, "@pb_list_timing_check_"+collection.Id, listTimingRateLimitRule) != nil {
|
||||
e.App.Logger().Debug("Randomized throttle because of too many failed searches", "collectionId", collection.Id)
|
||||
randomizedThrottle(100)
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, e.Result)
|
||||
})
|
||||
}
|
||||
|
||||
var listTimingRateLimitRule = core.RateLimitRule{MaxRequests: 3, Duration: 3}
|
||||
|
||||
func randomizedThrottle(softMax int64) {
|
||||
var timeout int64
|
||||
randRange, err := cryptoRand.Int(cryptoRand.Reader, big.NewInt(softMax))
|
||||
if err == nil {
|
||||
timeout = randRange.Int64()
|
||||
} else {
|
||||
timeout = softMax
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(timeout) * time.Millisecond)
|
||||
}
|
||||
|
||||
func recordView(e *core.RequestEvent) error {
|
||||
collection, err := e.App.FindCachedCollectionByNameOrId(e.Request.PathValue("collection"))
|
||||
if err != nil || collection == nil {
|
||||
@@ -169,17 +206,7 @@ func recordCreate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
}
|
||||
|
||||
hasSuperuserAuth := requestInfo.HasSuperuserAuth()
|
||||
canSkipRuleCheck := hasSuperuserAuth
|
||||
|
||||
// special case for the first superuser creation
|
||||
// ---
|
||||
if !canSkipRuleCheck && collection.Name == core.CollectionNameSuperusers {
|
||||
total, totalErr := e.App.CountRecords(core.CollectionNameSuperusers)
|
||||
canSkipRuleCheck = totalErr == nil && total == 0
|
||||
}
|
||||
// ---
|
||||
|
||||
if !canSkipRuleCheck && collection.CreateRule == nil {
|
||||
if !hasSuperuserAuth && collection.CreateRule == nil {
|
||||
return e.ForbiddenError("Only superusers can perform this action.", nil)
|
||||
}
|
||||
|
||||
@@ -212,7 +239,7 @@ func recordCreate(optFinalizer func(data any) error) func(e *core.RequestEvent)
|
||||
form.SetRecord(e.Record)
|
||||
|
||||
// temporary save the record and check it against the create and manage rules
|
||||
if !canSkipRuleCheck && e.Collection.CreateRule != nil {
|
||||
if !hasSuperuserAuth && e.Collection.CreateRule != nil {
|
||||
// temporary grant manager access level
|
||||
form.GrantManagerAccess()
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ func TestRecordCrudAuthOriginCreate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameAuthOrigins + "/records",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
ExpectedContent: []string{
|
||||
@@ -284,7 +284,7 @@ func TestRecordCrudAuthOriginUpdate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameAuthOrigins + "/records/9r2j0m74260ur8i",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
ExpectedContent: []string{
|
||||
|
||||
@@ -219,7 +219,7 @@ func TestRecordCrudExternalAuthCreate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameExternalAuths + "/records",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
ExpectedContent: []string{
|
||||
@@ -286,7 +286,7 @@ func TestRecordCrudExternalAuthUpdate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameExternalAuths + "/records/dlmflokuq1xl342",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
ExpectedContent: []string{
|
||||
|
||||
@@ -214,7 +214,7 @@ func TestRecordCrudMFADelete(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameMFAs + "/records/user1_0",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubMFARecords(app); err != nil {
|
||||
@@ -290,7 +290,7 @@ func TestRecordCrudMFACreate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameMFAs + "/records",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
@@ -371,7 +371,7 @@ func TestRecordCrudMFAUpdate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameMFAs + "/records/user1_0",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
|
||||
@@ -214,7 +214,7 @@ func TestRecordCrudOTPDelete(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameOTPs + "/records/user1_0",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
if err := tests.StubOTPRecords(app); err != nil {
|
||||
@@ -290,7 +290,7 @@ func TestRecordCrudOTPCreate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameOTPs + "/records",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
@@ -371,7 +371,7 @@ func TestRecordCrudOTPUpdate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameOTPs + "/records/user1_0",
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestRecordCrudSuperuserList(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records",
|
||||
Headers: map[string]string{
|
||||
// _superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -93,7 +93,7 @@ func TestRecordCrudSuperuserView(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records/sywbhecnh46rhm0",
|
||||
Headers: map[string]string{
|
||||
// _superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -142,7 +142,7 @@ func TestRecordCrudSuperuserDelete(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records/sbmbsdb40jyxf7h",
|
||||
Headers: map[string]string{
|
||||
// _superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -162,7 +162,7 @@ func TestRecordCrudSuperuserDelete(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records/sywbhecnh46rhm0",
|
||||
Headers: map[string]string{
|
||||
// _superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all other superusers
|
||||
@@ -229,48 +229,13 @@ func TestRecordCrudSuperuserCreate(t *testing.T) {
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "guest creating first superuser",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records",
|
||||
Body: body(),
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
// delete all superusers
|
||||
_, err := app.DB().NewQuery("DELETE FROM {{" + core.CollectionNameSuperusers + "}}").Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
},
|
||||
ExpectedContent: []string{
|
||||
`"collectionName":"_superusers"`,
|
||||
`"verified":true`,
|
||||
},
|
||||
NotExpectedContent: []string{
|
||||
// because the action has no auth the email field shouldn't be returned if emailVisibility is not set
|
||||
`"email"`,
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordCreateRequest": 1,
|
||||
"OnRecordEnrich": 1,
|
||||
"OnModelCreate": 1,
|
||||
"OnModelCreateExecute": 1,
|
||||
"OnModelAfterCreateSuccess": 1,
|
||||
"OnModelValidate": 1,
|
||||
"OnRecordCreate": 1,
|
||||
"OnRecordCreateExecute": 1,
|
||||
"OnRecordAfterCreateSuccess": 1,
|
||||
"OnRecordValidate": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "superusers auth",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records",
|
||||
Headers: map[string]string{
|
||||
// _superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
ExpectedContent: []string{
|
||||
@@ -339,7 +304,7 @@ func TestRecordCrudSuperuserUpdate(t *testing.T) {
|
||||
URL: "/api/collections/" + core.CollectionNameSuperusers + "/records/sywbhecnh46rhm0",
|
||||
Headers: map[string]string{
|
||||
// _superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Body: body(),
|
||||
ExpectedContent: []string{
|
||||
|
||||
+31
-31
@@ -121,7 +121,7 @@ func TestRecordCrudList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/demo1/records",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -145,7 +145,7 @@ func TestRecordCrudList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/demo1/records?filter=text~'test'&sort=-bool",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -167,7 +167,7 @@ func TestRecordCrudList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/demo1/records?filter=invalid~'test'",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -178,7 +178,7 @@ func TestRecordCrudList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/demo1/records?expand=rel_one,rel_many.rel,missing&perPage=2&sort=created",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -420,7 +420,7 @@ func TestRecordCrudList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/nologin/records",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -698,7 +698,7 @@ func TestRecordCrudView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/demo1/records/imy661ixudk5izi",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -738,7 +738,7 @@ func TestRecordCrudView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/demo1/records/al1h9ijdeojtsjy?expand=rel_one,rel_many.rel,missing&perPage=2&sort=created",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -833,7 +833,7 @@ func TestRecordCrudView(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/collections/nologin/records/oos036e9xvqeexy",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1090,7 +1090,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/clients/records/o1y0dd0spd786md",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -1109,7 +1109,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/clients/records/o1y0dd0spd786md",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.OnRecordDeleteRequest().BindFunc(func(e *core.RecordRequestEvent) error {
|
||||
@@ -1197,7 +1197,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/demo3/records/7nwo8tuiatetxdm",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -1223,7 +1223,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/demo3/records/1tmknxy2868d869",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
@@ -1248,7 +1248,7 @@ func TestRecordCrudDelete(t *testing.T) {
|
||||
Method: http.MethodDelete,
|
||||
URL: "/api/collections/users/records/oap640cot4yru2s",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
Delay: 100 * time.Millisecond,
|
||||
ExpectedStatus: 204,
|
||||
@@ -1544,7 +1544,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"rel_many_cascade":"lcl9d87w22ml6jy"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1585,7 +1585,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
Body: formData,
|
||||
Headers: map[string]string{
|
||||
"Content-Type": mp.FormDataContentType(),
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1720,7 +1720,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"title": "test"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1746,7 +1746,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"title": "test"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1772,7 +1772,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"title": "test"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1802,7 +1802,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"title": "test"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -1834,7 +1834,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"passwordConfirm":"1234567890"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1916,7 +1916,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"username":"Users75657"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -1943,7 +1943,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"rel":"invalid"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -2025,7 +2025,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
"verified":true
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -2169,7 +2169,7 @@ func TestRecordCrudCreate(t *testing.T) {
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
col, err := app.FindCollectionByNameOrId("demo3")
|
||||
@@ -2578,7 +2578,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"rel_many_cascade":"lcl9d87w22ml6jy"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -2619,7 +2619,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
Body: formData,
|
||||
Headers: map[string]string{
|
||||
"Content-Type": mp.FormDataContentType(),
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -2726,7 +2726,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"id": "mk5fmymtx4wspra"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -2829,7 +2829,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"verified":false
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -2857,7 +2857,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"rel":"invalid"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -3013,7 +3013,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
"verified":false
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -3251,7 +3251,7 @@ func TestRecordCrudUpdate(t *testing.T) {
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
col, err := app.FindCollectionByNameOrId("demo3")
|
||||
|
||||
@@ -122,7 +122,8 @@ func recordAuthResponse(e *core.RequestEvent, authRecord *core.Record, token str
|
||||
})
|
||||
}
|
||||
|
||||
// wantsMFA checks whether to enable MFA for the specified auth record based on its MFA rule.
|
||||
// wantsMFA checks whether to enable MFA for the specified auth record based on its MFA rule
|
||||
// (note: returns true even in case of an error as a safer default).
|
||||
func wantsMFA(e *core.RequestEvent, record *core.Record) (bool, error) {
|
||||
rule := record.Collection().MFA.Rule
|
||||
if rule == "" {
|
||||
@@ -131,7 +132,7 @@ func wantsMFA(e *core.RequestEvent, record *core.Record) (bool, error) {
|
||||
|
||||
requestInfo, err := e.RequestInfo()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
var exists bool
|
||||
@@ -144,13 +145,13 @@ func wantsMFA(e *core.RequestEvent, record *core.Record) (bool, error) {
|
||||
resolver := core.NewRecordFieldResolver(e.App, record.Collection(), requestInfo, true)
|
||||
expr, err := search.FilterData(rule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
resolver.UpdateQuery(query)
|
||||
|
||||
err = query.AndWhere(expr).Limit(1).Row(&exists)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
@@ -166,11 +167,10 @@ func checkMFA(e *core.RequestEvent, authRecord *core.Record, currentAuthMethod s
|
||||
}
|
||||
|
||||
ok, err := wantsMFA(e, authRecord)
|
||||
if err != nil {
|
||||
return "", e.BadRequestError("Failed to authenticate.", fmt.Errorf("MFA rule failure: %w", err))
|
||||
}
|
||||
if !ok {
|
||||
if err != nil {
|
||||
return "", e.BadRequestError("Failed to authenticate.", fmt.Errorf("MFA rule failure: %w", err))
|
||||
}
|
||||
|
||||
return "", nil // no mfa needed for this auth record
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ func checkMFA(e *core.RequestEvent, authRecord *core.Record, currentAuthMethod s
|
||||
}
|
||||
if err != nil || mfa.HasExpired(authRecord.Collection().MFA.DurationTime()) {
|
||||
deleteMFA()
|
||||
return "", firstApiError(err, e.BadRequestError("Invalid or expired MFA session.", err))
|
||||
return "", e.BadRequestError("Invalid or expired MFA session.", err)
|
||||
}
|
||||
|
||||
if mfa.RecordRef() != authRecord.Id || mfa.CollectionRef() != authRecord.Collection().Id {
|
||||
|
||||
+29
-30
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -26,12 +27,6 @@ type ServeConfig struct {
|
||||
// ShowStartBanner indicates whether to show or hide the server start console message.
|
||||
ShowStartBanner bool
|
||||
|
||||
// DashboardPath specifies the route path to the superusers dashboard interface
|
||||
// (default to "/_/{path...}").
|
||||
//
|
||||
// Note: Must include the "{path...}" wildcard parameter.
|
||||
DashboardPath string
|
||||
|
||||
// HttpAddr is the TCP address to listen for the HTTP server (eg. "127.0.0.1:80").
|
||||
HttpAddr string
|
||||
|
||||
@@ -66,12 +61,6 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
config.AllowedOrigins = []string{"*"}
|
||||
}
|
||||
|
||||
if config.DashboardPath == "" {
|
||||
config.DashboardPath = "/_/{path...}"
|
||||
} else if !strings.HasSuffix(config.DashboardPath, "{path...}") {
|
||||
return errors.New("invalid dashboard path - missing {path...} wildcard")
|
||||
}
|
||||
|
||||
// ensure that the latest migrations are applied before starting the server
|
||||
err := app.RunAllMigrations()
|
||||
if err != nil {
|
||||
@@ -88,11 +77,14 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
AllowMethods: []string{http.MethodGet, http.MethodHead, http.MethodPut, http.MethodPatch, http.MethodPost, http.MethodDelete},
|
||||
}))
|
||||
|
||||
pbRouter.BindFunc(installerRedirect(app, config.DashboardPath))
|
||||
|
||||
pbRouter.GET(config.DashboardPath, Static(ui.DistDirFS, false)).
|
||||
BindFunc(dashboardRemoveInstallerParam()).
|
||||
BindFunc(dashboardCacheControl()).
|
||||
pbRouter.GET("/_/{path...}", Static(ui.DistDirFS, false)).
|
||||
BindFunc(func(e *core.RequestEvent) error {
|
||||
// ingore root path
|
||||
if e.Request.PathValue(StaticWildcardParam) != "" {
|
||||
e.Response.Header().Set("Cache-Control", "max-age=1209600, stale-while-revalidate=86400")
|
||||
}
|
||||
return e.Next()
|
||||
}).
|
||||
Bind(Gzip())
|
||||
|
||||
// start http server
|
||||
@@ -240,18 +232,18 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
return errors.New("The OnServe finalizer wasn't invoked. Did you forget to call the ServeEvent.Next() method?")
|
||||
}
|
||||
|
||||
if config.ShowStartBanner {
|
||||
schema := "http"
|
||||
addr := server.Addr
|
||||
|
||||
if config.HttpsAddr != "" {
|
||||
schema = "https"
|
||||
|
||||
if len(config.CertificateDomains) > 0 {
|
||||
addr = config.CertificateDomains[0]
|
||||
}
|
||||
schema := "http"
|
||||
addr := server.Addr
|
||||
if config.HttpsAddr != "" {
|
||||
schema = "https"
|
||||
if len(config.CertificateDomains) > 0 {
|
||||
addr = config.CertificateDomains[0]
|
||||
}
|
||||
}
|
||||
baseURL := fmt.Sprintf("%s://%s", schema, addr)
|
||||
dashboardURL := fmt.Sprintf("%s/_", baseURL)
|
||||
|
||||
if config.ShowStartBanner {
|
||||
date := new(strings.Builder)
|
||||
log.New(date, "", log.LstdFlags).Print()
|
||||
|
||||
@@ -259,14 +251,21 @@ func Serve(app core.App, config ServeConfig) error {
|
||||
bold.Printf(
|
||||
"%s Server started at %s\n",
|
||||
strings.TrimSpace(date.String()),
|
||||
color.CyanString("%s://%s", schema, addr),
|
||||
color.CyanString("%s", baseURL),
|
||||
)
|
||||
|
||||
regular := color.New()
|
||||
regular.Printf("├─ REST API: %s\n", color.CyanString("%s://%s/api/", schema, addr))
|
||||
regular.Printf("└─ Admin UI: %s\n", color.CyanString("%s://%s/_/", schema, addr))
|
||||
regular.Printf("├─ REST API: %s\n", color.CyanString("%s/api/", baseURL))
|
||||
regular.Printf("└─ Dashboard: %s\n", color.CyanString("%s/", dashboardURL))
|
||||
}
|
||||
|
||||
go func() {
|
||||
installerErr := loadInstaller(app, dashboardURL)
|
||||
if installerErr != nil {
|
||||
app.Logger().Warn("Failed to initialize installer", "error", installerErr)
|
||||
}
|
||||
}()
|
||||
|
||||
var serveErr error
|
||||
if config.HttpsAddr != "" {
|
||||
if config.HttpAddr != "" {
|
||||
|
||||
+17
-17
@@ -42,7 +42,7 @@ func TestSettingsList(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/settings",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -102,7 +102,7 @@ func TestSettingsSet(t *testing.T) {
|
||||
URL: "/api/settings",
|
||||
Body: strings.NewReader(``),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -129,7 +129,7 @@ func TestSettingsSet(t *testing.T) {
|
||||
URL: "/api/settings",
|
||||
Body: strings.NewReader(`{"meta":{"appName":""}}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -150,7 +150,7 @@ func TestSettingsSet(t *testing.T) {
|
||||
URL: "/api/settings",
|
||||
Body: strings.NewReader(validData),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
@@ -211,7 +211,7 @@ func TestSettingsTestS3(t *testing.T) {
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/settings/test/s3",
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -226,7 +226,7 @@ func TestSettingsTestS3(t *testing.T) {
|
||||
URL: "/api/settings/test/s3",
|
||||
Body: strings.NewReader(`{"filesystem":"invalid"}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -241,7 +241,7 @@ func TestSettingsTestS3(t *testing.T) {
|
||||
URL: "/api/settings/test/s3",
|
||||
Body: strings.NewReader(`{"filesystem":"storage"}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -293,7 +293,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
URL: "/api/settings/test/email",
|
||||
Body: strings.NewReader(`{`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -305,7 +305,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
URL: "/api/settings/test/email",
|
||||
Body: strings.NewReader(`{}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -323,7 +323,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
"email": "test@example.com"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend() != 1 {
|
||||
@@ -359,7 +359,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
"email": "test@example.com"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend() != 1 {
|
||||
@@ -395,7 +395,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
"email": "test@example.com"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend() != 1 {
|
||||
@@ -431,7 +431,7 @@ func TestSettingsTestEmail(t *testing.T) {
|
||||
"email": "test@example.com"
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.TestMailer.TotalSend() != 1 {
|
||||
@@ -511,7 +511,7 @@ func TestGenerateAppleClientSecret(t *testing.T) {
|
||||
URL: "/api/settings/apple/generate-client-secret",
|
||||
Body: strings.NewReader(`{`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
@@ -523,7 +523,7 @@ func TestGenerateAppleClientSecret(t *testing.T) {
|
||||
URL: "/api/settings/apple/generate-client-secret",
|
||||
Body: strings.NewReader(`{}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -547,7 +547,7 @@ func TestGenerateAppleClientSecret(t *testing.T) {
|
||||
"duration": -1
|
||||
}`),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
@@ -571,7 +571,7 @@ func TestGenerateAppleClientSecret(t *testing.T) {
|
||||
"duration": 1
|
||||
}`, privatePem)),
|
||||
Headers: map[string]string{
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiY18zMzIzODY2MzM5IiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.v_bMAygr6hXPwD2DpPrFpNQ7dd68Q3pGstmYAsvNBJg",
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
|
||||
@@ -15,7 +15,6 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||
var allowedOrigins []string
|
||||
var httpAddr string
|
||||
var httpsAddr string
|
||||
var dashboardPath string
|
||||
|
||||
command := &cobra.Command{
|
||||
Use: "serve [domain(s)]",
|
||||
@@ -40,7 +39,6 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||
err := apis.Serve(app, apis.ServeConfig{
|
||||
HttpAddr: httpAddr,
|
||||
HttpsAddr: httpsAddr,
|
||||
DashboardPath: dashboardPath,
|
||||
ShowStartBanner: showStartBanner,
|
||||
AllowedOrigins: allowedOrigins,
|
||||
CertificateDomains: args,
|
||||
@@ -75,12 +73,5 @@ func NewServeCommand(app core.App, showStartBanner bool) *cobra.Command {
|
||||
"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",
|
||||
)
|
||||
|
||||
command.PersistentFlags().StringVar(
|
||||
&dashboardPath,
|
||||
"dashboard",
|
||||
"/_/{path...}",
|
||||
"The route path to the superusers dashboard; must include the '{path...}' wildcard parameter",
|
||||
)
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
+51
-6
@@ -7,21 +7,23 @@ import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-ozzo/ozzo-validation/v4/is"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewSuperuserCommand creates and returns new command for managing
|
||||
// superuser accounts (create, update, delete).
|
||||
// superuser accounts (create, update, upsert, delete).
|
||||
func NewSuperuserCommand(app core.App) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "superuser",
|
||||
Short: "Manages superuser accounts",
|
||||
Short: "Manage superusers",
|
||||
}
|
||||
|
||||
command.AddCommand(superuserUpsertCommand(app))
|
||||
command.AddCommand(superuserCreateCommand(app))
|
||||
command.AddCommand(superuserUpdateCommand(app))
|
||||
command.AddCommand(superuserDeleteCommand(app))
|
||||
command.AddCommand(superuserOTPCommand(app))
|
||||
|
||||
return command
|
||||
}
|
||||
@@ -30,7 +32,7 @@ func superuserUpsertCommand(app core.App) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "upsert",
|
||||
Example: "superuser upsert test@example.com 1234567890",
|
||||
Short: "Creates, or updates if email exists, a single superuser account",
|
||||
Short: "Creates, or updates if email exists, a single superuser",
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
@@ -70,7 +72,7 @@ func superuserCreateCommand(app core.App) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "create",
|
||||
Example: "superuser create test@example.com 1234567890",
|
||||
Short: "Creates a new superuser account",
|
||||
Short: "Creates a new superuser",
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
@@ -106,7 +108,7 @@ func superuserUpdateCommand(app core.App) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "update",
|
||||
Example: "superuser update test@example.com 1234567890",
|
||||
Short: "Changes the password of a single superuser account",
|
||||
Short: "Changes the password of a single superuser",
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
@@ -140,7 +142,7 @@ func superuserDeleteCommand(app core.App) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "delete",
|
||||
Example: "superuser delete test@example.com",
|
||||
Short: "Deletes an existing superuser account",
|
||||
Short: "Deletes an existing superuser",
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) == 0 || args[0] == "" || is.EmailFormat.Validate(args[0]) != nil {
|
||||
@@ -164,3 +166,46 @@ func superuserDeleteCommand(app core.App) *cobra.Command {
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func superuserOTPCommand(app core.App) *cobra.Command {
|
||||
command := &cobra.Command{
|
||||
Use: "otp",
|
||||
Example: "superuser otp test@example.com",
|
||||
Short: "Creates a new OTP for the specified superuser",
|
||||
SilenceUsage: true,
|
||||
RunE: func(command *cobra.Command, args []string) error {
|
||||
if len(args) == 0 || args[0] == "" || is.EmailFormat.Validate(args[0]) != nil {
|
||||
return errors.New("Invalid or missing email address.")
|
||||
}
|
||||
|
||||
superuser, err := app.FindAuthRecordByEmail(core.CollectionNameSuperusers, args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("Superuser with email %q doesn't exist.", args[0])
|
||||
}
|
||||
|
||||
if !superuser.Collection().OTP.Enabled {
|
||||
return errors.New("OTP is not enabled for the _superusers collection.")
|
||||
}
|
||||
|
||||
pass := security.RandomStringWithAlphabet(superuser.Collection().OTP.Length, "1234567890")
|
||||
|
||||
otp := core.NewOTP(app)
|
||||
otp.SetCollectionRef(superuser.Collection().Id)
|
||||
otp.SetRecordRef(superuser.Id)
|
||||
otp.SetPassword(pass)
|
||||
|
||||
err = app.Save(otp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create OTP: %w", err)
|
||||
}
|
||||
|
||||
color.New(color.BgGreen, color.FgBlack).Printf("Successfully created OTP for superuser %q:", superuser.Email())
|
||||
color.Green("\n├─ Id: %s", otp.Id)
|
||||
color.Green("├─ Pass: %s", pass)
|
||||
color.Green("└─ Valid: %ds\n\n", superuser.Collection().OTP.Duration)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -308,3 +308,96 @@ func TestSuperuserDeleteCommand(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuperuserOTPCommand(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
superusersCollection, err := app.FindCollectionByNameOrId(core.CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// remove all existing otps
|
||||
otps, err := app.FindAllOTPsByCollection(superusersCollection)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, otp := range otps {
|
||||
err = app.Delete(otp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
email string
|
||||
enabled bool
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
"empty email",
|
||||
"",
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid email",
|
||||
"invalid",
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"nonexisting superuser",
|
||||
"test_missing@example.com",
|
||||
true,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"existing superuser",
|
||||
"test@example.com",
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"existing superuser with disabled OTP",
|
||||
"test@example.com",
|
||||
false,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
command := cmd.NewSuperuserCommand(app)
|
||||
command.SetArgs([]string{"otp", s.email})
|
||||
|
||||
superusersCollection.OTP.Enabled = s.enabled
|
||||
if err = app.SaveNoValidate(superusersCollection); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := command.Execute()
|
||||
|
||||
hasErr := err != nil
|
||||
if s.expectError != hasErr {
|
||||
t.Fatalf("Expected hasErr %v, got %v (%v)", s.expectError, hasErr, err)
|
||||
}
|
||||
|
||||
if hasErr {
|
||||
return
|
||||
}
|
||||
|
||||
superuser, err := app.FindAuthRecordByEmail(superusersCollection, s.email)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
otps, _ := app.FindAllOTPsByRecord(superuser)
|
||||
if total := len(otps); total != 1 {
|
||||
t.Fatalf("Expected 1 OTP, got %d", total)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1361,6 +1361,9 @@ type App interface {
|
||||
// OnRecordRequestOTPRequest hook is triggered on each Record
|
||||
// request OTP API request.
|
||||
//
|
||||
// [RecordCreateOTPRequestEvent.Record] could be nil if no matching identity is found, allowing
|
||||
// you to manually create or locate a different Record model (by reassigning [RecordCreateOTPRequestEvent.Record]).
|
||||
//
|
||||
// If the optional "tags" list (Collection ids or names) is specified,
|
||||
// then all event handlers registered via the created hook will be
|
||||
// triggered and called only if their event data origin matches the tags.
|
||||
|
||||
+12
-2
@@ -1238,8 +1238,18 @@ func (app *BaseApp) registerBaseHooks() {
|
||||
Priority: 999,
|
||||
})
|
||||
|
||||
app.Cron().Add("__pbPragmaOptimize__", "0 0 * * *", func() {
|
||||
_, execErr := app.DB().NewQuery("PRAGMA optimize").Execute()
|
||||
app.Cron().Add("__pbDBOptimize__", "0 0 * * *", func() {
|
||||
_, execErr := app.NonconcurrentDB().NewQuery("PRAGMA wal_checkpoint(TRUNCATE)").Execute()
|
||||
if execErr != nil {
|
||||
app.Logger().Warn("Failed to run periodic PRAGMA wal_checkpoint for the main DB", slog.String("error", execErr.Error()))
|
||||
}
|
||||
|
||||
_, execErr = app.AuxNonconcurrentDB().NewQuery("PRAGMA wal_checkpoint(TRUNCATE)").Execute()
|
||||
if execErr != nil {
|
||||
app.Logger().Warn("Failed to run periodic PRAGMA wal_checkpoint for the auxiliary DB", slog.String("error", execErr.Error()))
|
||||
}
|
||||
|
||||
_, execErr = app.DB().NewQuery("PRAGMA optimize").Execute()
|
||||
if execErr != nil {
|
||||
app.Logger().Warn("Failed to run periodic PRAGMA optimize", slog.String("error", execErr.Error()))
|
||||
}
|
||||
|
||||
@@ -76,6 +76,11 @@ func (app *BaseApp) CreateBackup(ctx context.Context, name string) error {
|
||||
tempPath := filepath.Join(localTempDir, "pb_backup_"+security.PseudorandomString(6))
|
||||
createErr := e.App.RunInTransaction(func(txApp App) error {
|
||||
return txApp.AuxRunInTransaction(func(txApp App) error {
|
||||
// run manual checkpoint and truncate the WAL files
|
||||
// (errors are ignored because it is not that important and the PRAGMA may not be supported by the used driver)
|
||||
txApp.DB().NewQuery("PRAGMA wal_checkpoint(TRUNCATE)").Execute()
|
||||
txApp.AuxDB().NewQuery("PRAGMA wal_checkpoint(TRUNCATE)").Execute()
|
||||
|
||||
return archive.Create(txApp.DataDir(), tempPath, e.Exclude...)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ package core_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/logger"
|
||||
@@ -351,6 +353,12 @@ func TestBaseAppRefreshSettingsLoggerMinLevelEnabled(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// silence query logs
|
||||
app.DB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||
app.DB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||
app.NonconcurrentDB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||
app.NonconcurrentDB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||
|
||||
handler, ok := app.Logger().Handler().(*logger.BatchHandler)
|
||||
if !ok {
|
||||
t.Fatalf("Expected BatchHandler, got %v", app.Logger().Handler())
|
||||
|
||||
@@ -86,6 +86,12 @@ func (app *BaseApp) ImportCollections(toImport []map[string]any, deleteMissing b
|
||||
continue
|
||||
}
|
||||
if imported.Fields.GetById(f.GetId()) == nil {
|
||||
// replace with the existing id to prevent accidental column deletion
|
||||
// since otherwise the imported field will be treated as a new one
|
||||
found := imported.Fields.GetByName(f.GetName())
|
||||
if found != nil && found.Type() == f.Type() {
|
||||
found.SetId(f.GetId())
|
||||
}
|
||||
imported.Fields.Add(f)
|
||||
}
|
||||
}
|
||||
|
||||
+69
-29
@@ -3,8 +3,10 @@ package core
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/tools/dbutils"
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
@@ -309,6 +311,7 @@ type baseCollection struct {
|
||||
BaseModel
|
||||
|
||||
disableIntegrityChecks bool
|
||||
autogeneratedId string
|
||||
|
||||
ListRule *string `db:"listRule" json:"listRule" form:"listRule"`
|
||||
ViewRule *string `db:"viewRule" json:"viewRule" form:"viewRule"`
|
||||
@@ -364,6 +367,7 @@ func NewBaseCollection(name string, optId ...string) *Collection {
|
||||
m.Name = name
|
||||
m.Type = CollectionTypeBase
|
||||
|
||||
// @todo consider removing once inferred composite literals are supported
|
||||
if len(optId) > 0 {
|
||||
m.Id = optId[0]
|
||||
}
|
||||
@@ -383,6 +387,7 @@ func NewViewCollection(name string, optId ...string) *Collection {
|
||||
m.Name = name
|
||||
m.Type = CollectionTypeView
|
||||
|
||||
// @todo consider removing once inferred composite literals are supported
|
||||
if len(optId) > 0 {
|
||||
m.Id = optId[0]
|
||||
}
|
||||
@@ -402,6 +407,7 @@ func NewAuthCollection(name string, optId ...string) *Collection {
|
||||
m.Name = name
|
||||
m.Type = CollectionTypeAuth
|
||||
|
||||
// @todo consider removing once inferred composite literals are supported
|
||||
if len(optId) > 0 {
|
||||
m.Id = optId[0]
|
||||
}
|
||||
@@ -482,12 +488,13 @@ func (m *Collection) UnmarshalJSON(b []byte) error {
|
||||
minimal := &struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Id string `json:"id"`
|
||||
}{}
|
||||
if err := json.Unmarshal(b, minimal); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
blank := NewCollection(minimal.Type, minimal.Name)
|
||||
blank := NewCollection(minimal.Type, minimal.Name, minimal.Id)
|
||||
*m = *blank
|
||||
}
|
||||
|
||||
@@ -689,49 +696,82 @@ func onCollectionDeleteExecute(e *CollectionEvent) error {
|
||||
// save hook
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
func (c *Collection) initDefaultId() {
|
||||
if c.Id != "" {
|
||||
return // already set
|
||||
}
|
||||
func (c *Collection) idChecksum() string {
|
||||
return "pbc_" + crc32Checksum(c.Type+c.Name)
|
||||
}
|
||||
|
||||
if c.System && c.Name != "" {
|
||||
// for system collections we use crc32 checksum for consistency because they cannot be renamed
|
||||
c.Id = "pbc_" + crc32Checksum(c.Name)
|
||||
} else {
|
||||
c.Id = "pbc_" + security.RandomStringWithAlphabet(11, DefaultIdAlphabet)
|
||||
func (c *Collection) initDefaultId() {
|
||||
if c.Id == "" {
|
||||
c.Id = c.idChecksum()
|
||||
c.autogeneratedId = c.Id
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Collection) savePrepare() error {
|
||||
if c.Type == "" {
|
||||
c.Type = CollectionTypeBase
|
||||
func (c *Collection) updateGeneratedIdIfExists(app App) {
|
||||
if !c.IsNew() ||
|
||||
// the id was explicitly cleared
|
||||
c.Id == "" ||
|
||||
// the id was manually set
|
||||
c.Id != c.autogeneratedId {
|
||||
return
|
||||
}
|
||||
|
||||
if c.IsNew() {
|
||||
c.initDefaultId()
|
||||
c.Created = types.NowDateTime()
|
||||
// generate an up-to-date checksum
|
||||
newId := c.idChecksum()
|
||||
|
||||
// add a number to the current id (if already exists)
|
||||
for i := 2; i < 1000; i++ {
|
||||
var exists bool
|
||||
_ = app.CollectionQuery().Select("(1)").AndWhere(dbx.HashExp{"id": newId}).Limit(1).Row(&exists)
|
||||
if !exists {
|
||||
break
|
||||
}
|
||||
newId = c.idChecksum() + strconv.Itoa(i)
|
||||
}
|
||||
|
||||
c.Updated = types.NowDateTime()
|
||||
|
||||
// recreate the fields list to ensure that all normalizations
|
||||
// like default field id are applied
|
||||
c.Fields = NewFieldsList(c.Fields...)
|
||||
|
||||
c.initDefaultFields()
|
||||
|
||||
if c.IsAuth() {
|
||||
c.unsetMissingOAuth2MappedFields()
|
||||
// no change
|
||||
if c.Id == newId {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
// replace the old id in the index names (if any)
|
||||
for i, idx := range c.Indexes {
|
||||
parsed := dbutils.ParseIndex(idx)
|
||||
original := parsed.IndexName
|
||||
parsed.IndexName = strings.ReplaceAll(parsed.IndexName, c.Id, newId)
|
||||
if parsed.IndexName != original {
|
||||
c.Indexes[i] = parsed.Build()
|
||||
}
|
||||
}
|
||||
|
||||
// update model id
|
||||
c.Id = newId
|
||||
}
|
||||
|
||||
func onCollectionSave(e *CollectionEvent) error {
|
||||
if err := e.Collection.savePrepare(); err != nil {
|
||||
return err
|
||||
if e.Collection.Type == "" {
|
||||
e.Collection.Type = CollectionTypeBase
|
||||
}
|
||||
|
||||
if e.Collection.IsNew() {
|
||||
e.Collection.initDefaultId()
|
||||
e.Collection.Created = types.NowDateTime()
|
||||
}
|
||||
|
||||
e.Collection.Updated = types.NowDateTime()
|
||||
|
||||
// recreate the fields list to ensure that all normalizations
|
||||
// like default field id are applied
|
||||
e.Collection.Fields = NewFieldsList(e.Collection.Fields...)
|
||||
|
||||
e.Collection.initDefaultFields()
|
||||
|
||||
if e.Collection.IsAuth() {
|
||||
e.Collection.unsetMissingOAuth2MappedFields()
|
||||
}
|
||||
|
||||
e.Collection.updateGeneratedIdIfExists(e.App)
|
||||
|
||||
return e.Next()
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@ type optionsValidator interface {
|
||||
}
|
||||
|
||||
func (validator *collectionValidator) run() error {
|
||||
if validator.original.IsNew() {
|
||||
validator.new.updateGeneratedIdIfExists(validator.app)
|
||||
}
|
||||
|
||||
// generate fields from the query (overwriting any explicit user defined fields)
|
||||
if validator.new.IsView() {
|
||||
validator.new.Fields, _ = validator.app.CreateViewFields(validator.new.ViewQuery)
|
||||
@@ -604,6 +608,12 @@ func (cv *collectionValidator) checkIndexes(value any) error {
|
||||
continue
|
||||
}
|
||||
|
||||
// reset collate and sort since they are not important for the unique constraint
|
||||
for i := range oldParsed.Columns {
|
||||
oldParsed.Columns[i].Collate = ""
|
||||
oldParsed.Columns[i].Sort = ""
|
||||
}
|
||||
|
||||
oldParsedStr := oldParsed.Build()
|
||||
|
||||
for _, column := range oldParsed.Columns {
|
||||
@@ -617,19 +627,29 @@ func (cv *collectionValidator) checkIndexes(value any) error {
|
||||
newParsed := dbutils.ParseIndex(newIndex)
|
||||
|
||||
// exclude the non-important identifiers from the check
|
||||
newParsed.SchemaName = oldParsed.SchemaName
|
||||
newParsed.IndexName = oldParsed.IndexName
|
||||
newParsed.TableName = oldParsed.TableName
|
||||
|
||||
// exclude partial constraints
|
||||
newParsed.Where = oldParsed.Where
|
||||
|
||||
// reset collate and sort
|
||||
for i := range newParsed.Columns {
|
||||
newParsed.Columns[i].Collate = ""
|
||||
newParsed.Columns[i].Sort = ""
|
||||
}
|
||||
|
||||
if oldParsedStr == newParsed.Build() {
|
||||
hasMatch = true
|
||||
continue
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasMatch {
|
||||
return validation.NewError(
|
||||
"validation_unique_system_field_index_change",
|
||||
fmt.Sprintf("Unique index definition on system fields (%q) cannot be changed.", f.GetName()),
|
||||
"validation_invalid_unique_system_field_index",
|
||||
fmt.Sprintf("Unique index definition on system fields (%q) is invalid or missing.", f.GetName()),
|
||||
).SetParams(map[string]any{"fieldName": f.GetName()})
|
||||
}
|
||||
|
||||
|
||||
@@ -550,7 +550,7 @@ func TestCollectionValidate(t *testing.T) {
|
||||
expectedErrors: []string{"indexes"},
|
||||
},
|
||||
{
|
||||
name: "changing index on system field",
|
||||
name: "changing partial constraint of existing index on system field",
|
||||
collection: func(app core.App) (*core.Collection, error) {
|
||||
demo2, err := app.FindCollectionByNameOrId("demo2")
|
||||
if err != nil {
|
||||
@@ -571,7 +571,91 @@ func TestCollectionValidate(t *testing.T) {
|
||||
|
||||
// replace the index with a partial one
|
||||
demo2.RemoveIndex("idx_unique_demo2_title")
|
||||
demo2.AddIndex("idx_unique_demo2_title", true, "title", "1 = 1")
|
||||
demo2.AddIndex("idx_new_demo2_title", true, "title", "1 = 1")
|
||||
|
||||
return demo2, nil
|
||||
},
|
||||
expectedErrors: []string{},
|
||||
},
|
||||
{
|
||||
name: "changing column sort and collate of existing index on system field",
|
||||
collection: func(app core.App) (*core.Collection, error) {
|
||||
demo2, err := app.FindCollectionByNameOrId("demo2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// mark the title field as system
|
||||
demo2.Fields.GetByName("title").SetSystem(true)
|
||||
if err = app.Save(demo2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// refresh
|
||||
demo2, err = app.FindCollectionByNameOrId("demo2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// replace the index with a new one for the same column but with collate and sort
|
||||
demo2.RemoveIndex("idx_unique_demo2_title")
|
||||
demo2.AddIndex("idx_new_demo2_title", true, "title COLLATE test ASC", "")
|
||||
|
||||
return demo2, nil
|
||||
},
|
||||
expectedErrors: []string{},
|
||||
},
|
||||
{
|
||||
name: "adding new column to index on system field",
|
||||
collection: func(app core.App) (*core.Collection, error) {
|
||||
demo2, err := app.FindCollectionByNameOrId("demo2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// mark the title field as system
|
||||
demo2.Fields.GetByName("title").SetSystem(true)
|
||||
if err = app.Save(demo2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// refresh
|
||||
demo2, err = app.FindCollectionByNameOrId("demo2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// replace the index with a non-unique one
|
||||
demo2.RemoveIndex("idx_unique_demo2_title")
|
||||
demo2.AddIndex("idx_new_title", false, "title, id", "")
|
||||
|
||||
return demo2, nil
|
||||
},
|
||||
expectedErrors: []string{"indexes"},
|
||||
},
|
||||
{
|
||||
name: "changing index type on system field",
|
||||
collection: func(app core.App) (*core.Collection, error) {
|
||||
demo2, err := app.FindCollectionByNameOrId("demo2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// mark the title field as system
|
||||
demo2.Fields.GetByName("title").SetSystem(true)
|
||||
if err = app.Save(demo2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// refresh
|
||||
demo2, err = app.FindCollectionByNameOrId("demo2")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// replace the index with a non-unique one (partial constraints are ignored)
|
||||
demo2.RemoveIndex("idx_unique_demo2_title")
|
||||
demo2.AddIndex("idx_new_title", false, "title", "1=1")
|
||||
|
||||
return demo2, nil
|
||||
},
|
||||
|
||||
+10
-8
@@ -46,14 +46,16 @@ func baseLockRetry(op func(attempt int) error, maxRetries int) error {
|
||||
Retry:
|
||||
err := op(attempt)
|
||||
|
||||
if err != nil &&
|
||||
attempt <= maxRetries &&
|
||||
// we are checking the plain error text to handle both cgo and noncgo errors
|
||||
strings.Contains(err.Error(), "database is locked") {
|
||||
// wait and retry
|
||||
time.Sleep(getDefaultRetryInterval(attempt))
|
||||
attempt++
|
||||
goto Retry
|
||||
if err != nil && attempt <= maxRetries {
|
||||
errStr := err.Error()
|
||||
// we are checking the error against the plain error texts since the codes could vary between drivers
|
||||
if strings.Contains(errStr, "database is locked") ||
|
||||
strings.Contains(errStr, "table is locked") {
|
||||
// wait and retry
|
||||
time.Sleep(getDefaultRetryInterval(attempt))
|
||||
attempt++
|
||||
goto Retry
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
|
||||
@@ -33,6 +33,7 @@ func TestBaseLockRetry(t *testing.T) {
|
||||
{nil, 3, 1},
|
||||
{errors.New("test"), 3, 1},
|
||||
{errors.New("database is locked"), 3, 3},
|
||||
{errors.New("table is locked"), 3, 3},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
|
||||
+2
-2
@@ -191,7 +191,7 @@ func DefaultFieldIdValidationRule(value any) error {
|
||||
|
||||
rules := []validation.Rule{
|
||||
validation.Required,
|
||||
validation.Length(1, 255),
|
||||
validation.Length(1, 100),
|
||||
}
|
||||
|
||||
for _, r := range rules {
|
||||
@@ -217,7 +217,7 @@ func DefaultFieldNameValidationRule(value any) error {
|
||||
|
||||
rules := []validation.Rule{
|
||||
validation.Required,
|
||||
validation.Length(1, 255),
|
||||
validation.Length(1, 100),
|
||||
validation.Match(fieldNameRegex),
|
||||
validation.NotIn(excludeNames...),
|
||||
validation.By(checkForVia),
|
||||
|
||||
+3
-3
@@ -739,14 +739,14 @@ func (f *FileField) toSliceValue(raw any) []any {
|
||||
}
|
||||
|
||||
func (f *FileField) uniqueFiles(files []any) []any {
|
||||
existing := make(map[string]struct{}, len(files))
|
||||
found := make(map[string]struct{}, len(files))
|
||||
result := make([]any, 0, len(files))
|
||||
|
||||
for _, fv := range files {
|
||||
name := f.getFileName(fv)
|
||||
if _, ok := existing[name]; !ok {
|
||||
if _, ok := found[name]; !ok {
|
||||
result = append(result, fv)
|
||||
existing[name] = struct{}{}
|
||||
found[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -91,7 +91,7 @@ func testDefaultFieldIdValidation(t *testing.T, fieldType string) {
|
||||
"invalid length",
|
||||
func() core.Field {
|
||||
f := core.Fields[fieldType]()
|
||||
f.SetId(strings.Repeat("a", 256))
|
||||
f.SetId(strings.Repeat("a", 101))
|
||||
return f
|
||||
},
|
||||
true,
|
||||
@@ -100,7 +100,7 @@ func testDefaultFieldIdValidation(t *testing.T, fieldType string) {
|
||||
"valid length",
|
||||
func() core.Field {
|
||||
f := core.Fields[fieldType]()
|
||||
f.SetId(strings.Repeat("a", 255))
|
||||
f.SetId(strings.Repeat("a", 100))
|
||||
return f
|
||||
},
|
||||
false,
|
||||
@@ -142,7 +142,7 @@ func testDefaultFieldNameValidation(t *testing.T, fieldType string) {
|
||||
"invalid length",
|
||||
func() core.Field {
|
||||
f := core.Fields[fieldType]()
|
||||
f.SetName(strings.Repeat("a", 256))
|
||||
f.SetName(strings.Repeat("a", 101))
|
||||
return f
|
||||
},
|
||||
true,
|
||||
@@ -151,7 +151,7 @@ func testDefaultFieldNameValidation(t *testing.T, fieldType string) {
|
||||
"valid length",
|
||||
func() core.Field {
|
||||
f := core.Fields[fieldType]()
|
||||
f.SetName(strings.Repeat("a", 255))
|
||||
f.SetName(strings.Repeat("a", 100))
|
||||
return f
|
||||
},
|
||||
false,
|
||||
|
||||
+10
-8
@@ -4,8 +4,7 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/security"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// NewFieldsList creates a new FieldsList instance with the provided fields.
|
||||
@@ -171,12 +170,16 @@ func (l *FieldsList) add(newField Field) {
|
||||
// set default id
|
||||
if newFieldId == "" {
|
||||
replaceByName = true
|
||||
if newField.GetSystem() && newField.GetName() != "" {
|
||||
// for system fields we use crc32 checksum for consistency because they cannot be renamed
|
||||
newFieldId = newField.Type() + crc32Checksum(newField.GetName())
|
||||
} else {
|
||||
newFieldId = newField.Type() + security.RandomString(7)
|
||||
|
||||
baseId := newField.Type() + crc32Checksum(newField.GetName())
|
||||
newFieldId = baseId
|
||||
for i := 2; i < 1000; i++ {
|
||||
if l.GetById(newFieldId) == nil {
|
||||
break // already unique
|
||||
}
|
||||
newFieldId = baseId + strconv.Itoa(i)
|
||||
}
|
||||
newField.SetId(newFieldId)
|
||||
}
|
||||
|
||||
// replace existing
|
||||
@@ -196,7 +199,6 @@ func (l *FieldsList) add(newField Field) {
|
||||
}
|
||||
|
||||
// add new field
|
||||
newField.SetId(newFieldId)
|
||||
*l = append(fields, newField)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,13 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log/slog"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/tools/list"
|
||||
"github.com/pocketbase/pocketbase/tools/logger"
|
||||
)
|
||||
@@ -51,6 +54,12 @@ func TestBaseAppLoggerLevelDevPrint(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// silence query logs
|
||||
app.DB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||
app.DB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||
app.NonconcurrentDB().(*dbx.DB).ExecLogFunc = func(ctx context.Context, t time.Duration, sql string, result sql.Result, err error) {}
|
||||
app.NonconcurrentDB().(*dbx.DB).QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {}
|
||||
|
||||
app.Settings().Logs.MinLevel = testLogLevel
|
||||
if err := app.Save(app.Settings()); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -85,6 +85,20 @@ func (m *OTP) SetRecordRef(recordId string) {
|
||||
m.Set("recordRef", recordId)
|
||||
}
|
||||
|
||||
// SentTo returns the "sentTo" record field value.
|
||||
//
|
||||
// It could be any string value (email, phone, message app id, etc.)
|
||||
// and usually is used as part of the auth flow to update the verified
|
||||
// user state in case for example the sentTo value matches with the user record email.
|
||||
func (m *OTP) SentTo() string {
|
||||
return m.GetString("sentTo")
|
||||
}
|
||||
|
||||
// SetSentTo updates the "sentTo" record field value.
|
||||
func (m *OTP) SetSentTo(val string) {
|
||||
m.Set("sentTo", val)
|
||||
}
|
||||
|
||||
// Created returns the "created" record field value.
|
||||
func (m *OTP) Created() types.DateTime {
|
||||
return m.GetDateTime("created")
|
||||
|
||||
@@ -85,6 +85,30 @@ func TestOTPCollectionRef(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestOTPSentTo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
otp := core.NewOTP(app)
|
||||
|
||||
testValues := []string{"test_1", "test2", ""}
|
||||
for i, testValue := range testValues {
|
||||
t.Run(fmt.Sprintf("%d_%q", i, testValue), func(t *testing.T) {
|
||||
otp.SetSentTo(testValue)
|
||||
|
||||
if v := otp.SentTo(); v != testValue {
|
||||
t.Fatalf("Expected getter %q, got %q", testValue, v)
|
||||
}
|
||||
|
||||
if v := otp.GetString("sentTo"); v != testValue {
|
||||
t.Fatalf("Expected field value %q, got %q", testValue, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOTPCreated(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/hook"
|
||||
@@ -9,6 +11,10 @@ import (
|
||||
|
||||
const CollectionNameSuperusers = "_superusers"
|
||||
|
||||
// DefaultInstallerEmail is the default superuser email address
|
||||
// for the initial autogenerated superuser account.
|
||||
const DefaultInstallerEmail = "__pbinstaller@example.com"
|
||||
|
||||
func (app *BaseApp) registerSuperuserHooks() {
|
||||
app.OnRecordDelete(CollectionNameSuperusers).Bind(&hook.Handler[*RecordEvent]{
|
||||
Id: "pbSuperusersRecordDelete",
|
||||
@@ -39,13 +45,45 @@ func (app *BaseApp) registerSuperuserHooks() {
|
||||
Id: "pbSuperusersRecordSaveExec",
|
||||
Func: func(e *RecordEvent) error {
|
||||
e.Record.SetVerified(true) // always mark superusers as verified
|
||||
return e.Next()
|
||||
|
||||
if err := e.Next(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ensure that the installer superuser is deleted
|
||||
if e.Type == ModelEventTypeCreate && e.Record.Email() != DefaultInstallerEmail {
|
||||
record, err := app.FindAuthRecordByEmail(CollectionNameSuperusers, DefaultInstallerEmail)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// already deleted
|
||||
} else if err != nil {
|
||||
e.App.Logger().Warn("Failed to fetch installer superuser", "error", err)
|
||||
} else {
|
||||
err = e.App.Delete(record)
|
||||
if err != nil {
|
||||
e.App.Logger().Warn("Failed to delete installer superuser", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Priority: -99,
|
||||
}
|
||||
app.OnRecordCreateExecute(CollectionNameSuperusers).Bind(recordSaveHandler)
|
||||
app.OnRecordUpdateExecute(CollectionNameSuperusers).Bind(recordSaveHandler)
|
||||
|
||||
// prevent sending password reset emails to the installer address
|
||||
app.OnMailerRecordPasswordResetSend(CollectionNameSuperusers).Bind(&hook.Handler[*MailerRecordEvent]{
|
||||
Id: "pbSuperusersInstallerPasswordReset",
|
||||
Func: func(e *MailerRecordEvent) error {
|
||||
if e.Record.Email() == DefaultInstallerEmail {
|
||||
return errors.New("cannot reset the password for the installer superuser")
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
},
|
||||
})
|
||||
|
||||
collectionSaveHandler := &hook.Handler[*CollectionEvent]{
|
||||
Id: "pbSuperusersCollectionSaveExec",
|
||||
Func: func(e *CollectionEvent) error {
|
||||
|
||||
+34
-9
@@ -123,10 +123,10 @@ type settings struct {
|
||||
Backups BackupsConfig `form:"backups" json:"backups"`
|
||||
S3 S3Config `form:"s3" json:"s3"`
|
||||
Meta MetaConfig `form:"meta" json:"meta"`
|
||||
Logs LogsConfig `form:"logs" json:"logs"`
|
||||
Batch BatchConfig `form:"batch" json:"batch"`
|
||||
RateLimits RateLimitsConfig `form:"rateLimits" json:"rateLimits"`
|
||||
TrustedProxy TrustedProxyConfig `form:"trustedProxy" json:"trustedProxy"`
|
||||
Batch BatchConfig `form:"batch" json:"batch"`
|
||||
Logs LogsConfig `form:"logs" json:"logs"`
|
||||
}
|
||||
|
||||
// Settings defines the PocketBase app settings.
|
||||
@@ -617,19 +617,28 @@ func checkUniqueRuleLabel(value any) error {
|
||||
return validators.ErrUnsupportedValueType
|
||||
}
|
||||
|
||||
labels := make(map[string]struct{}, len(rules))
|
||||
existing := make([]string, 0, len(rules))
|
||||
|
||||
for i, rule := range rules {
|
||||
_, ok := labels[rule.Label]
|
||||
if ok {
|
||||
fullKey := rule.Label + "@@" + rule.Audience
|
||||
|
||||
var conflicts bool
|
||||
for _, key := range existing {
|
||||
if strings.HasPrefix(key, fullKey) || strings.HasPrefix(fullKey, key) {
|
||||
conflicts = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if conflicts {
|
||||
return validation.Errors{
|
||||
strconv.Itoa(i): validation.Errors{
|
||||
"label": validation.NewError("validation_duplicated_rate_limit_tag", "Rate limit tag with label "+rule.Label+" already exists.").
|
||||
"label": validation.NewError("validation_conflicting_rate_limit_rule", "Rate limit rule configuration with label "+rule.Label+" already exists or conflicts with another rule.").
|
||||
SetParams(map[string]any{"label": rule.Label}),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
labels[rule.Label] = struct{}{}
|
||||
existing = append(existing, fullKey)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,6 +647,13 @@ func checkUniqueRuleLabel(value any) error {
|
||||
|
||||
var rateLimitRuleLabelRegex = regexp.MustCompile(`^(\w+\ \/[\w\/-]*|\/[\w\/-]*|^\w+\:\w+|\*\:\w+|\w+)$`)
|
||||
|
||||
// The allowed RateLimitRule.Audience values
|
||||
const (
|
||||
RateLimitRuleAudienceAll = ""
|
||||
RateLimitRuleAudienceGuest = "@guest"
|
||||
RateLimitRuleAudienceAuth = "@auth"
|
||||
)
|
||||
|
||||
type RateLimitRule struct {
|
||||
// Label is the identifier of the current rule.
|
||||
//
|
||||
@@ -652,12 +668,18 @@ type RateLimitRule struct {
|
||||
// - POST /api/collections/
|
||||
Label string `form:"label" json:"label"`
|
||||
|
||||
// MaxRequests is the max allowed number of requests per Duration.
|
||||
MaxRequests int `form:"maxRequests" json:"maxRequests"`
|
||||
// Audience specifies the auth group the rule should apply for:
|
||||
// - "" - both guests and authenticated users (default)
|
||||
// - "guest" - only for guests
|
||||
// - "auth" - only for authenticated users
|
||||
Audience string `form:"audience" json:"audience"`
|
||||
|
||||
// Duration specifies the interval (in seconds) per which to reset
|
||||
// the counted/accumulated rate limiter tokens.
|
||||
Duration int64 `form:"duration" json:"duration"`
|
||||
|
||||
// MaxRequests is the max allowed number of requests per Duration.
|
||||
MaxRequests int `form:"maxRequests" json:"maxRequests"`
|
||||
}
|
||||
|
||||
// Validate makes RateLimitRule validatable by implementing [validation.Validatable] interface.
|
||||
@@ -666,6 +688,9 @@ func (c RateLimitRule) Validate() error {
|
||||
validation.Field(&c.Label, validation.Required, validation.Match(rateLimitRuleLabelRegex)),
|
||||
validation.Field(&c.MaxRequests, validation.Required, validation.Min(1)),
|
||||
validation.Field(&c.Duration, validation.Required, validation.Min(1)),
|
||||
validation.Field(&c.Audience,
|
||||
validation.In(RateLimitRuleAudienceAll, RateLimitRuleAudienceGuest, RateLimitRuleAudienceAuth),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+113
-2
@@ -106,7 +106,7 @@ func TestSettingsMarshalJSON(t *testing.T) {
|
||||
}
|
||||
rawStr := string(raw)
|
||||
|
||||
expected := `{"smtp":{"enabled":false,"port":0,"host":"","username":"abc","authMethod":"","tls":false,"localName":""},"backups":{"cron":"","cronMaxKeep":0,"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","forcePathStyle":false}},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","forcePathStyle":false},"meta":{"appName":"test123","appURL":"","senderName":"","senderAddress":"","hideControls":false},"logs":{"maxDays":0,"minLevel":0,"logIP":false,"logAuthId":false},"batch":{"enabled":false,"maxRequests":0,"timeout":0,"maxBodySize":0},"rateLimits":{"rules":[],"enabled":false},"trustedProxy":{"headers":[],"useLeftmostIP":false}}`
|
||||
expected := `{"smtp":{"enabled":false,"port":0,"host":"","username":"abc","authMethod":"","tls":false,"localName":""},"backups":{"cron":"","cronMaxKeep":0,"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","forcePathStyle":false}},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"","accessKey":"","forcePathStyle":false},"meta":{"appName":"test123","appURL":"","senderName":"","senderAddress":"","hideControls":false},"rateLimits":{"rules":[],"enabled":false},"trustedProxy":{"headers":[],"useLeftmostIP":false},"batch":{"enabled":false,"maxRequests":0,"timeout":0,"maxBodySize":0},"logs":{"maxDays":0,"minLevel":0,"logIP":false,"logAuthId":false}}`
|
||||
|
||||
if rawStr != expected {
|
||||
t.Fatalf("Expected\n%v\ngot\n%v", expected, rawStr)
|
||||
@@ -539,6 +539,86 @@ func TestRateLimitsConfigValidate(t *testing.T) {
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"duplicated rules with the same audience",
|
||||
core.RateLimitsConfig{
|
||||
Enabled: true,
|
||||
Rules: []core.RateLimitRule{
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 1,
|
||||
MaxRequests: 2,
|
||||
},
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 2,
|
||||
MaxRequests: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{"rules"},
|
||||
},
|
||||
{
|
||||
"duplicated rule with conflicting audience (A)",
|
||||
core.RateLimitsConfig{
|
||||
Enabled: true,
|
||||
Rules: []core.RateLimitRule{
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 1,
|
||||
MaxRequests: 2,
|
||||
},
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 1,
|
||||
MaxRequests: 2,
|
||||
Audience: core.RateLimitRuleAudienceGuest,
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{"rules"},
|
||||
},
|
||||
{
|
||||
"duplicated rule with conflicting audience (B)",
|
||||
core.RateLimitsConfig{
|
||||
Enabled: true,
|
||||
Rules: []core.RateLimitRule{
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 1,
|
||||
MaxRequests: 2,
|
||||
Audience: core.RateLimitRuleAudienceAuth,
|
||||
},
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 1,
|
||||
MaxRequests: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{"rules"},
|
||||
},
|
||||
{
|
||||
"duplicated rule with non-conflicting audience",
|
||||
core.RateLimitsConfig{
|
||||
Enabled: true,
|
||||
Rules: []core.RateLimitRule{
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 1,
|
||||
MaxRequests: 2,
|
||||
Audience: core.RateLimitRuleAudienceAuth,
|
||||
},
|
||||
{
|
||||
Label: "/a",
|
||||
Duration: 1,
|
||||
MaxRequests: 2,
|
||||
Audience: core.RateLimitRuleAudienceGuest,
|
||||
},
|
||||
},
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
@@ -611,8 +691,9 @@ func TestRateLimitRuleValidate(t *testing.T) {
|
||||
Label: "@abc",
|
||||
Duration: -1,
|
||||
MaxRequests: -1,
|
||||
Audience: "invalid",
|
||||
},
|
||||
[]string{"label", "duration", "maxRequests"},
|
||||
[]string{"label", "duration", "maxRequests", "audience"},
|
||||
},
|
||||
{
|
||||
"valid data (name)",
|
||||
@@ -659,6 +740,36 @@ func TestRateLimitRuleValidate(t *testing.T) {
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"invalid audience",
|
||||
core.RateLimitRule{
|
||||
Label: "/a/b/",
|
||||
Duration: 1,
|
||||
MaxRequests: 1,
|
||||
Audience: "invalid",
|
||||
},
|
||||
[]string{"audience"},
|
||||
},
|
||||
{
|
||||
"valid audience - " + core.RateLimitRuleAudienceGuest,
|
||||
core.RateLimitRule{
|
||||
Label: "POST /a/b/",
|
||||
Duration: 1,
|
||||
MaxRequests: 1,
|
||||
Audience: core.RateLimitRuleAudienceGuest,
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"valid audience - " + core.RateLimitRuleAudienceAuth,
|
||||
core.RateLimitRule{
|
||||
Label: "POST /a/b/",
|
||||
Duration: 1,
|
||||
MaxRequests: 1,
|
||||
Audience: core.RateLimitRuleAudienceAuth,
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
@@ -4,56 +4,56 @@ go 1.23
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.41
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.33
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.4
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.3
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.44
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.37
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.3
|
||||
github.com/aws/smithy-go v1.22.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/domodwyer/mailyak/v3 v3.6.2
|
||||
github.com/dop251/goja v0.0.0-20241009100908-5f46f2705ca3
|
||||
github.com/dop251/goja_nodejs v0.0.0-20240728170619-29b559befffc
|
||||
github.com/fatih/color v1.17.0
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.6
|
||||
github.com/ganigeorgiev/fexpr v0.4.1
|
||||
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1
|
||||
github.com/pocketbase/dbx v1.10.1
|
||||
github.com/pocketbase/tygoja v0.0.0-20241015175937-d6ff411a0f75
|
||||
github.com/spf13/cast v1.7.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
gocloud.dev v0.40.0
|
||||
golang.org/x/crypto v0.28.0
|
||||
golang.org/x/net v0.30.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/net v0.31.0
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
golang.org/x/sync v0.9.0
|
||||
modernc.org/sqlite v1.33.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.23 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.32.4 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
@@ -65,17 +65,17 @@ require (
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.8.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/image v0.21.0 // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/term v0.25.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/image v0.22.0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/term v0.26.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/tools v0.27.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
google.golang.org/api v0.201.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
|
||||
google.golang.org/grpc v1.67.1 // indirect
|
||||
google.golang.org/api v0.205.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113154021-e0fbfb71d213 // indirect
|
||||
google.golang.org/grpc v1.68.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20241004144649-1aea3fae8852 // indirect
|
||||
modernc.org/libc v1.61.0 // indirect
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||
cloud.google.com/go/auth v0.9.8 h1:+CSJ0Gw9iVeSENVCKJoLHhdUykDgXSc4Qn+gu2BRtR8=
|
||||
cloud.google.com/go/auth v0.9.8/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||
cloud.google.com/go/auth v0.10.1 h1:TnK46qldSfHWt2a0b/hciaiVJsmDXWy9FqyUan0uYiI=
|
||||
cloud.google.com/go/auth v0.10.1/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.5 h1:2p29+dePqsCHPP1bqDJcKj4qxRyYCcbzKpFyKGt3MTk=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.5/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
|
||||
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
|
||||
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
|
||||
cloud.google.com/go/iam v1.1.13 h1:7zWBXG9ERbMLrzQBRhFliAV+kjcRToDTgQT3CTwYyv4=
|
||||
@@ -25,42 +25,42 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3d
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
|
||||
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.4 h1:S13INUiTxgrPueTmrm5DZ+MiAo99zYzHEFh1UNkOxNE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.4/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.0 h1:FosVYWcqEtWNxHn8gB/Vs6jOlNwSoyOCA/g/sxyySOQ=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.0/go.mod h1:pYhbtvg1siOOg8h5an77rXle9tVG8T+BWLWAo7cOukc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.41 h1:7gXo+Axmp+R4Z+AK8YFQO0ZV3L0gizGINCOWxSLY9W8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.41/go.mod h1:u4Eb8d3394YLubphT4jLEwN1rLNq2wFOlT6OuxFwPzU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 h1:TMH3f/SCAWdNtXXVPPu5D6wrr4G5hI1rAxbcocKfC7Q=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17/go.mod h1:1ZRXLdTpzdJb9fwTMXiLipENRxkGMTn1sfKexGllQCw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.33 h1:X+4YY5kZRI/cOoSMVMGTqFXHAMg1bvvay7IBcqHpybQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.33/go.mod h1:DPynzu+cn92k5UQ6tZhX+wfTB4ah6QDU/NgdHqatmvk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.3 h1:kL5uAptPcPKaJ4q0sDUjUIdueO18Q7JDzl64GpVwdOM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.3/go.mod h1:SPEn1KA8YbgQnwiJ/OISU4fz7+F6Fe309Jf0QTsRCl4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.44 h1:qqfs5kulLUHUEXlHEZXLJkgGoF3kkUeFUTVA585cFpU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.44/go.mod h1:0Lm2YJ8etJdEdw23s+q/9wTpOeo2HhNE97XcRa7T8MA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.19 h1:woXadbf0c7enQ2UGCi8gW/WuKmE0xIzxBF/eD94jMKQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.19/go.mod h1:zminj5ucw7w0r65bP6nhyOd3xL6veAUMc3ElGMoLVb4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.37 h1:jHKR76E81sZvz1+x1vYYrHMxphG5LFBJPhSqEr4CLlE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.37/go.mod h1:iMkyPkmoJWQKzSOtaX+8oEJxAuqr7s8laxcqGDSHeII=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23 h1:A2w6m6Tmr+BNXjDsr7M90zkWjsu4JXHwrzPg235STs4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.23/go.mod h1:35EVp9wyeANdujZruvHiQUAo9E3vbhnIO1mTCAxMlY0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23 h1:pgYW9FCabt2M25MoHYCfMrVY2ghiiBKYWUVXfwZs+sU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.23/go.mod h1:c48kLgzO19wAu3CPkDWC28JbaJ+hfQlsdl7I2+oqIbk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 h1:7edmS3VOBDhK00b/MwGtGglCm7hhwNYnjJs/PgFdMQE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21/go.mod h1:Q9o5h4HoIWG8XfzxqiuK/CGUbepCJ8uTlaE3bAbxytQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.23 h1:1SZBDiRzzs3sNhOMVApyWPduWYGAX0imGy06XiBnCAM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.23/go.mod h1:i9TkxgbZmHVh2S0La6CAXtnyFhlCX/pJ0JsOvBAS6Mk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 h1:4FMHqLfk0efmTqhXVRL5xYRqlEBNBiRI7N6w4jsEdd4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2/go.mod h1:LWoqeWlK9OZeJxsROW2RqrSPvQHKTpp69r/iDjwsSaw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 h1:t7iUP9+4wdc5lt3E41huP+GvQZJD38WLsgVp4iOtAjg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2/go.mod h1:/niFCtmuQNxqx9v8WAPq5qh7EH25U4BF6tjoyq9bObM=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0 h1:xA6XhTF7PE89BCNHJbQi8VvPzcgMtmGC5dr8S8N7lHk=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0/go.mod h1:cB6oAuus7YXRZhWCc1wIwPywwZ1XwweNp2TVAEGYeB8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 h1:bSYXVyUzoTHoKalBmwaZxs97HU9DWWI3ehHSAMa7xOk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2/go.mod h1:skMqY7JElusiOUjMJMOv1jJsP7YUg7DrhgqZZWuzu1U=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/LfUWUF+zFPEcY9QXb7o=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4 h1:aaPpoG15S2qHkWm4KlEyF01zovK1nW4BBbyXuHNSE90=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.4/go.mod h1:eD9gS2EARTKgGr/W5xwgY/ik9z/zqpW+m/xOQbVxrMk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4 h1:tHxQi/XHPK0ctd/wdOw0t7Xrc2OxcRCnVzv8lwWPu0c=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.4/go.mod h1:4GQbF1vJzG60poZqWatZlhP31y8PGCCVTvIGPdaaYJ0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4 h1:E5ZAVOmI2apR8ADb72Q63KqwwwdW1XcMeXIlrZ1Psjg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.4/go.mod h1:wezzqVUOVVdk+2Z/JzQT4NxAU0NbhRe5W8pIE72jsWI=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.3 h1:neNOYJl72bHrz9ikAEED4VqWyND/Po0DnEx64RW6YM4=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.66.3/go.mod h1:TMhLIyRIyoGVlaEMAt+ITMbwskSTpcGsCPDq91/ihY0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.5 h1:HJwZwRt2Z2Tdec+m+fPjvdmkq2s9Ra+VR0hjF7V2o40=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.5/go.mod h1:wrMCEwjFPms+V86TCQQeOxQF/If4vT44FGIOFiMC2ck=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.4 h1:zcx9LiGWZ6i6pjdcoE9oXAB6mUdeyC36Ia/QEiIvYdg=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.4/go.mod h1:Tp/ly1cTjRLGBBmNccFumbZ8oqpZlpdhFf80SrRh4is=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.32.4 h1:yDxvkz3/uOKfxnv8YhzOi9m+2OGIxF+on3KOISbK5IU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.32.4/go.mod h1:9XEUty5v5UAsMiFOBJrNibZgwCeOma73jgGwwhgffa8=
|
||||
github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
|
||||
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -88,8 +88,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
@@ -111,8 +111,8 @@ github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5Nq
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
@@ -149,8 +149,8 @@ github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
|
||||
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s=
|
||||
github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A=
|
||||
github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o=
|
||||
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
@@ -223,20 +223,20 @@ gocloud.dev v0.40.0/go.mod h1:drz+VyYNBvrMTW0KZiBAYEdl8lbNZx+OQ7oQvdrFmSQ=
|
||||
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.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||
golang.org/x/image v0.22.0 h1:UtK5yLUzilVrkjMAZAZ34DXGpASN8i8pj8g+O+yd10g=
|
||||
golang.org/x/image v0.22.0/go.mod h1:9hPFhljd4zZ1GNSIZJ49sqbp45GKK9t6w+iXvGqZUz4=
|
||||
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=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -247,17 +247,17 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
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=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -269,19 +269,19 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
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=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -291,33 +291,33 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
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.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
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-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/api v0.201.0 h1:+7AD9JNM3tREtawRMu8sOjSbb8VYcYXJG/2eEOmfDu0=
|
||||
google.golang.org/api v0.201.0/go.mod h1:HVY0FCHVs89xIW9fzf/pBvOEm+OolHa86G/txFezyq4=
|
||||
google.golang.org/api v0.205.0 h1:LFaxkAIpDb/GsrWV20dMMo5MR0h8UARTbn24LmD+0Pg=
|
||||
google.golang.org/api v0.205.0/go.mod h1:NrK1EMqO8Xk6l6QwRAmrXXg2v6dzukhlOyvkYtnvUuc=
|
||||
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/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-20241007155032-5fefd90f89a9 h1:nFS3IivktIU5Mk6KQa+v6RKkHUpdQpphqGNLxqNnbEk=
|
||||
google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:tEzYTYZxbmVNOu0OAFH9HzdJtLn6h4Aj89zzlBCdHms=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU=
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113154021-e0fbfb71d213 h1:L+WcQXqkyf5MX6g7AudgYEuJjmYbqSRkTmJqGfAPw+Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241113154021-e0fbfb71d213/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||
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=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
||||
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
|
||||
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
||||
+40
-1
@@ -40,6 +40,8 @@ func SendRecordAuthAlert(app core.App, authRecord *core.Record) error {
|
||||
}
|
||||
|
||||
// SendRecordOTP sends OTP email to the specified auth record.
|
||||
//
|
||||
// This method will also update the "sentTo" field of the related OTP record to the mail sent To address (if the OTP exists and not already assigned).
|
||||
func SendRecordOTP(app core.App, authRecord *core.Record, otpId string, pass string) error {
|
||||
mailClient := app.NewMailClient()
|
||||
|
||||
@@ -72,7 +74,44 @@ func SendRecordOTP(app core.App, authRecord *core.Record, otpId string, pass str
|
||||
}
|
||||
|
||||
return app.OnMailerRecordOTPSend().Trigger(event, func(e *core.MailerRecordEvent) error {
|
||||
return e.Mailer.Send(e.Message)
|
||||
err := e.Mailer.Send(e.Message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var toAddress string
|
||||
if len(e.Message.To) > 0 {
|
||||
toAddress = e.Message.To[0].Address
|
||||
}
|
||||
if toAddress == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
otp, err := e.App.FindOTPById(otpId)
|
||||
if err != nil {
|
||||
e.App.Logger().Error(
|
||||
"Failed to find OTP to update its sentTo field",
|
||||
"error", err,
|
||||
"otpId", otpId,
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
if otp.SentTo() != "" {
|
||||
return nil // was already sent to another target
|
||||
}
|
||||
|
||||
otp.SetSentTo(toAddress)
|
||||
if err = e.App.Save(otp); err != nil {
|
||||
e.App.Logger().Error(
|
||||
"Failed to update OTP sentTo field",
|
||||
"error", err,
|
||||
"otpId", otpId,
|
||||
"to", toAddress,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ func createParamsTable(txApp core.App) error {
|
||||
}
|
||||
|
||||
func createMFAsCollection(txApp core.App) error {
|
||||
col := core.NewBaseCollection(core.CollectionNameMFAs, "_pbc"+core.CollectionNameMFAs)
|
||||
col := core.NewBaseCollection(core.CollectionNameMFAs)
|
||||
col.System = true
|
||||
|
||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||
@@ -157,7 +157,7 @@ func createMFAsCollection(txApp core.App) error {
|
||||
}
|
||||
|
||||
func createOTPsCollection(txApp core.App) error {
|
||||
col := core.NewBaseCollection(core.CollectionNameOTPs, "_pbc"+core.CollectionNameOTPs)
|
||||
col := core.NewBaseCollection(core.CollectionNameOTPs)
|
||||
col.System = true
|
||||
|
||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||
@@ -179,7 +179,12 @@ func createOTPsCollection(txApp core.App) error {
|
||||
System: true,
|
||||
Hidden: true,
|
||||
Required: true,
|
||||
Cost: 8, // low cost for better performce and because it is not critical
|
||||
Cost: 8, // low cost for better performance and because it is not critical
|
||||
})
|
||||
col.Fields.Add(&core.TextField{
|
||||
Name: "sentTo",
|
||||
System: true,
|
||||
Hidden: true,
|
||||
})
|
||||
col.Fields.Add(&core.AutodateField{
|
||||
Name: "created",
|
||||
@@ -198,7 +203,7 @@ func createOTPsCollection(txApp core.App) error {
|
||||
}
|
||||
|
||||
func createAuthOriginsCollection(txApp core.App) error {
|
||||
col := core.NewBaseCollection(core.CollectionNameAuthOrigins, "_pbc"+core.CollectionNameAuthOrigins)
|
||||
col := core.NewBaseCollection(core.CollectionNameAuthOrigins)
|
||||
col.System = true
|
||||
|
||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||
@@ -238,7 +243,7 @@ func createAuthOriginsCollection(txApp core.App) error {
|
||||
}
|
||||
|
||||
func createExternalAuthsCollection(txApp core.App) error {
|
||||
col := core.NewBaseCollection(core.CollectionNameExternalAuths, "_pbc"+core.CollectionNameExternalAuths)
|
||||
col := core.NewBaseCollection(core.CollectionNameExternalAuths)
|
||||
col.System = true
|
||||
|
||||
ownerRule := "@request.auth.id != '' && recordRef = @request.auth.id && collectionRef = @request.auth.collectionId"
|
||||
@@ -284,7 +289,7 @@ func createExternalAuthsCollection(txApp core.App) error {
|
||||
}
|
||||
|
||||
func createSuperusersCollection(txApp core.App) error {
|
||||
superusers := core.NewAuthCollection(core.CollectionNameSuperusers, "_pbc"+core.CollectionNameSuperusers)
|
||||
superusers := core.NewAuthCollection(core.CollectionNameSuperusers)
|
||||
superusers.System = true
|
||||
superusers.Fields.Add(&core.EmailField{
|
||||
Name: "email",
|
||||
@@ -309,6 +314,14 @@ func createSuperusersCollection(txApp core.App) error {
|
||||
|
||||
func createUsersCollection(txApp core.App) error {
|
||||
users := core.NewAuthCollection("users", "_pb_users_auth_")
|
||||
|
||||
ownerRule := "id = @request.auth.id"
|
||||
users.ListRule = types.Pointer(ownerRule)
|
||||
users.ViewRule = types.Pointer(ownerRule)
|
||||
users.CreateRule = types.Pointer("")
|
||||
users.UpdateRule = types.Pointer(ownerRule)
|
||||
users.DeleteRule = types.Pointer(ownerRule)
|
||||
|
||||
users.Fields.Add(&core.TextField{
|
||||
Name: "name",
|
||||
Max: 255,
|
||||
|
||||
@@ -365,8 +365,10 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
|
||||
c.Options["manageRule"] = nil
|
||||
if options["manageRule"] != nil {
|
||||
manageRule := cast.ToString(options["manageRule"])
|
||||
c.Options["manageRule"] = &manageRule
|
||||
manageRule, err := cast.ToStringE(options["manageRule"])
|
||||
if err == nil && manageRule != "" {
|
||||
c.Options["manageRule"] = migrateRule(&manageRule)
|
||||
}
|
||||
}
|
||||
|
||||
// passwordAuth
|
||||
@@ -494,15 +496,15 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
// ---
|
||||
c.Indexes = append(types.JSONArray[string]{
|
||||
fmt.Sprintf("CREATE UNIQUE INDEX `_%s_username_idx` ON `%s` (username COLLATE NOCASE)", c.Id, c.Name),
|
||||
fmt.Sprintf("CREATE UNIQUE INDEX `_%s_email_idx` ON `%s` (email) WHERE email != ''", c.Id, c.Name),
|
||||
fmt.Sprintf("CREATE UNIQUE INDEX `_%s_tokenKey_idx` ON `%s` (tokenKey)", c.Id, c.Name),
|
||||
fmt.Sprintf("CREATE UNIQUE INDEX `_%s_email_idx` ON `%s` (`email`) WHERE `email` != ''", c.Id, c.Name),
|
||||
fmt.Sprintf("CREATE UNIQUE INDEX `_%s_tokenKey_idx` ON `%s` (`tokenKey`)", c.Id, c.Name),
|
||||
}, c.Indexes...)
|
||||
|
||||
// prepend the auth system fields
|
||||
// ---
|
||||
tokenKeyField := map[string]any{
|
||||
"id": fieldIdChecksum("text", "tokenKey"),
|
||||
"type": "text",
|
||||
"id": "_pbf_auth_tokenKey_",
|
||||
"name": "tokenKey",
|
||||
"system": true,
|
||||
"hidden": true,
|
||||
@@ -515,8 +517,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
"autogeneratePattern": "[a-zA-Z0-9_]{50}",
|
||||
}
|
||||
passwordField := map[string]any{
|
||||
"id": fieldIdChecksum("password", "password"),
|
||||
"type": "password",
|
||||
"id": "_pbf_auth_password_",
|
||||
"name": "password",
|
||||
"presentable": false,
|
||||
"system": true,
|
||||
@@ -527,8 +529,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
"cost": bcrypt.DefaultCost, // new default
|
||||
}
|
||||
emailField := map[string]any{
|
||||
"id": fieldIdChecksum("email", "email"),
|
||||
"type": "email",
|
||||
"id": "_pbf_auth_email_",
|
||||
"name": "email",
|
||||
"system": true,
|
||||
"hidden": false,
|
||||
@@ -538,8 +540,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
"onlyDomains": cast.ToStringSlice(options["onlyEmailDomains"]),
|
||||
}
|
||||
emailVisibilityField := map[string]any{
|
||||
"id": fieldIdChecksum("bool", "emailVisibility"),
|
||||
"type": "bool",
|
||||
"id": "_pbf_auth_emailVisibility_",
|
||||
"name": "emailVisibility",
|
||||
"system": true,
|
||||
"hidden": false,
|
||||
@@ -547,8 +549,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
"required": false,
|
||||
}
|
||||
verifiedField := map[string]any{
|
||||
"id": fieldIdChecksum("bool", "verified"),
|
||||
"type": "bool",
|
||||
"id": "_pbf_auth_verified_",
|
||||
"name": "verified",
|
||||
"system": true,
|
||||
"hidden": false,
|
||||
@@ -556,8 +558,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
"required": false,
|
||||
}
|
||||
usernameField := map[string]any{
|
||||
"id": fieldIdChecksum("text", "username"),
|
||||
"type": "text",
|
||||
"id": "_pbf_auth_username_",
|
||||
"name": "username",
|
||||
"system": false,
|
||||
"hidden": false,
|
||||
@@ -597,8 +599,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
|
||||
// prepend the id field
|
||||
idField := map[string]any{
|
||||
"id": fieldIdChecksum("text", "id"),
|
||||
"type": "text",
|
||||
"id": "_pbf_text_id_",
|
||||
"name": "id",
|
||||
"system": true,
|
||||
"required": true,
|
||||
@@ -631,8 +633,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
|
||||
if addCreated {
|
||||
createdField := map[string]any{
|
||||
"id": fieldIdChecksum("autodate", "created"),
|
||||
"type": "autodate",
|
||||
"id": "_pbf_autodate_created_",
|
||||
"name": "created",
|
||||
"system": false,
|
||||
"presentable": false,
|
||||
@@ -645,8 +647,8 @@ func migrateOldCollections(txApp core.App, oldSettings *oldSettingsModel) error
|
||||
|
||||
if addUpdated {
|
||||
updatedField := map[string]any{
|
||||
"id": fieldIdChecksum("autodate", "updated"),
|
||||
"type": "autodate",
|
||||
"id": "_pbf_autodate_updated_",
|
||||
"name": "updated",
|
||||
"system": false,
|
||||
"presentable": false,
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
"strconv"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
// note: this migration will be deleted in future version
|
||||
|
||||
func collectionIdChecksum(typ, name string) string {
|
||||
return "pbc_" + strconv.Itoa(int(crc32.ChecksumIEEE([]byte(typ+name))))
|
||||
}
|
||||
|
||||
func fieldIdChecksum(typ, name string) string {
|
||||
return typ + strconv.Itoa(int(crc32.ChecksumIEEE([]byte(name))))
|
||||
}
|
||||
|
||||
// normalize system collection and field ids
|
||||
func init() {
|
||||
core.SystemMigrations.Register(func(txApp core.App) error {
|
||||
collections := []*core.Collection{}
|
||||
err := txApp.CollectionQuery().
|
||||
AndWhere(dbx.In(
|
||||
"name",
|
||||
core.CollectionNameMFAs,
|
||||
core.CollectionNameOTPs,
|
||||
core.CollectionNameExternalAuths,
|
||||
core.CollectionNameAuthOrigins,
|
||||
core.CollectionNameSuperusers,
|
||||
)).
|
||||
All(&collections)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, c := range collections {
|
||||
var needUpdate bool
|
||||
|
||||
references, err := txApp.FindCollectionReferences(c, c.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authOrigins, err := txApp.FindAllAuthOriginsByCollection(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mfas, err := txApp.FindAllMFAsByCollection(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
otps, err := txApp.FindAllOTPsByCollection(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
originalId := c.Id
|
||||
|
||||
// normalize collection id
|
||||
if checksum := collectionIdChecksum(c.Type, c.Name); c.Id != checksum {
|
||||
c.Id = checksum
|
||||
needUpdate = true
|
||||
}
|
||||
|
||||
// normalize system fields
|
||||
for _, f := range c.Fields {
|
||||
if !f.GetSystem() {
|
||||
continue
|
||||
}
|
||||
|
||||
if checksum := fieldIdChecksum(f.Type(), f.GetName()); f.GetId() != checksum {
|
||||
f.SetId(checksum)
|
||||
needUpdate = true
|
||||
}
|
||||
}
|
||||
|
||||
if !needUpdate {
|
||||
continue
|
||||
}
|
||||
|
||||
rawExport, err := c.DBExport(txApp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = txApp.DB().Update("_collections", rawExport, dbx.HashExp{"id": originalId}).Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// make sure that the cached collection id is also updated
|
||||
cached, err := txApp.FindCachedCollectionByNameOrId(c.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cached.Id = c.Id
|
||||
|
||||
// update collection references
|
||||
for refCollection, fields := range references {
|
||||
for _, f := range fields {
|
||||
relationField, ok := f.(*core.RelationField)
|
||||
if !ok || relationField.CollectionId == originalId {
|
||||
continue
|
||||
}
|
||||
|
||||
relationField.CollectionId = c.Id
|
||||
}
|
||||
if err = txApp.SaveNoValidate(refCollection); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// update mfas references
|
||||
for _, item := range mfas {
|
||||
item.SetCollectionRef(c.Id)
|
||||
if err = txApp.SaveNoValidate(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// update otps references
|
||||
for _, item := range otps {
|
||||
item.SetCollectionRef(c.Id)
|
||||
if err = txApp.SaveNoValidate(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// update authOrigins references
|
||||
for _, item := range authOrigins {
|
||||
item.SetCollectionRef(c.Id)
|
||||
if err = txApp.SaveNoValidate(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}, nil)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
)
|
||||
|
||||
// note: this migration will be deleted in future version
|
||||
|
||||
// add new OTP sentTo text field (if not already)
|
||||
func init() {
|
||||
core.SystemMigrations.Register(func(txApp core.App) error {
|
||||
otpCollection, err := txApp.FindCollectionByNameOrId(core.CollectionNameOTPs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field := otpCollection.Fields.GetByName("sentTo")
|
||||
if field != nil {
|
||||
return nil // already exists
|
||||
}
|
||||
|
||||
otpCollection.Fields.Add(&core.TextField{
|
||||
Name: "sentTo",
|
||||
System: true,
|
||||
Hidden: true,
|
||||
})
|
||||
|
||||
return txApp.Save(otpCollection)
|
||||
}, nil)
|
||||
}
|
||||
@@ -728,7 +728,6 @@ func apisBinds(vm *goja.Runtime) {
|
||||
obj.Set("requireGuestOnly", apis.RequireGuestOnly)
|
||||
obj.Set("requireAuth", apis.RequireAuth)
|
||||
obj.Set("requireSuperuserAuth", apis.RequireSuperuserAuth)
|
||||
obj.Set("requireSuperuserAuthOnlyIfAny", apis.RequireSuperuserAuthOnlyIfAny)
|
||||
obj.Set("requireSuperuserOrOwnerAuth", apis.RequireSuperuserOrOwnerAuth)
|
||||
obj.Set("skipSuccessActivityLog", apis.SkipSuccessActivityLog)
|
||||
obj.Set("gzip", apis.Gzip)
|
||||
|
||||
@@ -998,7 +998,7 @@ func TestApisBindsCount(t *testing.T) {
|
||||
apisBinds(vm)
|
||||
|
||||
testBindsCount(vm, "this", 8, t)
|
||||
testBindsCount(vm, "$apis", 12, t)
|
||||
testBindsCount(vm, "$apis", 11, t)
|
||||
}
|
||||
|
||||
func TestApisBindsApiError(t *testing.T) {
|
||||
|
||||
+3556
-3563
File diff suppressed because it is too large
Load Diff
@@ -104,7 +104,7 @@ declare function routerAdd(
|
||||
*
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare function routerUse(...middlewares: Array<string|((e: core.RequestEvent) => void)|Middleware): void;
|
||||
declare function routerUse(...middlewares: Array<string|((e: core.RequestEvent) => void)|Middleware>): void;
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// baseBinds
|
||||
@@ -527,7 +527,6 @@ declare class Command implements cobra.Command {
|
||||
constructor(cmd?: Partial<cobra.Command>)
|
||||
}
|
||||
|
||||
interface RequestInfo extends core.RequestInfo{} // merge
|
||||
/**
|
||||
* RequestInfo defines a single core.RequestInfo instance, usually used
|
||||
* as part of various filter checks.
|
||||
@@ -550,8 +549,10 @@ interface RequestInfo extends core.RequestInfo{} // merge
|
||||
*
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare class RequestInfo implements core.RequestInfo {
|
||||
constructor(info?: Partial<core.RequestInfo>)
|
||||
declare const RequestInfo: {
|
||||
new(info?: Partial<core.RequestInfo>): core.RequestInfo
|
||||
|
||||
// note: declare as "newable" const due to conflict with the RequestInfo TS node type
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -845,7 +846,11 @@ declare namespace $os {
|
||||
*/
|
||||
export let cmd: exec.command
|
||||
|
||||
export let args: os.args
|
||||
/**
|
||||
* Args hold the command-line arguments, starting with the program name.
|
||||
*/
|
||||
export let args: Array<string>
|
||||
|
||||
export let exit: os.exit
|
||||
export let getenv: os.getenv
|
||||
export let dirFS: os.dirFS
|
||||
@@ -993,7 +998,6 @@ declare namespace $apis {
|
||||
let requireGuestOnly: apis.requireGuestOnly
|
||||
let requireAuth: apis.requireAuth
|
||||
let requireSuperuserAuth: apis.requireSuperuserAuth
|
||||
let requireSuperuserAuthOnlyIfAny: apis.requireSuperuserAuthOnlyIfAny
|
||||
let requireSuperuserOrOwnerAuth: apis.requireSuperuserOrOwnerAuth
|
||||
let skipSuccessActivityLog: apis.skipSuccessActivityLog
|
||||
let gzip: apis.gzip
|
||||
|
||||
@@ -62,7 +62,7 @@ func (p *plugin) jsSnapshotTemplate(collections []*core.Collection) (string, err
|
||||
const template = jsTypesDirective + `migrate((app) => {
|
||||
const snapshot = %s;
|
||||
|
||||
return app.importCollections(snapshot, true);
|
||||
return app.importCollections(snapshot, false);
|
||||
}, (app) => {
|
||||
return null;
|
||||
})
|
||||
@@ -348,7 +348,7 @@ func init() {
|
||||
m.Register(func(app core.App) error {
|
||||
jsonData := ` + "`%s`" + `
|
||||
|
||||
return app.ImportCollectionsByMarshaledJSON([]byte(jsonData), true)
|
||||
return app.ImportCollectionsByMarshaledJSON([]byte(jsonData), false)
|
||||
}, func(app core.App) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
+7
-7
@@ -64,9 +64,9 @@ type Config struct {
|
||||
//
|
||||
// Note that the application will not be initialized/bootstrapped yet,
|
||||
// aka. DB connections, migrations, app settings, etc. will not be accessible.
|
||||
// Everything will be initialized when [PocketBase.Start()] is executed.
|
||||
// If you want to initialize the application before calling [PocketBase.Start()],
|
||||
// then you'll have to manually call [PocketBase.Bootstrap()].
|
||||
// Everything will be initialized when [PocketBase.Start] is executed.
|
||||
// If you want to initialize the application before calling [PocketBase.Start],
|
||||
// then you'll have to manually call [PocketBase.Bootstrap].
|
||||
func New() *PocketBase {
|
||||
_, isUsingGoRun := inspectRuntime()
|
||||
|
||||
@@ -79,9 +79,9 @@ func New() *PocketBase {
|
||||
//
|
||||
// Note that the application will not be initialized/bootstrapped yet,
|
||||
// aka. DB connections, migrations, app settings, etc. will not be accessible.
|
||||
// Everything will be initialized when [PocketBase.Start()] is executed.
|
||||
// If you want to initialize the application before calling [PocketBase..Start()],
|
||||
// then you'll have to manually call [PocketBase.Bootstrap()].
|
||||
// Everything will be initialized when [PocketBase.Start] is executed.
|
||||
// If you want to initialize the application before calling [PocketBase.Start],
|
||||
// then you'll have to manually call [PocketBase.Bootstrap].
|
||||
func NewWithConfig(config Config) *PocketBase {
|
||||
// initialize a default data directory based on the executable baseDir
|
||||
if config.DefaultDataDir == "" {
|
||||
@@ -141,7 +141,7 @@ func NewWithConfig(config Config) *PocketBase {
|
||||
}
|
||||
|
||||
// Start starts the application, aka. registers the default system
|
||||
// commands (serve, migrate, version) and executes pb.RootCmd.
|
||||
// commands (serve, superuser, version) and executes pb.RootCmd.
|
||||
func (pb *PocketBase) Start() error {
|
||||
// register system commands
|
||||
pb.RootCmd.AddCommand(cmd.NewSuperuserCommand(pb))
|
||||
|
||||
Binary file not shown.
+10
-1
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func TestProvidersCount(t *testing.T) {
|
||||
expected := 27
|
||||
expected := 28
|
||||
|
||||
if total := len(auth.Providers); total != expected {
|
||||
t.Fatalf("Expected %d providers, got %d", expected, total)
|
||||
@@ -269,4 +269,13 @@ func TestNewProviderByName(t *testing.T) {
|
||||
if _, ok := p.(*auth.Monday); !ok {
|
||||
t.Error("Expected to be instance of *auth.Monday")
|
||||
}
|
||||
|
||||
// wakatime
|
||||
p, err = auth.NewProviderByName(auth.NameWakatime)
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil, got error %v", err)
|
||||
}
|
||||
if _, ok := p.(*auth.Wakatime); !ok {
|
||||
t.Error("Expected to be instance of *auth.Wakatime")
|
||||
}
|
||||
}
|
||||
|
||||
+21
-11
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/instagram"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -16,9 +15,9 @@ func init() {
|
||||
var _ Provider = (*Instagram)(nil)
|
||||
|
||||
// NameInstagram is the unique name of the Instagram provider.
|
||||
const NameInstagram string = "instagram"
|
||||
const NameInstagram string = "instagram2" // "2" suffix to avoid conflicts with the old deprecated version
|
||||
|
||||
// Instagram allows authentication via Instagram OAuth2.
|
||||
// Instagram allows authentication via Instagram Login OAuth2.
|
||||
type Instagram struct {
|
||||
BaseProvider
|
||||
}
|
||||
@@ -29,16 +28,16 @@ func NewInstagramProvider() *Instagram {
|
||||
ctx: context.Background(),
|
||||
displayName: "Instagram",
|
||||
pkce: true,
|
||||
scopes: []string{"user_profile"},
|
||||
authURL: instagram.Endpoint.AuthURL,
|
||||
tokenURL: instagram.Endpoint.TokenURL,
|
||||
userInfoURL: "https://graph.instagram.com/me?fields=id,username,account_type",
|
||||
scopes: []string{"instagram_business_basic"},
|
||||
authURL: "https://www.instagram.com/oauth/authorize",
|
||||
tokenURL: "https://api.instagram.com/oauth/access_token",
|
||||
userInfoURL: "https://graph.instagram.com/me?fields=id,username,account_type,user_id,name,profile_picture_url,followers_count,follows_count,media_count",
|
||||
}}
|
||||
}
|
||||
|
||||
// FetchAuthUser returns an AuthUser instance based on the Instagram's user api.
|
||||
// FetchAuthUser returns an AuthUser instance based on the Instagram Login user api response.
|
||||
//
|
||||
// API reference: https://developers.facebook.com/docs/instagram-basic-display-api/reference/user#fields
|
||||
// API reference: https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/get-started#fields
|
||||
func (p *Instagram) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
data, err := p.FetchRawUserInfo(token)
|
||||
if err != nil {
|
||||
@@ -50,9 +49,18 @@ func (p *Instagram) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// include list of granted permissions to RawUser's payload
|
||||
if _, ok := rawUser["permissions"]; !ok {
|
||||
if permissions := token.Extra("permissions"); permissions != nil {
|
||||
rawUser["permissions"] = permissions
|
||||
}
|
||||
}
|
||||
|
||||
extracted := struct {
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Id string `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
AvatarURL string `json:"profile_picture_url"`
|
||||
}{}
|
||||
if err := json.Unmarshal(data, &extracted); err != nil {
|
||||
return nil, err
|
||||
@@ -61,6 +69,8 @@ func (p *Instagram) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
user := &AuthUser{
|
||||
Id: extracted.Id,
|
||||
Username: extracted.Username,
|
||||
Name: extracted.Name,
|
||||
AvatarURL: extracted.AvatarURL,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Providers[NameWakatime] = wrapFactory(NewWakatimeProvider)
|
||||
}
|
||||
|
||||
var _ Provider = (*Wakatime)(nil)
|
||||
|
||||
// NameWakatime is the unique name of the Wakatime provider.
|
||||
const NameWakatime = "wakatime"
|
||||
|
||||
// Wakatime is an auth provider for Wakatime.
|
||||
type Wakatime struct {
|
||||
BaseProvider
|
||||
}
|
||||
|
||||
// NewWakatimeProvider creates a new Wakatime provider instance with some defaults.
|
||||
func NewWakatimeProvider() *Wakatime {
|
||||
return &Wakatime{BaseProvider{
|
||||
ctx: context.Background(),
|
||||
displayName: "WakaTime",
|
||||
pkce: true,
|
||||
scopes: []string{"email"},
|
||||
authURL: "https://wakatime.com/oauth/authorize",
|
||||
tokenURL: "https://wakatime.com/oauth/token",
|
||||
userInfoURL: "https://wakatime.com/api/v1/users/current",
|
||||
}}
|
||||
}
|
||||
|
||||
// FetchAuthUser returns an AuthUser instance based on the Wakatime's user API.
|
||||
//
|
||||
// API reference: https://wakatime.com/developers#users
|
||||
func (p *Wakatime) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) {
|
||||
data, err := p.FetchRawUserInfo(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawUser := map[string]any{}
|
||||
if err := json.Unmarshal(data, &rawUser); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extracted := struct {
|
||||
Data struct {
|
||||
Id string `json:"id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Photo string `json:"photo"`
|
||||
IsPhotoPublic bool `json:"photo_public"`
|
||||
IsEmailConfirmed bool `json:"is_email_confirmed"`
|
||||
} `json:"data"`
|
||||
}{}
|
||||
|
||||
if err := json.Unmarshal(data, &extracted); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
user := &AuthUser{
|
||||
Id: extracted.Data.Id,
|
||||
Name: extracted.Data.DisplayName,
|
||||
Username: extracted.Data.Username,
|
||||
RawUser: rawUser,
|
||||
AccessToken: token.AccessToken,
|
||||
RefreshToken: token.RefreshToken,
|
||||
}
|
||||
|
||||
// note: we don't check for is_email_public field because PocketBase
|
||||
// has its own emailVisibility flag which is false by default
|
||||
if extracted.Data.IsEmailConfirmed {
|
||||
user.Email = extracted.Data.Email
|
||||
}
|
||||
|
||||
if extracted.Data.IsPhotoPublic {
|
||||
user.AvatarURL = extracted.Data.Photo
|
||||
}
|
||||
|
||||
user.Expiry, _ = types.ParseDateTime(token.Expiry)
|
||||
|
||||
return user, nil
|
||||
}
|
||||
@@ -96,37 +96,6 @@ func (e *Event) RemoteIP() string {
|
||||
return parsed.StringExpanded()
|
||||
}
|
||||
|
||||
// UnsafeRealIP returns the "real" client IP from common proxy headers
|
||||
// OR fallbacks to the RemoteIP if none is found.
|
||||
//
|
||||
// NB! The returned IP value could be anything and it shouldn't be trusted if not behind a trusted reverse proxy!
|
||||
func (e *Event) UnsafeRealIP() string {
|
||||
if ip := e.Request.Header.Get("CF-Connecting-IP"); ip != "" {
|
||||
return ip
|
||||
}
|
||||
|
||||
if ip := e.Request.Header.Get("Fly-Client-IP"); ip != "" {
|
||||
return ip
|
||||
}
|
||||
|
||||
if ip := e.Request.Header.Get("X-Real-IP"); ip != "" {
|
||||
return ip
|
||||
}
|
||||
|
||||
if ipsList := e.Request.Header.Get("X-Forwarded-For"); ipsList != "" {
|
||||
// extract the first non-empty leftmost-ish ip
|
||||
ips := strings.Split(ipsList, ",")
|
||||
for _, ip := range ips {
|
||||
ip = strings.TrimSpace(ip)
|
||||
if ip != "" {
|
||||
return ip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e.RemoteIP()
|
||||
}
|
||||
|
||||
// FindUploadedFiles extracts all form files of "key" from a http request
|
||||
// and returns a slice with filesystem.File instances (if any).
|
||||
func (e *Event) FindUploadedFiles(key string) ([]*filesystem.File, error) {
|
||||
|
||||
@@ -219,65 +219,6 @@ func TestEventRemoteIP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventUnsafeRealIP(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []struct {
|
||||
headers map[string]string
|
||||
expected string
|
||||
}{
|
||||
{nil, "1.2.3.4"},
|
||||
{
|
||||
map[string]string{"CF-Connecting-IP": "test"},
|
||||
"test",
|
||||
},
|
||||
{
|
||||
map[string]string{"Fly-Client-IP": "test"},
|
||||
"test",
|
||||
},
|
||||
{
|
||||
map[string]string{"X-Real-IP": "test"},
|
||||
"test",
|
||||
},
|
||||
{
|
||||
map[string]string{"X-Forwarded-For": "test1,test2,test3"},
|
||||
"test1",
|
||||
},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
keys := make([]string, 0, len(s.headers))
|
||||
for h := range s.headers {
|
||||
keys = append(keys, h)
|
||||
}
|
||||
|
||||
testName := strings.Join(keys, "_")
|
||||
if testName == "" {
|
||||
testName = "no_headers" + strconv.Itoa(i)
|
||||
}
|
||||
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, "/", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req.RemoteAddr = "1.2.3.4:80" // fallback
|
||||
|
||||
for k, v := range s.headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
event := router.Event{Request: req}
|
||||
|
||||
ip := event.UnsafeRealIP()
|
||||
|
||||
if ip != s.expected {
|
||||
t.Fatalf("Expected IP %q, got %q", s.expected, ip)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindUploadedFiles(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
filename string
|
||||
|
||||
+31
-8
@@ -34,9 +34,24 @@ var parsedFilterData = store.New(make(map[string][]fexpr.ExprGroup, 50))
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// The parsed expressions are limited up to DefaultFilterExprLimit.
|
||||
// Use [FilterData.BuildExprWithLimit] if you want to set a custom limit.
|
||||
func (f FilterData) BuildExpr(
|
||||
fieldResolver FieldResolver,
|
||||
placeholderReplacements ...dbx.Params,
|
||||
) (dbx.Expression, error) {
|
||||
return f.BuildExprWithLimit(fieldResolver, DefaultFilterExprLimit, placeholderReplacements...)
|
||||
}
|
||||
|
||||
// BuildExpr parses the current filter data and returns a new db WHERE expression.
|
||||
//
|
||||
// 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) BuildExprWithLimit(
|
||||
fieldResolver FieldResolver,
|
||||
maxExpressions int,
|
||||
placeholderReplacements ...dbx.Params,
|
||||
) (dbx.Expression, error) {
|
||||
raw := string(f)
|
||||
|
||||
@@ -64,8 +79,10 @@ func (f FilterData) BuildExpr(
|
||||
}
|
||||
}
|
||||
|
||||
if data, ok := parsedFilterData.GetOk(raw); ok {
|
||||
return buildParsedFilterExpr(data, fieldResolver)
|
||||
cacheKey := raw + "/" + strconv.Itoa(maxExpressions)
|
||||
|
||||
if data, ok := parsedFilterData.GetOk(cacheKey); ok {
|
||||
return buildParsedFilterExpr(data, fieldResolver, &maxExpressions)
|
||||
}
|
||||
|
||||
data, err := fexpr.Parse(raw)
|
||||
@@ -82,14 +99,14 @@ func (f FilterData) BuildExpr(
|
||||
|
||||
// store in cache
|
||||
// (the limit size is arbitrary and it is there to prevent the cache growing too big)
|
||||
parsedFilterData.SetIfLessThanLimit(raw, data, 500)
|
||||
parsedFilterData.SetIfLessThanLimit(cacheKey, data, 500)
|
||||
|
||||
return buildParsedFilterExpr(data, fieldResolver)
|
||||
return buildParsedFilterExpr(data, fieldResolver, &maxExpressions)
|
||||
}
|
||||
|
||||
func buildParsedFilterExpr(data []fexpr.ExprGroup, fieldResolver FieldResolver) (dbx.Expression, error) {
|
||||
func buildParsedFilterExpr(data []fexpr.ExprGroup, fieldResolver FieldResolver, maxExpressions *int) (dbx.Expression, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("empty filter expression")
|
||||
return nil, fexpr.ErrEmpty
|
||||
}
|
||||
|
||||
result := &concatExpr{separator: " "}
|
||||
@@ -100,11 +117,17 @@ func buildParsedFilterExpr(data []fexpr.ExprGroup, fieldResolver FieldResolver)
|
||||
|
||||
switch item := group.Item.(type) {
|
||||
case fexpr.Expr:
|
||||
if *maxExpressions <= 0 {
|
||||
return nil, ErrFilterExprLimit
|
||||
}
|
||||
|
||||
*maxExpressions--
|
||||
|
||||
expr, exprErr = resolveTokenizedExpr(item, fieldResolver)
|
||||
case fexpr.ExprGroup:
|
||||
expr, exprErr = buildParsedFilterExpr([]fexpr.ExprGroup{item}, fieldResolver)
|
||||
expr, exprErr = buildParsedFilterExpr([]fexpr.ExprGroup{item}, fieldResolver, maxExpressions)
|
||||
case []fexpr.ExprGroup:
|
||||
expr, exprErr = buildParsedFilterExpr(item, fieldResolver)
|
||||
expr, exprErr = buildParsedFilterExpr(item, fieldResolver, maxExpressions)
|
||||
default:
|
||||
exprErr = errors.New("unsupported expression item")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package search_test
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -239,6 +240,35 @@ func TestFilterDataBuildExprWithParams(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterDataBuildExprWithLimit(t *testing.T) {
|
||||
resolver := search.NewSimpleFieldResolver(`^\w+$`)
|
||||
|
||||
scenarios := []struct {
|
||||
limit int
|
||||
filter search.FilterData
|
||||
expectError bool
|
||||
}{
|
||||
{1, "1 = 1", false},
|
||||
{0, "1 = 1", true}, // new cache entry should be created
|
||||
{2, "1 = 1 || 1 = 1", false},
|
||||
{1, "1 = 1 || 1 = 1", true},
|
||||
{3, "1 = 1 || 1 = 1", false},
|
||||
{6, "(1=1 || 1=1) && (1=1 || (1=1 || 1=1)) && (1=1)", false},
|
||||
{5, "(1=1 || 1=1) && (1=1 || (1=1 || 1=1)) && (1=1)", true},
|
||||
}
|
||||
|
||||
for i, s := range scenarios {
|
||||
t.Run(fmt.Sprintf("limit_%d:%d", i, s.limit), func(t *testing.T) {
|
||||
_, err := s.filter.BuildExprWithLimit(resolver, s.limit)
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
t.Fatalf("Expected hasErr %v, got %v", s.expectError, hasErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLikeParamsWrapping(t *testing.T) {
|
||||
// create a dummy db
|
||||
sqlDB, err := sql.Open("sqlite", "file::memory:?cache=shared")
|
||||
|
||||
+72
-24
@@ -12,15 +12,36 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// DefaultPerPage specifies the default returned search result items.
|
||||
const DefaultPerPage int = 30
|
||||
const (
|
||||
// DefaultPerPage specifies the default number of returned search result items.
|
||||
DefaultPerPage int = 30
|
||||
|
||||
// @todo consider making it configurable
|
||||
//
|
||||
// MaxPerPage specifies the maximum allowed search result items returned in a single page.
|
||||
const MaxPerPage int = 1000
|
||||
// DefaultFilterExprLimit specifies the default filter expressions limit.
|
||||
DefaultFilterExprLimit int = 200
|
||||
|
||||
// url search query params
|
||||
// DefaultSortExprLimit specifies the default sort expressions limit.
|
||||
DefaultSortExprLimit int = 8
|
||||
|
||||
// MaxPerPage specifies the max allowed search result items returned in a single page.
|
||||
MaxPerPage int = 1000
|
||||
|
||||
// MaxFilterLength specifies the max allowed individual search filter parsable length.
|
||||
MaxFilterLength int = 3500
|
||||
|
||||
// MaxSortFieldLength specifies the max allowed individual sort field parsable length.
|
||||
MaxSortFieldLength int = 255
|
||||
)
|
||||
|
||||
// Common search errors.
|
||||
var (
|
||||
ErrEmptyQuery = errors.New("search query is not set")
|
||||
ErrSortExprLimit = errors.New("max sort expressions limit reached")
|
||||
ErrFilterExprLimit = errors.New("max filter expressions limit reached")
|
||||
ErrFilterLengthLimit = errors.New("max filter length limit reached")
|
||||
ErrSortFieldLengthLimit = errors.New("max sort field length limit reached")
|
||||
)
|
||||
|
||||
// URL search query params
|
||||
const (
|
||||
PageQueryParam string = "page"
|
||||
PerPageQueryParam string = "perPage"
|
||||
@@ -40,17 +61,19 @@ type Result struct {
|
||||
|
||||
// Provider represents a single configured search provider instance.
|
||||
type Provider struct {
|
||||
fieldResolver FieldResolver
|
||||
query *dbx.SelectQuery
|
||||
countCol string
|
||||
sort []SortField
|
||||
filter []FilterData
|
||||
page int
|
||||
perPage int
|
||||
skipTotal bool
|
||||
fieldResolver FieldResolver
|
||||
query *dbx.SelectQuery
|
||||
countCol string
|
||||
sort []SortField
|
||||
filter []FilterData
|
||||
page int
|
||||
perPage int
|
||||
skipTotal bool
|
||||
maxFilterExprLimit int
|
||||
maxSortExprLimit int
|
||||
}
|
||||
|
||||
// NewProvider creates and returns a new search provider.
|
||||
// NewProvider initializes and returns a new search provider.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
@@ -63,15 +86,31 @@ type Provider struct {
|
||||
// ParseAndExec("page=2&filter=id>0&sort=-email", &models)
|
||||
func NewProvider(fieldResolver FieldResolver) *Provider {
|
||||
return &Provider{
|
||||
fieldResolver: fieldResolver,
|
||||
countCol: "id",
|
||||
page: 1,
|
||||
perPage: DefaultPerPage,
|
||||
sort: []SortField{},
|
||||
filter: []FilterData{},
|
||||
fieldResolver: fieldResolver,
|
||||
countCol: "id",
|
||||
page: 1,
|
||||
perPage: DefaultPerPage,
|
||||
sort: []SortField{},
|
||||
filter: []FilterData{},
|
||||
maxFilterExprLimit: DefaultFilterExprLimit,
|
||||
maxSortExprLimit: DefaultSortExprLimit,
|
||||
}
|
||||
}
|
||||
|
||||
// MaxFilterExpressions changes the default max allowed filter expressions.
|
||||
//
|
||||
// Note that currently the limit is applied individually for each separate filter.
|
||||
func (s *Provider) MaxFilterExprLimit(max int) *Provider {
|
||||
s.maxFilterExprLimit = max
|
||||
return s
|
||||
}
|
||||
|
||||
// MaxSortExpressions changes the default max allowed sort expressions.
|
||||
func (s *Provider) MaxSortExprLimit(max int) *Provider {
|
||||
s.maxSortExprLimit = max
|
||||
return s
|
||||
}
|
||||
|
||||
// Query sets the base query that will be used to fetch the search items.
|
||||
func (s *Provider) Query(query *dbx.SelectQuery) *Provider {
|
||||
s.query = query
|
||||
@@ -188,7 +227,7 @@ func (s *Provider) Parse(urlQuery string) error {
|
||||
// the provided `items` slice with the found models.
|
||||
func (s *Provider) Exec(items any) (*Result, error) {
|
||||
if s.query == nil {
|
||||
return nil, errors.New("query is not set")
|
||||
return nil, ErrEmptyQuery
|
||||
}
|
||||
|
||||
// shallow clone the provider's query
|
||||
@@ -196,7 +235,10 @@ func (s *Provider) Exec(items any) (*Result, error) {
|
||||
|
||||
// build filters
|
||||
for _, f := range s.filter {
|
||||
expr, err := f.BuildExpr(s.fieldResolver)
|
||||
if len(f) > MaxFilterLength {
|
||||
return nil, ErrFilterLengthLimit
|
||||
}
|
||||
expr, err := f.BuildExprWithLimit(s.fieldResolver, s.maxFilterExprLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -206,7 +248,13 @@ func (s *Provider) Exec(items any) (*Result, error) {
|
||||
}
|
||||
|
||||
// apply sorting
|
||||
if len(s.sort) > s.maxSortExprLimit {
|
||||
return nil, ErrSortExprLimit
|
||||
}
|
||||
for _, sortField := range s.sort {
|
||||
if len(sortField.Name) > MaxSortFieldLength {
|
||||
return nil, ErrSortFieldLengthLimit
|
||||
}
|
||||
expr, err := sortField.BuildExpr(s.fieldResolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -25,6 +27,46 @@ func TestNewProvider(t *testing.T) {
|
||||
if p.perPage != DefaultPerPage {
|
||||
t.Fatalf("Expected perPage %d, got %d", DefaultPerPage, p.perPage)
|
||||
}
|
||||
|
||||
if p.maxFilterExprLimit != DefaultFilterExprLimit {
|
||||
t.Fatalf("Expected maxFilterExprLimit %d, got %d", DefaultFilterExprLimit, p.maxFilterExprLimit)
|
||||
}
|
||||
|
||||
if p.maxSortExprLimit != DefaultSortExprLimit {
|
||||
t.Fatalf("Expected maxSortExprLimit %d, got %d", DefaultSortExprLimit, p.maxSortExprLimit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxFilterExprLimit(t *testing.T) {
|
||||
p := NewProvider(&testFieldResolver{})
|
||||
|
||||
testVals := []int{0, -10, 10}
|
||||
|
||||
for _, val := range testVals {
|
||||
t.Run("max_"+strconv.Itoa(val), func(t *testing.T) {
|
||||
p.MaxFilterExprLimit(val)
|
||||
|
||||
if p.maxFilterExprLimit != val {
|
||||
t.Fatalf("Expected maxFilterExprLimit to change to %d, got %d", val, p.maxFilterExprLimit)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxSortExprLimit(t *testing.T) {
|
||||
p := NewProvider(&testFieldResolver{})
|
||||
|
||||
testVals := []int{0, -10, 10}
|
||||
|
||||
for _, val := range testVals {
|
||||
t.Run("max_"+strconv.Itoa(val), func(t *testing.T) {
|
||||
p.MaxSortExprLimit(val)
|
||||
|
||||
if p.maxSortExprLimit != val {
|
||||
t.Fatalf("Expected maxSortExprLimit to change to %d, got %d", val, p.maxSortExprLimit)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderQuery(t *testing.T) {
|
||||
@@ -428,6 +470,141 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderFilterAndSortLimits(t *testing.T) {
|
||||
testDB, err := createTestDB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testDB.Close()
|
||||
|
||||
query := testDB.Select("*").
|
||||
From("test").
|
||||
Where(dbx.Not(dbx.HashExp{"test1": nil})).
|
||||
OrderBy("test1 ASC")
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
filter []FilterData
|
||||
sort []SortField
|
||||
maxFilterExprLimit int
|
||||
maxSortExprLimit int
|
||||
expectError bool
|
||||
}{
|
||||
// filter
|
||||
{
|
||||
"<= max filter length",
|
||||
[]FilterData{
|
||||
"1=2",
|
||||
FilterData("1='" + strings.Repeat("a", MaxFilterLength-4) + "'"),
|
||||
},
|
||||
[]SortField{},
|
||||
1,
|
||||
0,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"> max filter length",
|
||||
[]FilterData{
|
||||
"1=2",
|
||||
FilterData("1='" + strings.Repeat("a", MaxFilterLength-3) + "'"),
|
||||
},
|
||||
[]SortField{},
|
||||
1,
|
||||
0,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"<= max filter exprs",
|
||||
[]FilterData{
|
||||
"1=2",
|
||||
"(1=1 || 1=1) && (1=1 || (1=1 || 1=1)) && (1=1)",
|
||||
},
|
||||
[]SortField{},
|
||||
6,
|
||||
0,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"> max filter exprs",
|
||||
[]FilterData{
|
||||
"1=2",
|
||||
"(1=1 || 1=1) && (1=1 || (1=1 || 1=1)) && (1=1)",
|
||||
},
|
||||
[]SortField{},
|
||||
5,
|
||||
0,
|
||||
true,
|
||||
},
|
||||
|
||||
// sort
|
||||
{
|
||||
"<= max sort field length",
|
||||
[]FilterData{},
|
||||
[]SortField{
|
||||
{"id", SortAsc},
|
||||
{"test1", SortDesc},
|
||||
{strings.Repeat("a", MaxSortFieldLength), SortDesc},
|
||||
},
|
||||
0,
|
||||
10,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"> max sort field length",
|
||||
[]FilterData{},
|
||||
[]SortField{
|
||||
{"id", SortAsc},
|
||||
{"test1", SortDesc},
|
||||
{strings.Repeat("b", MaxSortFieldLength+1), SortDesc},
|
||||
},
|
||||
0,
|
||||
10,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"<= max sort exprs",
|
||||
[]FilterData{},
|
||||
[]SortField{
|
||||
{"id", SortAsc},
|
||||
{"test1", SortDesc},
|
||||
},
|
||||
0,
|
||||
2,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"> max sort exprs",
|
||||
[]FilterData{},
|
||||
[]SortField{
|
||||
{"id", SortAsc},
|
||||
{"test1", SortDesc},
|
||||
},
|
||||
0,
|
||||
1,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
testResolver := &testFieldResolver{}
|
||||
p := NewProvider(testResolver).
|
||||
Query(query).
|
||||
Sort(s.sort).
|
||||
Filter(s.filter).
|
||||
MaxFilterExprLimit(s.maxFilterExprLimit).
|
||||
MaxSortExprLimit(s.maxSortExprLimit)
|
||||
|
||||
_, err := p.Exec(&[]testTableStruct{})
|
||||
|
||||
hasErr := err != nil
|
||||
if hasErr != s.expectError {
|
||||
t.Fatalf("Expected hasErr %v, got %v", s.expectError, hasErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProviderParseAndExec(t *testing.T) {
|
||||
testDB, err := createTestDB()
|
||||
if err != nil {
|
||||
@@ -577,7 +754,14 @@ func createTestDB() (*testDB, error) {
|
||||
}
|
||||
|
||||
db := testDB{DB: dbx.NewFromDB(sqlDB, "sqlite")}
|
||||
db.CreateTable("test", map[string]string{"id": "int default 0", "test1": "int default 0", "test2": "text default ''", "test3": "text default ''"}).Execute()
|
||||
db.CreateTable("test", map[string]string{
|
||||
"id": "int default 0",
|
||||
"test1": "int default 0",
|
||||
"test2": "text default ''",
|
||||
"test3": "text default ''",
|
||||
strings.Repeat("a", MaxSortFieldLength): "text default ''",
|
||||
strings.Repeat("b", MaxSortFieldLength+1): "text default ''",
|
||||
}).Execute()
|
||||
db.Insert("test", dbx.Params{"id": 1, "test1": 1, "test2": "test2.1"}).Execute()
|
||||
db.Insert("test", dbx.Params{"id": 2, "test1": 2, "test2": "test2.2"}).Execute()
|
||||
db.QueryLogFunc = func(ctx context.Context, t time.Duration, sql string, rows *sql.Rows, err error) {
|
||||
|
||||
@@ -30,6 +30,11 @@ func (b *Broker) ChunkedClients(chunkSize int) [][]Client {
|
||||
return list.ToChunks(b.store.Values(), chunkSize)
|
||||
}
|
||||
|
||||
// TotalClients returns the total number of registered clients.
|
||||
func (b *Broker) TotalClients() int {
|
||||
return b.store.Length()
|
||||
}
|
||||
|
||||
// ClientById finds a registered client by its id.
|
||||
//
|
||||
// Returns non-nil error when client with clientId is not registered.
|
||||
|
||||
@@ -62,6 +62,21 @@ func TestChunkedClients(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTotalClients(t *testing.T) {
|
||||
b := subscriptions.NewBroker()
|
||||
|
||||
if total := b.TotalClients(); total != 0 {
|
||||
t.Fatalf("Expected no clients, got %d", total)
|
||||
}
|
||||
|
||||
b.Register(subscriptions.NewDefaultClient())
|
||||
b.Register(subscriptions.NewDefaultClient())
|
||||
|
||||
if total := b.TotalClients(); total != 2 {
|
||||
t.Fatalf("Expected %d clients, got %d", 2, total)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientById(t *testing.T) {
|
||||
b := subscriptions.NewBroker()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# all environments should start with 'PB_' prefix
|
||||
PB_BACKEND_URL = "../"
|
||||
PB_INSTALLER_PARAM = "pbinstal"
|
||||
PB_MFA_DOCS = "https://pocketbase.io/docs/authentication/#multi-factor-authentication"
|
||||
PB_OAUTH2_EXAMPLE = "https://pocketbase.io/docs/authentication/#authenticate-with-oauth2"
|
||||
PB_RULES_SYNTAX_DOCS = "https://pocketbase.io/docs/api-rules-and-filters/"
|
||||
@@ -10,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.23.0-rc9"
|
||||
PB_VERSION = "v0.23.0-rc14"
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
import{S as Ce,i as Be,s as Te,Q as Le,T as G,e as c,w as $,b as k,c as ae,f as h,g as d,h as a,m as ne,x as I,U as ye,V as Re,k as Se,W as Ue,n as Qe,t as J,a as N,o as u,d as ie,p as oe,C as je,r as O,u as qe,R as De}from"./index-Co7n6kj7.js";import{F as Ee}from"./FieldsQueryParam-DH-ZC4qv.js";function $e(n,s,l){const o=n.slice();return o[8]=s[l],o}function Me(n,s,l){const o=n.slice();return o[8]=s[l],o}function Ae(n,s){let l,o=s[8].code+"",p,b,i,f;function m(){return s[6](s[8])}return{key:n,first:null,c(){l=c("button"),p=$(o),b=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(v,w){d(v,l,w),a(l,p),a(l,b),i||(f=qe(l,"click",m),i=!0)},p(v,w){s=v,w&4&&o!==(o=s[8].code+"")&&I(p,o),w&6&&O(l,"active",s[1]===s[8].code)},d(v){v&&u(l),i=!1,f()}}}function Pe(n,s){let l,o,p,b;return o=new De({props:{content:s[8].body}}),{key:n,first:null,c(){l=c("div"),ae(o.$$.fragment),p=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(i,f){d(i,l,f),ne(o,l,null),a(l,p),b=!0},p(i,f){s=i;const m={};f&4&&(m.content=s[8].body),o.$set(m),(!b||f&6)&&O(l,"active",s[1]===s[8].code)},i(i){b||(J(o.$$.fragment,i),b=!0)},o(i){N(o.$$.fragment,i),b=!1},d(i){i&&u(l),ie(o)}}}function Fe(n){var ke,ge;let s,l,o=n[0].name+"",p,b,i,f,m,v,w,g=n[0].name+"",V,ce,W,M,z,L,K,A,D,re,E,R,de,X,F=n[0].name+"",Y,ue,Z,S,x,P,ee,fe,te,T,le,U,se,C,Q,y=[],me=new Map,pe,j,_=[],be=new Map,B;M=new Le({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(ke=n[0])==null?void 0:ke.name}').listAuthMethods();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(ge=n[0])==null?void 0:ge.name}').listAuthMethods();
|
||||
`}}),T=new Ee({});let H=G(n[2]);const he=e=>e[8].code;for(let e=0;e<H.length;e+=1){let t=Me(n,H,e),r=he(t);me.set(r,y[e]=Ae(r,t))}let q=G(n[2]);const _e=e=>e[8].code;for(let e=0;e<q.length;e+=1){let t=$e(n,q,e),r=_e(t);be.set(r,_[e]=Pe(r,t))}return{c(){s=c("h3"),l=$("List auth methods ("),p=$(o),b=$(")"),i=k(),f=c("div"),m=c("p"),v=$("Returns a public list with all allowed "),w=c("strong"),V=$(g),ce=$(" authentication methods."),W=k(),ae(M.$$.fragment),z=k(),L=c("h6"),L.textContent="API details",K=k(),A=c("div"),D=c("strong"),D.textContent="GET",re=k(),E=c("div"),R=c("p"),de=$("/api/collections/"),X=c("strong"),Y=$(F),ue=$("/auth-methods"),Z=k(),S=c("div"),S.textContent="Query parameters",x=k(),P=c("table"),ee=c("thead"),ee.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',fe=k(),te=c("tbody"),ae(T.$$.fragment),le=k(),U=c("div"),U.textContent="Responses",se=k(),C=c("div"),Q=c("div");for(let e=0;e<y.length;e+=1)y[e].c();pe=k(),j=c("div");for(let e=0;e<_.length;e+=1)_[e].c();h(s,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(L,"class","m-b-xs"),h(D,"class","label label-primary"),h(E,"class","content"),h(A,"class","alert alert-info"),h(S,"class","section-title"),h(P,"class","table-compact table-border m-b-base"),h(U,"class","section-title"),h(Q,"class","tabs-header compact combined left"),h(j,"class","tabs-content"),h(C,"class","tabs")},m(e,t){d(e,s,t),a(s,l),a(s,p),a(s,b),d(e,i,t),d(e,f,t),a(f,m),a(m,v),a(m,w),a(w,V),a(m,ce),d(e,W,t),ne(M,e,t),d(e,z,t),d(e,L,t),d(e,K,t),d(e,A,t),a(A,D),a(A,re),a(A,E),a(E,R),a(R,de),a(R,X),a(X,Y),a(R,ue),d(e,Z,t),d(e,S,t),d(e,x,t),d(e,P,t),a(P,ee),a(P,fe),a(P,te),ne(T,te,null),d(e,le,t),d(e,U,t),d(e,se,t),d(e,C,t),a(C,Q);for(let r=0;r<y.length;r+=1)y[r]&&y[r].m(Q,null);a(C,pe),a(C,j);for(let r=0;r<_.length;r+=1)_[r]&&_[r].m(j,null);B=!0},p(e,[t]){var ve,we;(!B||t&1)&&o!==(o=e[0].name+"")&&I(p,o),(!B||t&1)&&g!==(g=e[0].name+"")&&I(V,g);const r={};t&9&&(r.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(ve=e[0])==null?void 0:ve.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();
|
||||
`),M.$set(r),(!B||t&1)&&F!==(F=e[0].name+"")&&I(Y,F),t&6&&(H=G(e[2]),y=ye(y,t,he,1,e,H,me,Q,Re,Ae,null,Me)),t&6&&(q=G(e[2]),Se(),_=ye(_,t,_e,1,e,q,be,j,Ue,Pe,null,$e),Qe())},i(e){if(!B){J(M.$$.fragment,e),J(T.$$.fragment,e);for(let t=0;t<q.length;t+=1)J(_[t]);B=!0}},o(e){N(M.$$.fragment,e),N(T.$$.fragment,e);for(let t=0;t<_.length;t+=1)N(_[t]);B=!1},d(e){e&&(u(s),u(i),u(f),u(W),u(z),u(L),u(K),u(A),u(Z),u(S),u(x),u(P),u(le),u(U),u(se),u(C)),ie(M,e),ie(T);for(let t=0;t<y.length;t+=1)y[t].d();for(let t=0;t<_.length;t+=1)_[t].d()}}}function He(n,s,l){let o,{collection:p}=s,b=200,i=[],f={},m=!1;v();async function v(){l(5,m=!0);try{l(4,f=await oe.collection(p.name).listAuthMethods())}catch(g){oe.error(g)}l(5,m=!1)}const w=g=>l(1,b=g.code);return n.$$set=g=>{"collection"in g&&l(0,p=g.collection)},n.$$.update=()=>{n.$$.dirty&48&&l(2,i=[{code:200,body:m?"...":JSON.stringify(f,null,2)},{code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "Missing collection context.",
|
||||
"data": {}
|
||||
}
|
||||
`}])},l(3,o=je.getApiExampleUrl(oe.baseURL)),[p,b,i,o,f,m,w]}class Je extends Ce{constructor(s){super(),Be(this,s,He,Fe,Te,{collection:0})}}export{Je as default};
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import{S as Be,i as Ce,s as Te,U as Le,W as I,f as c,y as w,h as k,c as ae,j as h,l as d,n as a,m as ne,G as Q,X as $e,Y as Se,D as Ue,Z as je,E as De,t as J,a as N,u,d as ie,p as oe,I as Ee,k as O,o as Re,V as qe}from"./index-0yJTQg7M.js";import{F as Fe}from"./FieldsQueryParam-BrZEETlt.js";function we(n,s,l){const o=n.slice();return o[8]=s[l],o}function Me(n,s,l){const o=n.slice();return o[8]=s[l],o}function Ae(n,s){let l,o=s[8].code+"",p,b,i,f;function m(){return s[6](s[8])}return{key:n,first:null,c(){l=c("button"),p=w(o),b=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(v,y){d(v,l,y),a(l,p),a(l,b),i||(f=Re(l,"click",m),i=!0)},p(v,y){s=v,y&4&&o!==(o=s[8].code+"")&&Q(p,o),y&6&&O(l,"active",s[1]===s[8].code)},d(v){v&&u(l),i=!1,f()}}}function Pe(n,s){let l,o,p,b;return o=new qe({props:{content:s[8].body}}),{key:n,first:null,c(){l=c("div"),ae(o.$$.fragment),p=k(),h(l,"class","tab-item"),O(l,"active",s[1]===s[8].code),this.first=l},m(i,f){d(i,l,f),ne(o,l,null),a(l,p),b=!0},p(i,f){s=i;const m={};f&4&&(m.content=s[8].body),o.$set(m),(!b||f&6)&&O(l,"active",s[1]===s[8].code)},i(i){b||(J(o.$$.fragment,i),b=!0)},o(i){N(o.$$.fragment,i),b=!1},d(i){i&&u(l),ie(o)}}}function Ge(n){var ke,ge;let s,l,o=n[0].name+"",p,b,i,f,m,v,y,g=n[0].name+"",V,ce,W,M,X,L,Y,A,q,re,F,S,de,Z,G=n[0].name+"",z,ue,K,U,x,P,ee,fe,te,T,le,j,se,B,D,$=[],me=new Map,pe,E,_=[],be=new Map,C;M=new Le({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(ke=n[0])==null?void 0:ke.name}').listAuthMethods();
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[3]}');
|
||||
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(ge=n[0])==null?void 0:ge.name}').listAuthMethods();
|
||||
`}}),T=new Fe({});let H=I(n[2]);const he=e=>e[8].code;for(let e=0;e<H.length;e+=1){let t=Me(n,H,e),r=he(t);me.set(r,$[e]=Ae(r,t))}let R=I(n[2]);const _e=e=>e[8].code;for(let e=0;e<R.length;e+=1){let t=we(n,R,e),r=_e(t);be.set(r,_[e]=Pe(r,t))}return{c(){s=c("h3"),l=w("List auth methods ("),p=w(o),b=w(")"),i=k(),f=c("div"),m=c("p"),v=w("Returns a public list with all allowed "),y=c("strong"),V=w(g),ce=w(" authentication methods."),W=k(),ae(M.$$.fragment),X=k(),L=c("h6"),L.textContent="API details",Y=k(),A=c("div"),q=c("strong"),q.textContent="GET",re=k(),F=c("div"),S=c("p"),de=w("/api/collections/"),Z=c("strong"),z=w(G),ue=w("/auth-methods"),K=k(),U=c("div"),U.textContent="Query parameters",x=k(),P=c("table"),ee=c("thead"),ee.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',fe=k(),te=c("tbody"),ae(T.$$.fragment),le=k(),j=c("div"),j.textContent="Responses",se=k(),B=c("div"),D=c("div");for(let e=0;e<$.length;e+=1)$[e].c();pe=k(),E=c("div");for(let e=0;e<_.length;e+=1)_[e].c();h(s,"class","m-b-sm"),h(f,"class","content txt-lg m-b-sm"),h(L,"class","m-b-xs"),h(q,"class","label label-primary"),h(F,"class","content"),h(A,"class","alert alert-info"),h(U,"class","section-title"),h(P,"class","table-compact table-border m-b-base"),h(j,"class","section-title"),h(D,"class","tabs-header compact combined left"),h(E,"class","tabs-content"),h(B,"class","tabs")},m(e,t){d(e,s,t),a(s,l),a(s,p),a(s,b),d(e,i,t),d(e,f,t),a(f,m),a(m,v),a(m,y),a(y,V),a(m,ce),d(e,W,t),ne(M,e,t),d(e,X,t),d(e,L,t),d(e,Y,t),d(e,A,t),a(A,q),a(A,re),a(A,F),a(F,S),a(S,de),a(S,Z),a(Z,z),a(S,ue),d(e,K,t),d(e,U,t),d(e,x,t),d(e,P,t),a(P,ee),a(P,fe),a(P,te),ne(T,te,null),d(e,le,t),d(e,j,t),d(e,se,t),d(e,B,t),a(B,D);for(let r=0;r<$.length;r+=1)$[r]&&$[r].m(D,null);a(B,pe),a(B,E);for(let r=0;r<_.length;r+=1)_[r]&&_[r].m(E,null);C=!0},p(e,[t]){var ve,ye;(!C||t&1)&&o!==(o=e[0].name+"")&&Q(p,o),(!C||t&1)&&g!==(g=e[0].name+"")&&Q(V,g);const r={};t&9&&(r.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
const result = await pb.collection('${(ve=e[0])==null?void 0:ve.name}').listAuthMethods();
|
||||
`),t&9&&(r.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
final result = await pb.collection('${(ye=e[0])==null?void 0:ye.name}').listAuthMethods();
|
||||
`),M.$set(r),(!C||t&1)&&G!==(G=e[0].name+"")&&Q(z,G),t&6&&(H=I(e[2]),$=$e($,t,he,1,e,H,me,D,Se,Ae,null,Me)),t&6&&(R=I(e[2]),Ue(),_=$e(_,t,_e,1,e,R,be,E,je,Pe,null,we),De())},i(e){if(!C){J(M.$$.fragment,e),J(T.$$.fragment,e);for(let t=0;t<R.length;t+=1)J(_[t]);C=!0}},o(e){N(M.$$.fragment,e),N(T.$$.fragment,e);for(let t=0;t<_.length;t+=1)N(_[t]);C=!1},d(e){e&&(u(s),u(i),u(f),u(W),u(X),u(L),u(Y),u(A),u(K),u(U),u(x),u(P),u(le),u(j),u(se),u(B)),ie(M,e),ie(T);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<_.length;t+=1)_[t].d()}}}function He(n,s,l){let o,{collection:p}=s,b=200,i=[],f={},m=!1;v();async function v(){l(5,m=!0);try{l(4,f=await oe.collection(p.name).listAuthMethods())}catch(g){oe.error(g)}l(5,m=!1)}const y=g=>l(1,b=g.code);return n.$$set=g=>{"collection"in g&&l(0,p=g.collection)},n.$$.update=()=>{n.$$.dirty&48&&l(2,i=[{code:200,body:m?"...":JSON.stringify(f,null,2)},{code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "Missing collection context.",
|
||||
"data": {}
|
||||
}
|
||||
`}])},l(3,o=Ee.getApiExampleUrl(oe.baseURL)),[p,b,i,o,f,m,y]}class Je extends Be{constructor(s){super(),Ce(this,s,He,Ge,Te,{collection:0})}}export{Je as default};
|
||||
-79
@@ -1,79 +0,0 @@
|
||||
import{S as Qe,i as je,s as Je,Q as Ke,R as Ne,T 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 W,x as de,U as Oe,V as We,k as Ie,W as Ge,n as Xe,t as E,a as U,o as u,d as I,C as Ve,p as Ye,r as G,u as Ze}from"./index-Co7n6kj7.js";import{F as et}from"./FieldsQueryParam-DH-ZC4qv.js";function Ee(r,a,l){const n=r.slice();return n[5]=a[l],n}function Ue(r,a,l){const n=r.slice();return n[5]=a[l],n}function xe(r,a){let l,n=a[5].code+"",m,_,i,h;function g(){return a[4](a[5])}return{key:r,first:null,c(){l=s("button"),m=k(n),_=p(),b(l,"class","tab-item"),G(l,"active",a[1]===a[5].code),this.first=l},m(v,w){d(v,l,w),o(l,m),o(l,_),i||(h=Ze(l,"click",g),i=!0)},p(v,w){a=v,w&4&&n!==(n=a[5].code+"")&&de(m,n),w&6&&G(l,"active",a[1]===a[5].code)},d(v){v&&u(l),i=!1,h()}}}function ze(r,a){let l,n,m,_;return n=new Ne({props:{content:a[5].body}}),{key:r,first:null,c(){l=s("div"),K(n.$$.fragment),m=p(),b(l,"class","tab-item"),G(l,"active",a[1]===a[5].code),this.first=l},m(i,h){d(i,l,h),W(n,l,null),o(l,m),_=!0},p(i,h){a=i;const g={};h&4&&(g.content=a[5].body),n.$set(g),(!_||h&6)&&G(l,"active",a[1]===a[5].code)},i(i){_||(E(n.$$.fragment,i),_=!0)},o(i){U(n.$$.fragment,i),_=!1},d(i){i&&u(l),I(n)}}}function tt(r){var De,Fe;let a,l,n=r[0].name+"",m,_,i,h,g,v,w,M,X,S,x,ue,z,q,pe,Y,N=r[0].name+"",Z,he,fe,Q,ee,D,te,T,oe,be,F,C,ae,me,le,_e,f,ke,P,ge,ve,$e,se,ye,ne,Se,we,Te,re,Ce,Re,A,ie,H,ce,R,L,y=[],Pe=new Map,Ae,O,$=[],Be=new Map,B;v=new Ke({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r[3]}');
|
||||
|
||||
...
|
||||
|
||||
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.record.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.record.id);
|
||||
`}}),P=new Ne({props:{content:"?expand=relField1,relField2.subRelField"}}),A=new et({props:{prefix:"record."}});let j=J(r[2]);const Me=e=>e[5].code;for(let e=0;e<j.length;e+=1){let t=Ue(r,j,e),c=Me(t);Pe.set(c,y[e]=xe(c,t))}let V=J(r[2]);const qe=e=>e[5].code;for(let e=0;e<V.length;e+=1){let t=Ee(r,V,e),c=qe(t);Be.set(c,$[e]=ze(c,t))}return{c(){a=s("h3"),l=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>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.</p>`,g=p(),K(v.$$.fragment),w=p(),M=s("h6"),M.textContent="API details",X=p(),S=s("div"),x=s("strong"),x.textContent="POST",ue=p(),z=s("div"),q=s("p"),pe=k("/api/collections/"),Y=s("strong"),Z=k(N),he=k("/auth-refresh"),fe=p(),Q=s("p"),Q.innerHTML="Requires <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"),ae=s("td"),ae.textContent="expand",me=p(),le=s("td"),le.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ke=k(`Auto expand record relations. Ex.:
|
||||
`),K(P.$$.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."),Re=p(),K(A.$$.fragment),ie=p(),H=s("div"),H.textContent="Responses",ce=p(),R=s("div"),L=s("div");for(let e=0;e<y.length;e+=1)y[e].c();Ae=p(),O=s("div");for(let e=0;e<$.length;e+=1)$[e].c();b(a,"class","m-b-sm"),b(h,"class","content txt-lg m-b-sm"),b(M,"class","m-b-xs"),b(x,"class","label label-primary"),b(z,"class","content"),b(Q,"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(L,"class","tabs-header compact combined left"),b(O,"class","tabs-content"),b(R,"class","tabs")},m(e,t){d(e,a,t),o(a,l),o(a,m),o(a,_),d(e,i,t),d(e,h,t),d(e,g,t),W(v,e,t),d(e,w,t),d(e,M,t),d(e,X,t),d(e,S,t),o(S,x),o(S,ue),o(S,z),o(z,q),o(q,pe),o(q,Y),o(Y,Z),o(q,he),o(S,fe),o(S,Q),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,ae),o(C,me),o(C,le),o(C,_e),o(C,f),o(f,ke),W(P,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,Re),W(A,F,null),d(e,ie,t),d(e,H,t),d(e,ce,t),d(e,R,t),o(R,L);for(let c=0;c<y.length;c+=1)y[c]&&y[c].m(L,null);o(R,Ae),o(R,O);for(let c=0;c<$.length;c+=1)$[c]&&$[c].m(O,null);B=!0},p(e,[t]){var He,Le;(!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('${(He=e[0])==null?void 0:He.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.record.id);
|
||||
`),t&9&&(c.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${e[3]}');
|
||||
|
||||
...
|
||||
|
||||
final authData = await pb.collection('${(Le=e[0])==null?void 0:Le.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.record.id);
|
||||
`),v.$set(c),(!B||t&1)&&N!==(N=e[0].name+"")&&de(Z,N),t&6&&(j=J(e[2]),y=Oe(y,t,Me,1,e,j,Pe,L,We,xe,null,Ue)),t&6&&(V=J(e[2]),Ie(),$=Oe($,t,qe,1,e,V,Be,O,Ge,ze,null,Ee),Xe())},i(e){if(!B){E(v.$$.fragment,e),E(P.$$.fragment,e),E(A.$$.fragment,e);for(let t=0;t<V.length;t+=1)E($[t]);B=!0}},o(e){U(v.$$.fragment,e),U(P.$$.fragment,e),U(A.$$.fragment,e);for(let t=0;t<$.length;t+=1)U($[t]);B=!1},d(e){e&&(u(a),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(H),u(ce),u(R)),I(v,e),I(P),I(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,a,l){let n,{collection:m}=a,_=200,i=[];const h=g=>l(1,_=g.code);return r.$$set=g=>{"collection"in g&&l(0,m=g.collection)},r.$$.update=()=>{r.$$.dirty&1&&l(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Ve.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": {}
|
||||
}
|
||||
`}])},l(3,n=Ve.getApiExampleUrl(Ye.baseURL)),[m,_,i,n,h]}class st extends Qe{constructor(a){super(),je(this,a,ot,tt,Je,{collection:0})}}export{st as default};
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import{S as xe,i as Ie,s as Je,U as Ke,V as Ne,W as J,f as s,y as k,h as p,c as K,j as b,l as d,n as o,m as Q,G as de,X as Le,Y as Qe,D as We,Z as Ge,E as Xe,t as V,a as U,u,d as W,I as Oe,p as Ye,k as G,o as Ze}from"./index-0yJTQg7M.js";import{F as et}from"./FieldsQueryParam-BrZEETlt.js";function Ve(r,a,l){const n=r.slice();return n[5]=a[l],n}function Ue(r,a,l){const n=r.slice();return n[5]=a[l],n}function je(r,a){let l,n=a[5].code+"",m,_,i,h;function g(){return a[4](a[5])}return{key:r,first:null,c(){l=s("button"),m=k(n),_=p(),b(l,"class","tab-item"),G(l,"active",a[1]===a[5].code),this.first=l},m(v,w){d(v,l,w),o(l,m),o(l,_),i||(h=Ze(l,"click",g),i=!0)},p(v,w){a=v,w&4&&n!==(n=a[5].code+"")&&de(m,n),w&6&&G(l,"active",a[1]===a[5].code)},d(v){v&&u(l),i=!1,h()}}}function ze(r,a){let l,n,m,_;return n=new Ne({props:{content:a[5].body}}),{key:r,first:null,c(){l=s("div"),K(n.$$.fragment),m=p(),b(l,"class","tab-item"),G(l,"active",a[1]===a[5].code),this.first=l},m(i,h){d(i,l,h),Q(n,l,null),o(l,m),_=!0},p(i,h){a=i;const g={};h&4&&(g.content=a[5].body),n.$set(g),(!_||h&6)&&G(l,"active",a[1]===a[5].code)},i(i){_||(V(n.$$.fragment,i),_=!0)},o(i){U(n.$$.fragment,i),_=!1},d(i){i&&u(l),W(n)}}}function tt(r){var qe,Fe;let a,l,n=r[0].name+"",m,_,i,h,g,v,w,D,X,S,j,ue,z,M,pe,Y,N=r[0].name+"",Z,he,fe,x,ee,q,te,T,oe,be,F,C,ae,me,le,_e,f,ke,P,ge,ve,ye,se,$e,ne,Se,we,Te,re,Ce,Re,A,ie,E,ce,R,H,$=[],Pe=new Map,Ae,L,y=[],Be=new Map,B;v=new Ke({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.record.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.record.id);
|
||||
`}}),P=new Ne({props:{content:"?expand=relField1,relField2.subRelField"}}),A=new et({props:{prefix:"record."}});let I=J(r[2]);const De=e=>e[5].code;for(let e=0;e<I.length;e+=1){let t=Ue(r,I,e),c=De(t);Pe.set(c,$[e]=je(c,t))}let O=J(r[2]);const Me=e=>e[5].code;for(let e=0;e<O.length;e+=1){let t=Ve(r,O,e),c=Me(t);Be.set(c,y[e]=ze(c,t))}return{c(){a=s("h3"),l=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>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.</p>`,g=p(),K(v.$$.fragment),w=p(),D=s("h6"),D.textContent="API details",X=p(),S=s("div"),j=s("strong"),j.textContent="POST",ue=p(),z=s("div"),M=s("p"),pe=k("/api/collections/"),Y=s("strong"),Z=k(N),he=k("/auth-refresh"),fe=p(),x=s("p"),x.innerHTML="Requires <code>Authorization:TOKEN</code> header",ee=p(),q=s("div"),q.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"),ae=s("td"),ae.textContent="expand",me=p(),le=s("td"),le.innerHTML='<span class="label">String</span>',_e=p(),f=s("td"),ke=k(`Auto expand record relations. Ex.:
|
||||
`),K(P.$$.fragment),ge=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),ve=s("br"),ye=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),se=s("code"),se.textContent="expand",$e=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."),Re=p(),K(A.$$.fragment),ie=p(),E=s("div"),E.textContent="Responses",ce=p(),R=s("div"),H=s("div");for(let e=0;e<$.length;e+=1)$[e].c();Ae=p(),L=s("div");for(let e=0;e<y.length;e+=1)y[e].c();b(a,"class","m-b-sm"),b(h,"class","content txt-lg m-b-sm"),b(D,"class","m-b-xs"),b(j,"class","label label-primary"),b(z,"class","content"),b(x,"class","txt-hint txt-sm txt-right"),b(S,"class","alert alert-success"),b(q,"class","section-title"),b(T,"class","table-compact table-border m-b-base"),b(E,"class","section-title"),b(H,"class","tabs-header compact combined left"),b(L,"class","tabs-content"),b(R,"class","tabs")},m(e,t){d(e,a,t),o(a,l),o(a,m),o(a,_),d(e,i,t),d(e,h,t),d(e,g,t),Q(v,e,t),d(e,w,t),d(e,D,t),d(e,X,t),d(e,S,t),o(S,j),o(S,ue),o(S,z),o(z,M),o(M,pe),o(M,Y),o(Y,Z),o(M,he),o(S,fe),o(S,x),d(e,ee,t),d(e,q,t),d(e,te,t),d(e,T,t),o(T,oe),o(T,be),o(T,F),o(F,C),o(C,ae),o(C,me),o(C,le),o(C,_e),o(C,f),o(f,ke),Q(P,f,null),o(f,ge),o(f,ve),o(f,ye),o(f,se),o(f,$e),o(f,ne),o(f,Se),o(f,we),o(f,Te),o(f,re),o(f,Ce),o(F,Re),Q(A,F,null),d(e,ie,t),d(e,E,t),d(e,ce,t),d(e,R,t),o(R,H);for(let c=0;c<$.length;c+=1)$[c]&&$[c].m(H,null);o(R,Ae),o(R,L);for(let c=0;c<y.length;c+=1)y[c]&&y[c].m(L,null);B=!0},p(e,[t]){var Ee,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('${(Ee=e[0])==null?void 0:Ee.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.record.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.record.id);
|
||||
`),v.$set(c),(!B||t&1)&&N!==(N=e[0].name+"")&&de(Z,N),t&6&&(I=J(e[2]),$=Le($,t,De,1,e,I,Pe,H,Qe,je,null,Ue)),t&6&&(O=J(e[2]),We(),y=Le(y,t,Me,1,e,O,Be,L,Ge,ze,null,Ve),Xe())},i(e){if(!B){V(v.$$.fragment,e),V(P.$$.fragment,e),V(A.$$.fragment,e);for(let t=0;t<O.length;t+=1)V(y[t]);B=!0}},o(e){U(v.$$.fragment,e),U(P.$$.fragment,e),U(A.$$.fragment,e);for(let t=0;t<y.length;t+=1)U(y[t]);B=!1},d(e){e&&(u(a),u(i),u(h),u(g),u(w),u(D),u(X),u(S),u(ee),u(q),u(te),u(T),u(ie),u(E),u(ce),u(R)),W(v,e),W(P),W(A);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<y.length;t+=1)y[t].d()}}}function ot(r,a,l){let n,{collection:m}=a,_=200,i=[];const h=g=>l(1,_=g.code);return r.$$set=g=>{"collection"in g&&l(0,m=g.collection)},r.$$.update=()=>{r.$$.dirty&1&&l(2,i=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:Oe.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": {}
|
||||
}
|
||||
`}])},l(3,n=Oe.getApiExampleUrl(Ye.baseURL)),[m,_,i,n,h]}class st extends xe{constructor(a){super(),Ie(this,a,ot,tt,Je,{collection:0})}}export{st as default};
|
||||
Vendored
+9
-9
@@ -1,4 +1,4 @@
|
||||
import{S as xe,i as Ee,s as Je,Q as Qe,R as je,T as z,e as o,w as k,b as h,c as I,f as p,g as r,h as a,m as K,x as pe,U as Ue,V as Ne,k as ze,W as Ie,n as Ke,t as j,a as x,o as c,d as G,C as Be,p as Ge,r as X,u as Xe}from"./index-Co7n6kj7.js";import{F as Ye}from"./FieldsQueryParam-DH-ZC4qv.js";function Fe(s,l,n){const i=s.slice();return i[5]=l[n],i}function Le(s,l,n){const i=s.slice();return i[5]=l[n],i}function He(s,l){let n,i=l[5].code+"",f,g,d,b;function _(){return l[4](l[5])}return{key:s,first:null,c(){n=o("button"),f=k(i),g=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(v,O){r(v,n,O),a(n,f),a(n,g),d||(b=Xe(n,"click",_),d=!0)},p(v,O){l=v,O&4&&i!==(i=l[5].code+"")&&pe(f,i),O&6&&X(n,"active",l[1]===l[5].code)},d(v){v&&c(n),d=!1,b()}}}function Ve(s,l){let n,i,f,g;return i=new je({props:{content:l[5].body}}),{key:s,first:null,c(){n=o("div"),I(i.$$.fragment),f=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(d,b){r(d,n,b),K(i,n,null),a(n,f),g=!0},p(d,b){l=d;const _={};b&4&&(_.content=l[5].body),i.$set(_),(!g||b&6)&&X(n,"active",l[1]===l[5].code)},i(d){g||(j(i.$$.fragment,d),g=!0)},o(d){x(i.$$.fragment,d),g=!1},d(d){d&&c(n),G(i)}}}function Ze(s){let l,n,i=s[0].name+"",f,g,d,b,_,v,O,D,Y,A,E,be,J,P,me,Z,Q=s[0].name+"",ee,fe,te,M,ae,W,le,U,ne,y,oe,ge,B,S,se,ke,ie,_e,m,ve,C,we,$e,Oe,re,Ae,ce,ye,Se,Te,de,Ce,qe,q,ue,F,he,T,L,$=[],Re=new Map,De,H,w=[],Pe=new Map,R;v=new Qe({props:{js:`
|
||||
import{S as Ee,i as Je,s as xe,U as Ie,V as Ve,W as Q,f as o,y as k,h,c as z,j as p,l as r,n as a,m as G,G as pe,X as Ue,Y as Ne,D as Qe,Z as ze,E as Ge,t as V,a as E,u as c,d as K,I as Be,p as Ke,k as X,o as Xe}from"./index-0yJTQg7M.js";import{F as Ye}from"./FieldsQueryParam-BrZEETlt.js";function Fe(s,l,n){const i=s.slice();return i[5]=l[n],i}function Le(s,l,n){const i=s.slice();return i[5]=l[n],i}function je(s,l){let n,i=l[5].code+"",f,g,d,b;function _(){return l[4](l[5])}return{key:s,first:null,c(){n=o("button"),f=k(i),g=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(v,O){r(v,n,O),a(n,f),a(n,g),d||(b=Xe(n,"click",_),d=!0)},p(v,O){l=v,O&4&&i!==(i=l[5].code+"")&&pe(f,i),O&6&&X(n,"active",l[1]===l[5].code)},d(v){v&&c(n),d=!1,b()}}}function He(s,l){let n,i,f,g;return i=new Ve({props:{content:l[5].body}}),{key:s,first:null,c(){n=o("div"),z(i.$$.fragment),f=h(),p(n,"class","tab-item"),X(n,"active",l[1]===l[5].code),this.first=n},m(d,b){r(d,n,b),G(i,n,null),a(n,f),g=!0},p(d,b){l=d;const _={};b&4&&(_.content=l[5].body),i.$set(_),(!g||b&6)&&X(n,"active",l[1]===l[5].code)},i(d){g||(V(i.$$.fragment,d),g=!0)},o(d){E(i.$$.fragment,d),g=!1},d(d){d&&c(n),K(i)}}}function Ze(s){let l,n,i=s[0].name+"",f,g,d,b,_,v,O,R,Y,y,J,be,x,P,me,Z,I=s[0].name+"",ee,fe,te,M,ae,W,le,U,ne,A,oe,ge,B,S,se,ke,ie,_e,m,ve,C,we,$e,Oe,re,ye,ce,Ae,Se,Te,de,Ce,qe,q,ue,F,he,T,L,$=[],De=new Map,Re,j,w=[],Pe=new Map,D;v=new Ie({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${s[3]}');
|
||||
@@ -45,18 +45,18 @@ import{S as xe,i as Ee,s as Je,Q as Qe,R as je,T as z,e as o,w as k,b as h,c as
|
||||
|
||||
// "logout"
|
||||
pb.authStore.clear();
|
||||
`}}),C=new je({props:{content:"?expand=relField1,relField2.subRelField"}}),q=new Ye({props:{prefix:"record."}});let N=z(s[2]);const Me=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=Le(s,N,e),u=Me(t);Re.set(u,$[e]=He(u,t))}let V=z(s[2]);const We=e=>e[5].code;for(let e=0;e<V.length;e+=1){let t=Fe(s,V,e),u=We(t);Pe.set(u,w[e]=Ve(u,t))}return{c(){l=o("h3"),n=k("Auth with OAuth2 ("),f=k(i),g=k(")"),d=h(),b=o("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p> <p>For more details please check the
|
||||
`}}),C=new Ve({props:{content:"?expand=relField1,relField2.subRelField"}}),q=new Ye({props:{prefix:"record."}});let N=Q(s[2]);const Me=e=>e[5].code;for(let e=0;e<N.length;e+=1){let t=Le(s,N,e),u=Me(t);De.set(u,$[e]=je(u,t))}let H=Q(s[2]);const We=e=>e[5].code;for(let e=0;e<H.length;e+=1){let t=Fe(s,H,e),u=We(t);Pe.set(u,w[e]=He(u,t))}return{c(){l=o("h3"),n=k("Auth with OAuth2 ("),f=k(i),g=k(")"),d=h(),b=o("div"),b.innerHTML=`<p>Authenticate with an OAuth2 provider and returns a new auth token and record data.</p> <p>For more details please check the
|
||||
<a href="https://pocketbase.io/docs/authentication/#authenticate-with-oauth2" target="_blank" rel="noopener noreferrer">OAuth2 integration documentation
|
||||
</a>.</p>`,_=h(),I(v.$$.fragment),O=h(),D=o("h6"),D.textContent="API details",Y=h(),A=o("div"),E=o("strong"),E.textContent="POST",be=h(),J=o("div"),P=o("p"),me=k("/api/collections/"),Z=o("strong"),ee=k(Q),fe=k("/auth-with-oauth2"),te=h(),M=o("div"),M.textContent="Body Parameters",ae=h(),W=o("table"),W.innerHTML=`<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>provider</span></div></td> <td><span class="label">String</span></td> <td>The name of the OAuth2 client provider (eg. "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
|
||||
</a>.</p>`,_=h(),z(v.$$.fragment),O=h(),R=o("h6"),R.textContent="API details",Y=h(),y=o("div"),J=o("strong"),J.textContent="POST",be=h(),x=o("div"),P=o("p"),me=k("/api/collections/"),Z=o("strong"),ee=k(I),fe=k("/auth-with-oauth2"),te=h(),M=o("div"),M.textContent="Body Parameters",ae=h(),W=o("table"),W.innerHTML=`<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>provider</span></div></td> <td><span class="label">String</span></td> <td>The name of the OAuth2 client provider (eg. "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(),U=o("div"),U.textContent="Query parameters",ne=h(),y=o("table"),oe=o("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',ge=h(),B=o("tbody"),S=o("tr"),se=o("td"),se.textContent="expand",ke=h(),ie=o("td"),ie.innerHTML='<span class="label">String</span>',_e=h(),m=o("td"),ve=k(`Auto expand record relations. Ex.:
|
||||
`),I(C.$$.fragment),we=k(`
|
||||
upload currently are not supported during OAuth2 sign-ups.</em></p></td></tr></tbody>`,le=h(),U=o("div"),U.textContent="Query parameters",ne=h(),A=o("table"),oe=o("thead"),oe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',ge=h(),B=o("tbody"),S=o("tr"),se=o("td"),se.textContent="expand",ke=h(),ie=o("td"),ie.innerHTML='<span class="label">String</span>',_e=h(),m=o("td"),ve=k(`Auto expand record relations. Ex.:
|
||||
`),z(C.$$.fragment),we=k(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),$e=o("br"),Oe=k(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),re=o("code"),re.textContent="expand",Ae=k(" property (eg. "),ce=o("code"),ce.textContent='"expand": {"relField1": {...}, ...}',ye=k(`).
|
||||
`),re=o("code"),re.textContent="expand",ye=k(" property (eg. "),ce=o("code"),ce.textContent='"expand": {"relField1": {...}, ...}',Ae=k(`).
|
||||
`),Se=o("br"),Te=k(`
|
||||
Only the relations to which the request user has permissions to `),de=o("strong"),de.textContent="view",Ce=k(" will be expanded."),qe=h(),I(q.$$.fragment),ue=h(),F=o("div"),F.textContent="Responses",he=h(),T=o("div"),L=o("div");for(let e=0;e<$.length;e+=1)$[e].c();De=h(),H=o("div");for(let e=0;e<w.length;e+=1)w[e].c();p(l,"class","m-b-sm"),p(b,"class","content txt-lg m-b-sm"),p(D,"class","m-b-xs"),p(E,"class","label label-primary"),p(J,"class","content"),p(A,"class","alert alert-success"),p(M,"class","section-title"),p(W,"class","table-compact table-border m-b-base"),p(U,"class","section-title"),p(y,"class","table-compact table-border m-b-base"),p(F,"class","section-title"),p(L,"class","tabs-header compact combined left"),p(H,"class","tabs-content"),p(T,"class","tabs")},m(e,t){r(e,l,t),a(l,n),a(l,f),a(l,g),r(e,d,t),r(e,b,t),r(e,_,t),K(v,e,t),r(e,O,t),r(e,D,t),r(e,Y,t),r(e,A,t),a(A,E),a(A,be),a(A,J),a(J,P),a(P,me),a(P,Z),a(Z,ee),a(P,fe),r(e,te,t),r(e,M,t),r(e,ae,t),r(e,W,t),r(e,le,t),r(e,U,t),r(e,ne,t),r(e,y,t),a(y,oe),a(y,ge),a(y,B),a(B,S),a(S,se),a(S,ke),a(S,ie),a(S,_e),a(S,m),a(m,ve),K(C,m,null),a(m,we),a(m,$e),a(m,Oe),a(m,re),a(m,Ae),a(m,ce),a(m,ye),a(m,Se),a(m,Te),a(m,de),a(m,Ce),a(B,qe),K(q,B,null),r(e,ue,t),r(e,F,t),r(e,he,t),r(e,T,t),a(T,L);for(let u=0;u<$.length;u+=1)$[u]&&$[u].m(L,null);a(T,De),a(T,H);for(let u=0;u<w.length;u+=1)w[u]&&w[u].m(H,null);R=!0},p(e,[t]){(!R||t&1)&&i!==(i=e[0].name+"")&&pe(f,i);const u={};t&9&&(u.js=`
|
||||
Only the relations to which the request user has permissions to `),de=o("strong"),de.textContent="view",Ce=k(" will be expanded."),qe=h(),z(q.$$.fragment),ue=h(),F=o("div"),F.textContent="Responses",he=h(),T=o("div"),L=o("div");for(let e=0;e<$.length;e+=1)$[e].c();Re=h(),j=o("div");for(let e=0;e<w.length;e+=1)w[e].c();p(l,"class","m-b-sm"),p(b,"class","content txt-lg m-b-sm"),p(R,"class","m-b-xs"),p(J,"class","label label-primary"),p(x,"class","content"),p(y,"class","alert alert-success"),p(M,"class","section-title"),p(W,"class","table-compact table-border m-b-base"),p(U,"class","section-title"),p(A,"class","table-compact table-border m-b-base"),p(F,"class","section-title"),p(L,"class","tabs-header compact combined left"),p(j,"class","tabs-content"),p(T,"class","tabs")},m(e,t){r(e,l,t),a(l,n),a(l,f),a(l,g),r(e,d,t),r(e,b,t),r(e,_,t),G(v,e,t),r(e,O,t),r(e,R,t),r(e,Y,t),r(e,y,t),a(y,J),a(y,be),a(y,x),a(x,P),a(P,me),a(P,Z),a(Z,ee),a(P,fe),r(e,te,t),r(e,M,t),r(e,ae,t),r(e,W,t),r(e,le,t),r(e,U,t),r(e,ne,t),r(e,A,t),a(A,oe),a(A,ge),a(A,B),a(B,S),a(S,se),a(S,ke),a(S,ie),a(S,_e),a(S,m),a(m,ve),G(C,m,null),a(m,we),a(m,$e),a(m,Oe),a(m,re),a(m,ye),a(m,ce),a(m,Ae),a(m,Se),a(m,Te),a(m,de),a(m,Ce),a(B,qe),G(q,B,null),r(e,ue,t),r(e,F,t),r(e,he,t),r(e,T,t),a(T,L);for(let u=0;u<$.length;u+=1)$[u]&&$[u].m(L,null);a(T,Re),a(T,j);for(let u=0;u<w.length;u+=1)w[u]&&w[u].m(j,null);D=!0},p(e,[t]){(!D||t&1)&&i!==(i=e[0].name+"")&&pe(f,i);const u={};t&9&&(u.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[3]}');
|
||||
@@ -103,7 +103,7 @@ import{S as xe,i as Ee,s as Je,Q as Qe,R as je,T as z,e as o,w as k,b as h,c as
|
||||
|
||||
// "logout"
|
||||
pb.authStore.clear();
|
||||
`),v.$set(u),(!R||t&1)&&Q!==(Q=e[0].name+"")&&pe(ee,Q),t&6&&(N=z(e[2]),$=Ue($,t,Me,1,e,N,Re,L,Ne,He,null,Le)),t&6&&(V=z(e[2]),ze(),w=Ue(w,t,We,1,e,V,Pe,H,Ie,Ve,null,Fe),Ke())},i(e){if(!R){j(v.$$.fragment,e),j(C.$$.fragment,e),j(q.$$.fragment,e);for(let t=0;t<V.length;t+=1)j(w[t]);R=!0}},o(e){x(v.$$.fragment,e),x(C.$$.fragment,e),x(q.$$.fragment,e);for(let t=0;t<w.length;t+=1)x(w[t]);R=!1},d(e){e&&(c(l),c(d),c(b),c(_),c(O),c(D),c(Y),c(A),c(te),c(M),c(ae),c(W),c(le),c(U),c(ne),c(y),c(ue),c(F),c(he),c(T)),G(v,e),G(C),G(q);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function et(s,l,n){let i,{collection:f}=l,g=200,d=[];const b=_=>n(1,g=_.code);return s.$$set=_=>{"collection"in _&&n(0,f=_.collection)},s.$$.update=()=>{s.$$.dirty&1&&n(2,d=[{code:200,body:JSON.stringify({token:"JWT_AUTH_TOKEN",record:Be.dummyCollectionRecord(f),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarURL:"https://example.com/avatar.png",accessToken:"...",refreshToken:"...",rawUser:{}}},null,2)},{code:400,body:`
|
||||
`),v.$set(u),(!D||t&1)&&I!==(I=e[0].name+"")&&pe(ee,I),t&6&&(N=Q(e[2]),$=Ue($,t,Me,1,e,N,De,L,Ne,je,null,Le)),t&6&&(H=Q(e[2]),Qe(),w=Ue(w,t,We,1,e,H,Pe,j,ze,He,null,Fe),Ge())},i(e){if(!D){V(v.$$.fragment,e),V(C.$$.fragment,e),V(q.$$.fragment,e);for(let t=0;t<H.length;t+=1)V(w[t]);D=!0}},o(e){E(v.$$.fragment,e),E(C.$$.fragment,e),E(q.$$.fragment,e);for(let t=0;t<w.length;t+=1)E(w[t]);D=!1},d(e){e&&(c(l),c(d),c(b),c(_),c(O),c(R),c(Y),c(y),c(te),c(M),c(ae),c(W),c(le),c(U),c(ne),c(A),c(ue),c(F),c(he),c(T)),K(v,e),K(C),K(q);for(let t=0;t<$.length;t+=1)$[t].d();for(let t=0;t<w.length;t+=1)w[t].d()}}}function et(s,l,n){let i,{collection:f}=l,g=200,d=[];const b=_=>n(1,g=_.code);return s.$$set=_=>{"collection"in _&&n(0,f=_.collection)},s.$$.update=()=>{s.$$.dirty&1&&n(2,d=[{code:200,body:JSON.stringify({token:"JWT_AUTH_TOKEN",record:Be.dummyCollectionRecord(f),meta:{id:"abc123",name:"John Doe",username:"john.doe",email:"test@example.com",avatarURL:"https://example.com/avatar.png",accessToken:"...",refreshToken:"...",rawUser:{}}},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while submitting the form.",
|
||||
@@ -114,4 +114,4 @@ import{S as xe,i as Ee,s as Je,Q as Qe,R as je,T as z,e as o,w as k,b as h,c as
|
||||
}
|
||||
}
|
||||
}
|
||||
`}])},n(3,i=Be.getApiExampleUrl(Ge.baseURL)),[f,g,d,i,b]}class lt extends xe{constructor(l){super(),Ee(this,l,et,Ze,Je,{collection:0})}}export{lt as default};
|
||||
`}])},n(3,i=Be.getApiExampleUrl(Ke.baseURL)),[f,g,d,i,b]}class lt extends Ee{constructor(l){super(),Je(this,l,et,Ze,xe,{collection:0})}}export{lt as default};
|
||||
+8
-8
@@ -1,4 +1,4 @@
|
||||
import{S as ee,i as te,s as le,T as U,e as p,b as S,w as V,f as g,g as b,h,x as z,U as G,V as ge,k as Z,W as ke,n as x,t as L,a as N,o as _,C as oe,r as Y,u as ae,R as we,c as K,m as Q,d as X,Q as $e,X as se,p as Te,Y as ne}from"./index-Co7n6kj7.js";function ie(s,t,e){const a=s.slice();return a[4]=t[e],a}function ce(s,t,e){const a=s.slice();return a[4]=t[e],a}function re(s,t){let e,a=t[4].code+"",d,c,r,n;function u(){return t[3](t[4])}return{key:s,first:null,c(){e=p("button"),d=V(a),c=S(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(m,q){b(m,e,q),h(e,d),h(e,c),r||(n=ae(e,"click",u),r=!0)},p(m,q){t=m,q&4&&a!==(a=t[4].code+"")&&z(d,a),q&6&&Y(e,"active",t[1]===t[4].code)},d(m){m&&_(e),r=!1,n()}}}function de(s,t){let e,a,d,c;return a=new we({props:{content:t[4].body}}),{key:s,first:null,c(){e=p("div"),K(a.$$.fragment),d=S(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(r,n){b(r,e,n),Q(a,e,null),h(e,d),c=!0},p(r,n){t=r;const u={};n&4&&(u.content=t[4].body),a.$set(u),(!c||n&6)&&Y(e,"active",t[1]===t[4].code)},i(r){c||(L(a.$$.fragment,r),c=!0)},o(r){N(a.$$.fragment,r),c=!1},d(r){r&&_(e),X(a)}}}function Pe(s){let t,e,a,d,c,r,n,u=s[0].name+"",m,q,M,C,B,A,H,R,W,y,P,w=[],$=new Map,E,D,k=[],J=new Map,I,i=U(s[2]);const v=l=>l[4].code;for(let l=0;l<i.length;l+=1){let o=ce(s,i,l),f=v(o);$.set(f,w[l]=re(f,o))}let O=U(s[2]);const j=l=>l[4].code;for(let l=0;l<O.length;l+=1){let o=ie(s,O,l),f=j(o);J.set(f,k[l]=de(f,o))}return{c(){t=p("div"),e=p("strong"),e.textContent="POST",a=S(),d=p("div"),c=p("p"),r=V("/api/collections/"),n=p("strong"),m=V(u),q=V("/auth-with-otp"),M=S(),C=p("div"),C.textContent="Body Parameters",B=S(),A=p("table"),A.innerHTML='<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>otpId</span></div></td> <td><span class="label">String</span></td> <td>The id of the OTP request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>The one-time password.</td></tr></tbody>',H=S(),R=p("div"),R.textContent="Responses",W=S(),y=p("div"),P=p("div");for(let l=0;l<w.length;l+=1)w[l].c();E=S(),D=p("div");for(let l=0;l<k.length;l+=1)k[l].c();g(e,"class","label label-primary"),g(d,"class","content"),g(t,"class","alert alert-success"),g(C,"class","section-title"),g(A,"class","table-compact table-border m-b-base"),g(R,"class","section-title"),g(P,"class","tabs-header compact combined left"),g(D,"class","tabs-content"),g(y,"class","tabs")},m(l,o){b(l,t,o),h(t,e),h(t,a),h(t,d),h(d,c),h(c,r),h(c,n),h(n,m),h(c,q),b(l,M,o),b(l,C,o),b(l,B,o),b(l,A,o),b(l,H,o),b(l,R,o),b(l,W,o),b(l,y,o),h(y,P);for(let f=0;f<w.length;f+=1)w[f]&&w[f].m(P,null);h(y,E),h(y,D);for(let f=0;f<k.length;f+=1)k[f]&&k[f].m(D,null);I=!0},p(l,[o]){(!I||o&1)&&u!==(u=l[0].name+"")&&z(m,u),o&6&&(i=U(l[2]),w=G(w,o,v,1,l,i,$,P,ge,re,null,ce)),o&6&&(O=U(l[2]),Z(),k=G(k,o,j,1,l,O,J,D,ke,de,null,ie),x())},i(l){if(!I){for(let o=0;o<O.length;o+=1)L(k[o]);I=!0}},o(l){for(let o=0;o<k.length;o+=1)N(k[o]);I=!1},d(l){l&&(_(t),_(M),_(C),_(B),_(A),_(H),_(R),_(W),_(y));for(let o=0;o<w.length;o+=1)w[o].d();for(let o=0;o<k.length;o+=1)k[o].d()}}}function Oe(s,t,e){let{collection:a}=t,d=200,c=[];const r=n=>e(1,d=n.code);return s.$$set=n=>{"collection"in n&&e(0,a=n.collection)},s.$$.update=()=>{s.$$.dirty&1&&e(2,c=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:oe.dummyCollectionRecord(a)},null,2)},{code:400,body:`
|
||||
import{S as ee,i as te,s as le,W as U,f as p,h as y,y as V,j as g,l as b,n as h,G as Z,X as z,Y as ge,D as Q,Z as ke,E as x,t as L,a as N,u as _,I as oe,k as Y,o as ae,V as we,c as G,m as K,d as X,U as $e,_ as se,p as Te,$ as ne}from"./index-0yJTQg7M.js";function ie(s,t,e){const a=s.slice();return a[4]=t[e],a}function ce(s,t,e){const a=s.slice();return a[4]=t[e],a}function re(s,t){let e,a=t[4].code+"",d,c,r,n;function u(){return t[3](t[4])}return{key:s,first:null,c(){e=p("button"),d=V(a),c=y(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(m,q){b(m,e,q),h(e,d),h(e,c),r||(n=ae(e,"click",u),r=!0)},p(m,q){t=m,q&4&&a!==(a=t[4].code+"")&&Z(d,a),q&6&&Y(e,"active",t[1]===t[4].code)},d(m){m&&_(e),r=!1,n()}}}function de(s,t){let e,a,d,c;return a=new we({props:{content:t[4].body}}),{key:s,first:null,c(){e=p("div"),G(a.$$.fragment),d=y(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(r,n){b(r,e,n),K(a,e,null),h(e,d),c=!0},p(r,n){t=r;const u={};n&4&&(u.content=t[4].body),a.$set(u),(!c||n&6)&&Y(e,"active",t[1]===t[4].code)},i(r){c||(L(a.$$.fragment,r),c=!0)},o(r){N(a.$$.fragment,r),c=!1},d(r){r&&_(e),X(a)}}}function Pe(s){let t,e,a,d,c,r,n,u=s[0].name+"",m,q,M,C,B,A,H,R,W,S,P,w=[],$=new Map,J,D,k=[],j=new Map,I,i=U(s[2]);const v=l=>l[4].code;for(let l=0;l<i.length;l+=1){let o=ce(s,i,l),f=v(o);$.set(f,w[l]=re(f,o))}let O=U(s[2]);const E=l=>l[4].code;for(let l=0;l<O.length;l+=1){let o=ie(s,O,l),f=E(o);j.set(f,k[l]=de(f,o))}return{c(){t=p("div"),e=p("strong"),e.textContent="POST",a=y(),d=p("div"),c=p("p"),r=V("/api/collections/"),n=p("strong"),m=V(u),q=V("/auth-with-otp"),M=y(),C=p("div"),C.textContent="Body Parameters",B=y(),A=p("table"),A.innerHTML='<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>otpId</span></div></td> <td><span class="label">String</span></td> <td>The id of the OTP request.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>The one-time password.</td></tr></tbody>',H=y(),R=p("div"),R.textContent="Responses",W=y(),S=p("div"),P=p("div");for(let l=0;l<w.length;l+=1)w[l].c();J=y(),D=p("div");for(let l=0;l<k.length;l+=1)k[l].c();g(e,"class","label label-primary"),g(d,"class","content"),g(t,"class","alert alert-success"),g(C,"class","section-title"),g(A,"class","table-compact table-border m-b-base"),g(R,"class","section-title"),g(P,"class","tabs-header compact combined left"),g(D,"class","tabs-content"),g(S,"class","tabs")},m(l,o){b(l,t,o),h(t,e),h(t,a),h(t,d),h(d,c),h(c,r),h(c,n),h(n,m),h(c,q),b(l,M,o),b(l,C,o),b(l,B,o),b(l,A,o),b(l,H,o),b(l,R,o),b(l,W,o),b(l,S,o),h(S,P);for(let f=0;f<w.length;f+=1)w[f]&&w[f].m(P,null);h(S,J),h(S,D);for(let f=0;f<k.length;f+=1)k[f]&&k[f].m(D,null);I=!0},p(l,[o]){(!I||o&1)&&u!==(u=l[0].name+"")&&Z(m,u),o&6&&(i=U(l[2]),w=z(w,o,v,1,l,i,$,P,ge,re,null,ce)),o&6&&(O=U(l[2]),Q(),k=z(k,o,E,1,l,O,j,D,ke,de,null,ie),x())},i(l){if(!I){for(let o=0;o<O.length;o+=1)L(k[o]);I=!0}},o(l){for(let o=0;o<k.length;o+=1)N(k[o]);I=!1},d(l){l&&(_(t),_(M),_(C),_(B),_(A),_(H),_(R),_(W),_(S));for(let o=0;o<w.length;o+=1)w[o].d();for(let o=0;o<k.length;o+=1)k[o].d()}}}function Oe(s,t,e){let{collection:a}=t,d=200,c=[];const r=n=>e(1,d=n.code);return s.$$set=n=>{"collection"in n&&e(0,a=n.collection)},s.$$.update=()=>{s.$$.dirty&1&&e(2,c=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:oe.dummyCollectionRecord(a)},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
@@ -9,7 +9,7 @@ import{S as ee,i as te,s as le,T as U,e as p,b as S,w as V,f as g,g as b,h,x as
|
||||
}
|
||||
}
|
||||
}
|
||||
`}])},[a,d,c,r]}class Se extends ee{constructor(t){super(),te(this,t,Oe,Pe,le,{collection:0})}}function ue(s,t,e){const a=s.slice();return a[4]=t[e],a}function he(s,t,e){const a=s.slice();return a[4]=t[e],a}function pe(s,t){let e,a=t[4].code+"",d,c,r,n;function u(){return t[3](t[4])}return{key:s,first:null,c(){e=p("button"),d=V(a),c=S(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(m,q){b(m,e,q),h(e,d),h(e,c),r||(n=ae(e,"click",u),r=!0)},p(m,q){t=m,q&4&&a!==(a=t[4].code+"")&&z(d,a),q&6&&Y(e,"active",t[1]===t[4].code)},d(m){m&&_(e),r=!1,n()}}}function fe(s,t){let e,a,d,c;return a=new we({props:{content:t[4].body}}),{key:s,first:null,c(){e=p("div"),K(a.$$.fragment),d=S(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(r,n){b(r,e,n),Q(a,e,null),h(e,d),c=!0},p(r,n){t=r;const u={};n&4&&(u.content=t[4].body),a.$set(u),(!c||n&6)&&Y(e,"active",t[1]===t[4].code)},i(r){c||(L(a.$$.fragment,r),c=!0)},o(r){N(a.$$.fragment,r),c=!1},d(r){r&&_(e),X(a)}}}function ye(s){let t,e,a,d,c,r,n,u=s[0].name+"",m,q,M,C,B,A,H,R,W,y,P,w=[],$=new Map,E,D,k=[],J=new Map,I,i=U(s[2]);const v=l=>l[4].code;for(let l=0;l<i.length;l+=1){let o=he(s,i,l),f=v(o);$.set(f,w[l]=pe(f,o))}let O=U(s[2]);const j=l=>l[4].code;for(let l=0;l<O.length;l+=1){let o=ue(s,O,l),f=j(o);J.set(f,k[l]=fe(f,o))}return{c(){t=p("div"),e=p("strong"),e.textContent="POST",a=S(),d=p("div"),c=p("p"),r=V("/api/collections/"),n=p("strong"),m=V(u),q=V("/request-otp"),M=S(),C=p("div"),C.textContent="Body Parameters",B=S(),A=p("table"),A.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>email</span></div></td> <td><span class="label">String</span></td> <td>The auth record email address to send the OTP request (if exists).</td></tr></tbody>',H=S(),R=p("div"),R.textContent="Responses",W=S(),y=p("div"),P=p("div");for(let l=0;l<w.length;l+=1)w[l].c();E=S(),D=p("div");for(let l=0;l<k.length;l+=1)k[l].c();g(e,"class","label label-primary"),g(d,"class","content"),g(t,"class","alert alert-success"),g(C,"class","section-title"),g(A,"class","table-compact table-border m-b-base"),g(R,"class","section-title"),g(P,"class","tabs-header compact combined left"),g(D,"class","tabs-content"),g(y,"class","tabs")},m(l,o){b(l,t,o),h(t,e),h(t,a),h(t,d),h(d,c),h(c,r),h(c,n),h(n,m),h(c,q),b(l,M,o),b(l,C,o),b(l,B,o),b(l,A,o),b(l,H,o),b(l,R,o),b(l,W,o),b(l,y,o),h(y,P);for(let f=0;f<w.length;f+=1)w[f]&&w[f].m(P,null);h(y,E),h(y,D);for(let f=0;f<k.length;f+=1)k[f]&&k[f].m(D,null);I=!0},p(l,[o]){(!I||o&1)&&u!==(u=l[0].name+"")&&z(m,u),o&6&&(i=U(l[2]),w=G(w,o,v,1,l,i,$,P,ge,pe,null,he)),o&6&&(O=U(l[2]),Z(),k=G(k,o,j,1,l,O,J,D,ke,fe,null,ue),x())},i(l){if(!I){for(let o=0;o<O.length;o+=1)L(k[o]);I=!0}},o(l){for(let o=0;o<k.length;o+=1)N(k[o]);I=!1},d(l){l&&(_(t),_(M),_(C),_(B),_(A),_(H),_(R),_(W),_(y));for(let o=0;o<w.length;o+=1)w[o].d();for(let o=0;o<k.length;o+=1)k[o].d()}}}function qe(s,t,e){let{collection:a}=t,d=200,c=[];const r=n=>e(1,d=n.code);return s.$$set=n=>{"collection"in n&&e(0,a=n.collection)},e(2,c=[{code:200,body:JSON.stringify({otpId:oe.randomString(15)},null,2)},{code:400,body:`
|
||||
`}])},[a,d,c,r]}class ye extends ee{constructor(t){super(),te(this,t,Oe,Pe,le,{collection:0})}}function ue(s,t,e){const a=s.slice();return a[4]=t[e],a}function he(s,t,e){const a=s.slice();return a[4]=t[e],a}function pe(s,t){let e,a=t[4].code+"",d,c,r,n;function u(){return t[3](t[4])}return{key:s,first:null,c(){e=p("button"),d=V(a),c=y(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(m,q){b(m,e,q),h(e,d),h(e,c),r||(n=ae(e,"click",u),r=!0)},p(m,q){t=m,q&4&&a!==(a=t[4].code+"")&&Z(d,a),q&6&&Y(e,"active",t[1]===t[4].code)},d(m){m&&_(e),r=!1,n()}}}function fe(s,t){let e,a,d,c;return a=new we({props:{content:t[4].body}}),{key:s,first:null,c(){e=p("div"),G(a.$$.fragment),d=y(),g(e,"class","tab-item"),Y(e,"active",t[1]===t[4].code),this.first=e},m(r,n){b(r,e,n),K(a,e,null),h(e,d),c=!0},p(r,n){t=r;const u={};n&4&&(u.content=t[4].body),a.$set(u),(!c||n&6)&&Y(e,"active",t[1]===t[4].code)},i(r){c||(L(a.$$.fragment,r),c=!0)},o(r){N(a.$$.fragment,r),c=!1},d(r){r&&_(e),X(a)}}}function Se(s){let t,e,a,d,c,r,n,u=s[0].name+"",m,q,M,C,B,A,H,R,W,S,P,w=[],$=new Map,J,D,k=[],j=new Map,I,i=U(s[2]);const v=l=>l[4].code;for(let l=0;l<i.length;l+=1){let o=he(s,i,l),f=v(o);$.set(f,w[l]=pe(f,o))}let O=U(s[2]);const E=l=>l[4].code;for(let l=0;l<O.length;l+=1){let o=ue(s,O,l),f=E(o);j.set(f,k[l]=fe(f,o))}return{c(){t=p("div"),e=p("strong"),e.textContent="POST",a=y(),d=p("div"),c=p("p"),r=V("/api/collections/"),n=p("strong"),m=V(u),q=V("/request-otp"),M=y(),C=p("div"),C.textContent="Body Parameters",B=y(),A=p("table"),A.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>email</span></div></td> <td><span class="label">String</span></td> <td>The auth record email address to send the OTP request (if exists).</td></tr></tbody>',H=y(),R=p("div"),R.textContent="Responses",W=y(),S=p("div"),P=p("div");for(let l=0;l<w.length;l+=1)w[l].c();J=y(),D=p("div");for(let l=0;l<k.length;l+=1)k[l].c();g(e,"class","label label-primary"),g(d,"class","content"),g(t,"class","alert alert-success"),g(C,"class","section-title"),g(A,"class","table-compact table-border m-b-base"),g(R,"class","section-title"),g(P,"class","tabs-header compact combined left"),g(D,"class","tabs-content"),g(S,"class","tabs")},m(l,o){b(l,t,o),h(t,e),h(t,a),h(t,d),h(d,c),h(c,r),h(c,n),h(n,m),h(c,q),b(l,M,o),b(l,C,o),b(l,B,o),b(l,A,o),b(l,H,o),b(l,R,o),b(l,W,o),b(l,S,o),h(S,P);for(let f=0;f<w.length;f+=1)w[f]&&w[f].m(P,null);h(S,J),h(S,D);for(let f=0;f<k.length;f+=1)k[f]&&k[f].m(D,null);I=!0},p(l,[o]){(!I||o&1)&&u!==(u=l[0].name+"")&&Z(m,u),o&6&&(i=U(l[2]),w=z(w,o,v,1,l,i,$,P,ge,pe,null,he)),o&6&&(O=U(l[2]),Q(),k=z(k,o,E,1,l,O,j,D,ke,fe,null,ue),x())},i(l){if(!I){for(let o=0;o<O.length;o+=1)L(k[o]);I=!0}},o(l){for(let o=0;o<k.length;o+=1)N(k[o]);I=!1},d(l){l&&(_(t),_(M),_(C),_(B),_(A),_(H),_(R),_(W),_(S));for(let o=0;o<w.length;o+=1)w[o].d();for(let o=0;o<k.length;o+=1)k[o].d()}}}function qe(s,t,e){let{collection:a}=t,d=200,c=[];const r=n=>e(1,d=n.code);return s.$$set=n=>{"collection"in n&&e(0,a=n.collection)},e(2,c=[{code:200,body:JSON.stringify({otpId:oe.randomString(15)},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
@@ -26,7 +26,7 @@ import{S as ee,i as te,s as le,T as U,e as p,b as S,w as V,f as g,g as b,h,x as
|
||||
"message": "You've send too many OTP requests, please try again later.",
|
||||
"data": {}
|
||||
}
|
||||
`}]),[a,d,c,r]}class Ae extends ee{constructor(t){super(),te(this,t,qe,ye,le,{collection:0})}}function me(s,t,e){const a=s.slice();return a[5]=t[e],a[7]=e,a}function be(s,t,e){const a=s.slice();return a[5]=t[e],a[7]=e,a}function _e(s){let t,e,a,d,c;function r(){return s[4](s[7])}return{c(){t=p("button"),e=p("div"),e.textContent=`${s[5].title}`,a=S(),g(e,"class","txt"),g(t,"class","tab-item"),Y(t,"active",s[1]==s[7])},m(n,u){b(n,t,u),h(t,e),h(t,a),d||(c=ae(t,"click",r),d=!0)},p(n,u){s=n,u&2&&Y(t,"active",s[1]==s[7])},d(n){n&&_(t),d=!1,c()}}}function ve(s){let t,e,a,d;var c=s[5].component;function r(n,u){return{props:{collection:n[0]}}}return c&&(e=ne(c,r(s))),{c(){t=p("div"),e&&K(e.$$.fragment),a=S(),g(t,"class","tab-item"),Y(t,"active",s[1]==s[7])},m(n,u){b(n,t,u),e&&Q(e,t,null),h(t,a),d=!0},p(n,u){if(c!==(c=n[5].component)){if(e){Z();const m=e;N(m.$$.fragment,1,0,()=>{X(m,1)}),x()}c?(e=ne(c,r(n)),K(e.$$.fragment),L(e.$$.fragment,1),Q(e,t,a)):e=null}else if(c){const m={};u&1&&(m.collection=n[0]),e.$set(m)}(!d||u&2)&&Y(t,"active",n[1]==n[7])},i(n){d||(e&&L(e.$$.fragment,n),d=!0)},o(n){e&&N(e.$$.fragment,n),d=!1},d(n){n&&_(t),e&&X(e)}}}function Re(s){var D,k,J,I;let t,e,a=s[0].name+"",d,c,r,n,u,m,q,M,C,B,A,H,R,W;m=new $e({props:{js:`
|
||||
`}]),[a,d,c,r]}class Ae extends ee{constructor(t){super(),te(this,t,qe,Se,le,{collection:0})}}function me(s,t,e){const a=s.slice();return a[5]=t[e],a[7]=e,a}function be(s,t,e){const a=s.slice();return a[5]=t[e],a[7]=e,a}function _e(s){let t,e,a,d,c;function r(){return s[4](s[7])}return{c(){t=p("button"),e=p("div"),e.textContent=`${s[5].title}`,a=y(),g(e,"class","txt"),g(t,"class","tab-item"),Y(t,"active",s[1]==s[7])},m(n,u){b(n,t,u),h(t,e),h(t,a),d||(c=ae(t,"click",r),d=!0)},p(n,u){s=n,u&2&&Y(t,"active",s[1]==s[7])},d(n){n&&_(t),d=!1,c()}}}function ve(s){let t,e,a,d;var c=s[5].component;function r(n,u){return{props:{collection:n[0]}}}return c&&(e=ne(c,r(s))),{c(){t=p("div"),e&&G(e.$$.fragment),a=y(),g(t,"class","tab-item"),Y(t,"active",s[1]==s[7])},m(n,u){b(n,t,u),e&&K(e,t,null),h(t,a),d=!0},p(n,u){if(c!==(c=n[5].component)){if(e){Q();const m=e;N(m.$$.fragment,1,0,()=>{X(m,1)}),x()}c?(e=ne(c,r(n)),G(e.$$.fragment),L(e.$$.fragment,1),K(e,t,a)):e=null}else if(c){const m={};u&1&&(m.collection=n[0]),e.$set(m)}(!d||u&2)&&Y(t,"active",n[1]==n[7])},i(n){d||(e&&L(e.$$.fragment,n),d=!0)},o(n){e&&N(e.$$.fragment,n),d=!1},d(n){n&&_(t),e&&X(e)}}}function Re(s){var D,k,j,I;let t,e,a=s[0].name+"",d,c,r,n,u,m,q,M,C,B,A,H,R,W;m=new $e({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${s[2]}');
|
||||
@@ -59,7 +59,7 @@ import{S as ee,i as te,s as le,T as U,e as p,b as S,w as V,f as g,g as b,h,x as
|
||||
...
|
||||
|
||||
// send OTP email to the provided auth record
|
||||
final req = await pb.collection('${(J=s[0])==null?void 0:J.name}').requestOTP('test@example.com');
|
||||
final req = await pb.collection('${(j=s[0])==null?void 0:j.name}').requestOTP('test@example.com');
|
||||
|
||||
// ... show a screen/popup to enter the password from the email ...
|
||||
|
||||
@@ -76,8 +76,8 @@ import{S as ee,i as te,s as le,T as U,e as p,b as S,w as V,f as g,g as b,h,x as
|
||||
|
||||
// "logout"
|
||||
pb.authStore.clear();
|
||||
`}});let y=U(s[3]),P=[];for(let i=0;i<y.length;i+=1)P[i]=_e(be(s,y,i));let w=U(s[3]),$=[];for(let i=0;i<w.length;i+=1)$[i]=ve(me(s,w,i));const E=i=>N($[i],1,1,()=>{$[i]=null});return{c(){t=p("h3"),e=V("Auth with OTP ("),d=V(a),c=V(")"),r=S(),n=p("div"),n.innerHTML=`<p>Authenticate with an one-time password (OTP).</p> <p>Note that when requesting an OTP we return an <code>otpId</code> even if a user with the provided email
|
||||
doesn't exist as a very basic enumeration protection.</p>`,u=S(),K(m.$$.fragment),q=S(),M=p("h6"),M.textContent="API details",C=S(),B=p("div"),A=p("div");for(let i=0;i<P.length;i+=1)P[i].c();H=S(),R=p("div");for(let i=0;i<$.length;i+=1)$[i].c();g(t,"class","m-b-sm"),g(n,"class","content txt-lg m-b-sm"),g(M,"class","m-b-xs"),g(A,"class","tabs-header compact"),g(R,"class","tabs-content"),g(B,"class","tabs")},m(i,v){b(i,t,v),h(t,e),h(t,d),h(t,c),b(i,r,v),b(i,n,v),b(i,u,v),Q(m,i,v),b(i,q,v),b(i,M,v),b(i,C,v),b(i,B,v),h(B,A);for(let O=0;O<P.length;O+=1)P[O]&&P[O].m(A,null);h(B,H),h(B,R);for(let O=0;O<$.length;O+=1)$[O]&&$[O].m(R,null);W=!0},p(i,[v]){var j,l,o,f;(!W||v&1)&&a!==(a=i[0].name+"")&&z(d,a);const O={};if(v&5&&(O.js=`
|
||||
`}});let S=U(s[3]),P=[];for(let i=0;i<S.length;i+=1)P[i]=_e(be(s,S,i));let w=U(s[3]),$=[];for(let i=0;i<w.length;i+=1)$[i]=ve(me(s,w,i));const J=i=>N($[i],1,1,()=>{$[i]=null});return{c(){t=p("h3"),e=V("Auth with OTP ("),d=V(a),c=V(")"),r=y(),n=p("div"),n.innerHTML=`<p>Authenticate with an one-time password (OTP).</p> <p>Note that when requesting an OTP we return an <code>otpId</code> even if a user with the provided email
|
||||
doesn't exist as a very basic enumeration protection.</p>`,u=y(),G(m.$$.fragment),q=y(),M=p("h6"),M.textContent="API details",C=y(),B=p("div"),A=p("div");for(let i=0;i<P.length;i+=1)P[i].c();H=y(),R=p("div");for(let i=0;i<$.length;i+=1)$[i].c();g(t,"class","m-b-sm"),g(n,"class","content txt-lg m-b-sm"),g(M,"class","m-b-xs"),g(A,"class","tabs-header compact"),g(R,"class","tabs-content"),g(B,"class","tabs")},m(i,v){b(i,t,v),h(t,e),h(t,d),h(t,c),b(i,r,v),b(i,n,v),b(i,u,v),K(m,i,v),b(i,q,v),b(i,M,v),b(i,C,v),b(i,B,v),h(B,A);for(let O=0;O<P.length;O+=1)P[O]&&P[O].m(A,null);h(B,H),h(B,R);for(let O=0;O<$.length;O+=1)$[O]&&$[O].m(R,null);W=!0},p(i,[v]){var E,l,o,f;(!W||v&1)&&a!==(a=i[0].name+"")&&Z(d,a);const O={};if(v&5&&(O.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i[2]}');
|
||||
@@ -85,7 +85,7 @@ import{S as ee,i as te,s as le,T as U,e as p,b as S,w as V,f as g,g as b,h,x as
|
||||
...
|
||||
|
||||
// send OTP email to the provided auth record
|
||||
const req = await pb.collection('${(j=i[0])==null?void 0:j.name}').requestOTP('test@example.com');
|
||||
const req = await pb.collection('${(E=i[0])==null?void 0:E.name}').requestOTP('test@example.com');
|
||||
|
||||
// ... show a screen/popup to enter the password from the email ...
|
||||
|
||||
@@ -127,4 +127,4 @@ import{S as ee,i as te,s as le,T as U,e as p,b as S,w as V,f as g,g as b,h,x as
|
||||
|
||||
// "logout"
|
||||
pb.authStore.clear();
|
||||
`),m.$set(O),v&10){y=U(i[3]);let T;for(T=0;T<y.length;T+=1){const F=be(i,y,T);P[T]?P[T].p(F,v):(P[T]=_e(F),P[T].c(),P[T].m(A,null))}for(;T<P.length;T+=1)P[T].d(1);P.length=y.length}if(v&11){w=U(i[3]);let T;for(T=0;T<w.length;T+=1){const F=me(i,w,T);$[T]?($[T].p(F,v),L($[T],1)):($[T]=ve(F),$[T].c(),L($[T],1),$[T].m(R,null))}for(Z(),T=w.length;T<$.length;T+=1)E(T);x()}},i(i){if(!W){L(m.$$.fragment,i);for(let v=0;v<w.length;v+=1)L($[v]);W=!0}},o(i){N(m.$$.fragment,i),$=$.filter(Boolean);for(let v=0;v<$.length;v+=1)N($[v]);W=!1},d(i){i&&(_(t),_(r),_(n),_(u),_(q),_(M),_(C),_(B)),X(m,i),se(P,i),se($,i)}}}function Ce(s,t,e){let a,{collection:d}=t;const c=[{title:"OTP Request",component:Ae},{title:"OTP Auth",component:Se}];let r=0;const n=u=>e(1,r=u);return s.$$set=u=>{"collection"in u&&e(0,d=u.collection)},e(2,a=oe.getApiExampleUrl(Te.baseURL)),[d,r,a,c,n]}class De extends ee{constructor(t){super(),te(this,t,Ce,Re,le,{collection:0})}}export{De as default};
|
||||
`),m.$set(O),v&10){S=U(i[3]);let T;for(T=0;T<S.length;T+=1){const F=be(i,S,T);P[T]?P[T].p(F,v):(P[T]=_e(F),P[T].c(),P[T].m(A,null))}for(;T<P.length;T+=1)P[T].d(1);P.length=S.length}if(v&11){w=U(i[3]);let T;for(T=0;T<w.length;T+=1){const F=me(i,w,T);$[T]?($[T].p(F,v),L($[T],1)):($[T]=ve(F),$[T].c(),L($[T],1),$[T].m(R,null))}for(Q(),T=w.length;T<$.length;T+=1)J(T);x()}},i(i){if(!W){L(m.$$.fragment,i);for(let v=0;v<w.length;v+=1)L($[v]);W=!0}},o(i){N(m.$$.fragment,i),$=$.filter(Boolean);for(let v=0;v<$.length;v+=1)N($[v]);W=!1},d(i){i&&(_(t),_(r),_(n),_(u),_(q),_(M),_(C),_(B)),X(m,i),se(P,i),se($,i)}}}function Ce(s,t,e){let a,{collection:d}=t;const c=[{title:"OTP Request",component:Ae},{title:"OTP Auth",component:ye}];let r=0;const n=u=>e(1,r=u);return s.$$set=u=>{"collection"in u&&e(0,d=u.collection)},e(2,a=oe.getApiExampleUrl(Te.baseURL)),[d,r,a,c,n]}class De extends ee{constructor(t){super(),te(this,t,Ce,Re,le,{collection:0})}}export{De as default};
|
||||
Vendored
+9
-9
@@ -1,4 +1,4 @@
|
||||
import{S as kt,i as gt,s as vt,Q as St,T as L,R as _t,e as s,w as f,b as u,c as ae,f as k,g as c,h as t,m as oe,x as G,U as ct,V as wt,k as yt,W as $t,n as Pt,t as X,a as z,o as d,d as se,X as Rt,C as dt,p as Ct,r as ne,u as Tt}from"./index-Co7n6kj7.js";import{F as Ot}from"./FieldsQueryParam-DH-ZC4qv.js";function pt(i,o,a){const n=i.slice();return n[7]=o[a],n}function ut(i,o,a){const n=i.slice();return n[7]=o[a],n}function ht(i,o,a){const n=i.slice();return n[12]=o[a],n[14]=a,n}function At(i){let o;return{c(){o=f("or")},m(a,n){c(a,o,n)},d(a){a&&d(o)}}}function bt(i){let o,a,n=i[12]+"",m,b=i[14]>0&&At();return{c(){b&&b.c(),o=u(),a=s("strong"),m=f(n)},m(r,h){b&&b.m(r,h),c(r,o,h),c(r,a,h),t(a,m)},p(r,h){h&2&&n!==(n=r[12]+"")&&G(m,n)},d(r){r&&(d(o),d(a)),b&&b.d(r)}}}function ft(i,o){let a,n=o[7].code+"",m,b,r,h;function g(){return o[6](o[7])}return{key:i,first:null,c(){a=s("button"),m=f(n),b=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m($,_){c($,a,_),t(a,m),t(a,b),r||(h=Tt(a,"click",g),r=!0)},p($,_){o=$,_&8&&n!==(n=o[7].code+"")&&G(m,n),_&12&&ne(a,"active",o[2]===o[7].code)},d($){$&&d(a),r=!1,h()}}}function mt(i,o){let a,n,m,b;return n=new _t({props:{content:o[7].body}}),{key:i,first:null,c(){a=s("div"),ae(n.$$.fragment),m=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m(r,h){c(r,a,h),oe(n,a,null),t(a,m),b=!0},p(r,h){o=r;const g={};h&8&&(g.content=o[7].body),n.$set(g),(!b||h&12)&&ne(a,"active",o[2]===o[7].code)},i(r){b||(X(n.$$.fragment,r),b=!0)},o(r){z(n.$$.fragment,r),b=!1},d(r){r&&d(a),se(n)}}}function Dt(i){var ot,st;let o,a,n=i[0].name+"",m,b,r,h,g,$,_,Z=i[1].join("/")+"",ie,De,re,We,ce,R,de,q,pe,C,x,Ue,ee,H,Fe,ue,te=i[0].name+"",he,Me,be,j,fe,T,me,Be,V,O,_e,Le,ke,qe,Y,ge,He,ve,Se,E,we,A,ye,je,N,D,$e,Ve,Pe,Ye,v,Ee,F,Ne,Qe,Ie,Re,Je,Ce,Ke,Xe,ze,Te,Ge,Ze,M,Oe,Q,Ae,W,I,P=[],xe=new Map,et,J,w=[],tt=new Map,U;R=new St({props:{js:`
|
||||
import{S as kt,i as gt,s as vt,U as St,W as L,V as _t,f as s,y as f,h as u,c as ae,j as k,l as c,n as t,m as oe,G as Z,X as ct,Y as wt,D as yt,Z as $t,E as Pt,t as K,a as X,u as d,d as se,_ as Rt,I as dt,p as Ct,k as ne,o as Ot}from"./index-0yJTQg7M.js";import{F as Tt}from"./FieldsQueryParam-BrZEETlt.js";function pt(i,o,a){const n=i.slice();return n[7]=o[a],n}function ut(i,o,a){const n=i.slice();return n[7]=o[a],n}function ht(i,o,a){const n=i.slice();return n[12]=o[a],n[14]=a,n}function At(i){let o;return{c(){o=f("or")},m(a,n){c(a,o,n)},d(a){a&&d(o)}}}function bt(i){let o,a,n=i[12]+"",m,b=i[14]>0&&At();return{c(){b&&b.c(),o=u(),a=s("strong"),m=f(n)},m(r,h){b&&b.m(r,h),c(r,o,h),c(r,a,h),t(a,m)},p(r,h){h&2&&n!==(n=r[12]+"")&&Z(m,n)},d(r){r&&(d(o),d(a)),b&&b.d(r)}}}function ft(i,o){let a,n=o[7].code+"",m,b,r,h;function g(){return o[6](o[7])}return{key:i,first:null,c(){a=s("button"),m=f(n),b=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m($,_){c($,a,_),t(a,m),t(a,b),r||(h=Ot(a,"click",g),r=!0)},p($,_){o=$,_&8&&n!==(n=o[7].code+"")&&Z(m,n),_&12&&ne(a,"active",o[2]===o[7].code)},d($){$&&d(a),r=!1,h()}}}function mt(i,o){let a,n,m,b;return n=new _t({props:{content:o[7].body}}),{key:i,first:null,c(){a=s("div"),ae(n.$$.fragment),m=u(),k(a,"class","tab-item"),ne(a,"active",o[2]===o[7].code),this.first=a},m(r,h){c(r,a,h),oe(n,a,null),t(a,m),b=!0},p(r,h){o=r;const g={};h&8&&(g.content=o[7].body),n.$set(g),(!b||h&12)&&ne(a,"active",o[2]===o[7].code)},i(r){b||(K(n.$$.fragment,r),b=!0)},o(r){X(n.$$.fragment,r),b=!1},d(r){r&&d(a),se(n)}}}function Dt(i){var ot,st;let o,a,n=i[0].name+"",m,b,r,h,g,$,_,z=i[1].join("/")+"",ie,De,re,We,ce,R,de,q,pe,C,x,Ue,ee,H,Fe,ue,te=i[0].name+"",he,Me,be,j,fe,O,me,Be,Y,T,_e,Le,ke,qe,E,ge,He,ve,Se,V,we,A,ye,je,N,D,$e,Ye,Pe,Ee,v,Ve,F,Ne,Ie,Je,Re,Qe,Ce,Ge,Ke,Xe,Oe,Ze,ze,M,Te,I,Ae,W,J,P=[],xe=new Map,et,Q,w=[],tt=new Map,U;R=new St({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i[5]}');
|
||||
@@ -36,15 +36,15 @@ import{S as kt,i as gt,s as vt,Q as St,T as L,R as _t,e as s,w as f,b as u,c as
|
||||
|
||||
// "logout"
|
||||
pb.authStore.clear();
|
||||
`}});let B=L(i[1]),S=[];for(let e=0;e<B.length;e+=1)S[e]=bt(ht(i,B,e));F=new _t({props:{content:"?expand=relField1,relField2.subRelField"}}),M=new Ot({props:{prefix:"record."}});let le=L(i[3]);const lt=e=>e[7].code;for(let e=0;e<le.length;e+=1){let l=ut(i,le,e),p=lt(l);xe.set(p,P[e]=ft(p,l))}let K=L(i[3]);const at=e=>e[7].code;for(let e=0;e<K.length;e+=1){let l=pt(i,K,e),p=at(l);tt.set(p,w[e]=mt(p,l))}return{c(){o=s("h3"),a=f("Auth with password ("),m=f(n),b=f(")"),r=u(),h=s("div"),g=s("p"),$=f(`Authenticate with combination of
|
||||
`),_=s("strong"),ie=f(Z),De=f(" and "),re=s("strong"),re.textContent="password",We=f("."),ce=u(),ae(R.$$.fragment),de=u(),q=s("h6"),q.textContent="API details",pe=u(),C=s("div"),x=s("strong"),x.textContent="POST",Ue=u(),ee=s("div"),H=s("p"),Fe=f("/api/collections/"),ue=s("strong"),he=f(te),Me=f("/auth-with-password"),be=u(),j=s("div"),j.textContent="Body Parameters",fe=u(),T=s("table"),me=s("thead"),me.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',Be=u(),V=s("tbody"),O=s("tr"),_e=s("td"),_e.innerHTML='<div class="inline-flex"><span class="label label-success">Required</span> <span>identity</span></div>',Le=u(),ke=s("td"),ke.innerHTML='<span class="label">String</span>',qe=u(),Y=s("td");for(let e=0;e<S.length;e+=1)S[e].c();ge=f(`
|
||||
of the record to authenticate.`),He=u(),ve=s("tr"),ve.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>',Se=u(),E=s("div"),E.textContent="Query parameters",we=u(),A=s("table"),ye=s("thead"),ye.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',je=u(),N=s("tbody"),D=s("tr"),$e=s("td"),$e.textContent="expand",Ve=u(),Pe=s("td"),Pe.innerHTML='<span class="label">String</span>',Ye=u(),v=s("td"),Ee=f(`Auto expand record relations. Ex.:
|
||||
`}});let B=L(i[1]),S=[];for(let e=0;e<B.length;e+=1)S[e]=bt(ht(i,B,e));F=new _t({props:{content:"?expand=relField1,relField2.subRelField"}}),M=new Tt({props:{prefix:"record."}});let le=L(i[3]);const lt=e=>e[7].code;for(let e=0;e<le.length;e+=1){let l=ut(i,le,e),p=lt(l);xe.set(p,P[e]=ft(p,l))}let G=L(i[3]);const at=e=>e[7].code;for(let e=0;e<G.length;e+=1){let l=pt(i,G,e),p=at(l);tt.set(p,w[e]=mt(p,l))}return{c(){o=s("h3"),a=f("Auth with password ("),m=f(n),b=f(")"),r=u(),h=s("div"),g=s("p"),$=f(`Authenticate with combination of
|
||||
`),_=s("strong"),ie=f(z),De=f(" and "),re=s("strong"),re.textContent="password",We=f("."),ce=u(),ae(R.$$.fragment),de=u(),q=s("h6"),q.textContent="API details",pe=u(),C=s("div"),x=s("strong"),x.textContent="POST",Ue=u(),ee=s("div"),H=s("p"),Fe=f("/api/collections/"),ue=s("strong"),he=f(te),Me=f("/auth-with-password"),be=u(),j=s("div"),j.textContent="Body Parameters",fe=u(),O=s("table"),me=s("thead"),me.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',Be=u(),Y=s("tbody"),T=s("tr"),_e=s("td"),_e.innerHTML='<div class="inline-flex"><span class="label label-success">Required</span> <span>identity</span></div>',Le=u(),ke=s("td"),ke.innerHTML='<span class="label">String</span>',qe=u(),E=s("td");for(let e=0;e<S.length;e+=1)S[e].c();ge=f(`
|
||||
of the record to authenticate.`),He=u(),ve=s("tr"),ve.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>',Se=u(),V=s("div"),V.textContent="Query parameters",we=u(),A=s("table"),ye=s("thead"),ye.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',je=u(),N=s("tbody"),D=s("tr"),$e=s("td"),$e.textContent="expand",Ye=u(),Pe=s("td"),Pe.innerHTML='<span class="label">String</span>',Ee=u(),v=s("td"),Ve=f(`Auto expand record relations. Ex.:
|
||||
`),ae(F.$$.fragment),Ne=f(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Qe=s("br"),Ie=f(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Ie=s("br"),Je=f(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),Re=s("code"),Re.textContent="expand",Je=f(" property (eg. "),Ce=s("code"),Ce.textContent='"expand": {"relField1": {...}, ...}',Ke=f(`).
|
||||
`),Xe=s("br"),ze=f(`
|
||||
Only the relations to which the request user has permissions to `),Te=s("strong"),Te.textContent="view",Ge=f(" will be expanded."),Ze=u(),ae(M.$$.fragment),Oe=u(),Q=s("div"),Q.textContent="Responses",Ae=u(),W=s("div"),I=s("div");for(let e=0;e<P.length;e+=1)P[e].c();et=u(),J=s("div");for(let e=0;e<w.length;e+=1)w[e].c();k(o,"class","m-b-sm"),k(h,"class","content txt-lg m-b-sm"),k(q,"class","m-b-xs"),k(x,"class","label label-primary"),k(ee,"class","content"),k(C,"class","alert alert-success"),k(j,"class","section-title"),k(T,"class","table-compact table-border m-b-base"),k(E,"class","section-title"),k(A,"class","table-compact table-border m-b-base"),k(Q,"class","section-title"),k(I,"class","tabs-header compact combined left"),k(J,"class","tabs-content"),k(W,"class","tabs")},m(e,l){c(e,o,l),t(o,a),t(o,m),t(o,b),c(e,r,l),c(e,h,l),t(h,g),t(g,$),t(g,_),t(_,ie),t(g,De),t(g,re),t(g,We),c(e,ce,l),oe(R,e,l),c(e,de,l),c(e,q,l),c(e,pe,l),c(e,C,l),t(C,x),t(C,Ue),t(C,ee),t(ee,H),t(H,Fe),t(H,ue),t(ue,he),t(H,Me),c(e,be,l),c(e,j,l),c(e,fe,l),c(e,T,l),t(T,me),t(T,Be),t(T,V),t(V,O),t(O,_e),t(O,Le),t(O,ke),t(O,qe),t(O,Y);for(let p=0;p<S.length;p+=1)S[p]&&S[p].m(Y,null);t(Y,ge),t(V,He),t(V,ve),c(e,Se,l),c(e,E,l),c(e,we,l),c(e,A,l),t(A,ye),t(A,je),t(A,N),t(N,D),t(D,$e),t(D,Ve),t(D,Pe),t(D,Ye),t(D,v),t(v,Ee),oe(F,v,null),t(v,Ne),t(v,Qe),t(v,Ie),t(v,Re),t(v,Je),t(v,Ce),t(v,Ke),t(v,Xe),t(v,ze),t(v,Te),t(v,Ge),t(N,Ze),oe(M,N,null),c(e,Oe,l),c(e,Q,l),c(e,Ae,l),c(e,W,l),t(W,I);for(let p=0;p<P.length;p+=1)P[p]&&P[p].m(I,null);t(W,et),t(W,J);for(let p=0;p<w.length;p+=1)w[p]&&w[p].m(J,null);U=!0},p(e,[l]){var nt,it;(!U||l&1)&&n!==(n=e[0].name+"")&&G(m,n),(!U||l&2)&&Z!==(Z=e[1].join("/")+"")&&G(ie,Z);const p={};if(l&49&&(p.js=`
|
||||
`),Re=s("code"),Re.textContent="expand",Qe=f(" property (eg. "),Ce=s("code"),Ce.textContent='"expand": {"relField1": {...}, ...}',Ge=f(`).
|
||||
`),Ke=s("br"),Xe=f(`
|
||||
Only the relations to which the request user has permissions to `),Oe=s("strong"),Oe.textContent="view",Ze=f(" will be expanded."),ze=u(),ae(M.$$.fragment),Te=u(),I=s("div"),I.textContent="Responses",Ae=u(),W=s("div"),J=s("div");for(let e=0;e<P.length;e+=1)P[e].c();et=u(),Q=s("div");for(let e=0;e<w.length;e+=1)w[e].c();k(o,"class","m-b-sm"),k(h,"class","content txt-lg m-b-sm"),k(q,"class","m-b-xs"),k(x,"class","label label-primary"),k(ee,"class","content"),k(C,"class","alert alert-success"),k(j,"class","section-title"),k(O,"class","table-compact table-border m-b-base"),k(V,"class","section-title"),k(A,"class","table-compact table-border m-b-base"),k(I,"class","section-title"),k(J,"class","tabs-header compact combined left"),k(Q,"class","tabs-content"),k(W,"class","tabs")},m(e,l){c(e,o,l),t(o,a),t(o,m),t(o,b),c(e,r,l),c(e,h,l),t(h,g),t(g,$),t(g,_),t(_,ie),t(g,De),t(g,re),t(g,We),c(e,ce,l),oe(R,e,l),c(e,de,l),c(e,q,l),c(e,pe,l),c(e,C,l),t(C,x),t(C,Ue),t(C,ee),t(ee,H),t(H,Fe),t(H,ue),t(ue,he),t(H,Me),c(e,be,l),c(e,j,l),c(e,fe,l),c(e,O,l),t(O,me),t(O,Be),t(O,Y),t(Y,T),t(T,_e),t(T,Le),t(T,ke),t(T,qe),t(T,E);for(let p=0;p<S.length;p+=1)S[p]&&S[p].m(E,null);t(E,ge),t(Y,He),t(Y,ve),c(e,Se,l),c(e,V,l),c(e,we,l),c(e,A,l),t(A,ye),t(A,je),t(A,N),t(N,D),t(D,$e),t(D,Ye),t(D,Pe),t(D,Ee),t(D,v),t(v,Ve),oe(F,v,null),t(v,Ne),t(v,Ie),t(v,Je),t(v,Re),t(v,Qe),t(v,Ce),t(v,Ge),t(v,Ke),t(v,Xe),t(v,Oe),t(v,Ze),t(N,ze),oe(M,N,null),c(e,Te,l),c(e,I,l),c(e,Ae,l),c(e,W,l),t(W,J);for(let p=0;p<P.length;p+=1)P[p]&&P[p].m(J,null);t(W,et),t(W,Q);for(let p=0;p<w.length;p+=1)w[p]&&w[p].m(Q,null);U=!0},p(e,[l]){var nt,it;(!U||l&1)&&n!==(n=e[0].name+"")&&Z(m,n),(!U||l&2)&&z!==(z=e[1].join("/")+"")&&Z(ie,z);const p={};if(l&49&&(p.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[5]}');
|
||||
@@ -82,7 +82,7 @@ import{S as kt,i as gt,s as vt,Q as St,T as L,R as _t,e as s,w as f,b as u,c as
|
||||
|
||||
// "logout"
|
||||
pb.authStore.clear();
|
||||
`),R.$set(p),(!U||l&1)&&te!==(te=e[0].name+"")&&G(he,te),l&2){B=L(e[1]);let y;for(y=0;y<B.length;y+=1){const rt=ht(e,B,y);S[y]?S[y].p(rt,l):(S[y]=bt(rt),S[y].c(),S[y].m(Y,ge))}for(;y<S.length;y+=1)S[y].d(1);S.length=B.length}l&12&&(le=L(e[3]),P=ct(P,l,lt,1,e,le,xe,I,wt,ft,null,ut)),l&12&&(K=L(e[3]),yt(),w=ct(w,l,at,1,e,K,tt,J,$t,mt,null,pt),Pt())},i(e){if(!U){X(R.$$.fragment,e),X(F.$$.fragment,e),X(M.$$.fragment,e);for(let l=0;l<K.length;l+=1)X(w[l]);U=!0}},o(e){z(R.$$.fragment,e),z(F.$$.fragment,e),z(M.$$.fragment,e);for(let l=0;l<w.length;l+=1)z(w[l]);U=!1},d(e){e&&(d(o),d(r),d(h),d(ce),d(de),d(q),d(pe),d(C),d(be),d(j),d(fe),d(T),d(Se),d(E),d(we),d(A),d(Oe),d(Q),d(Ae),d(W)),se(R,e),Rt(S,e),se(F),se(M);for(let l=0;l<P.length;l+=1)P[l].d();for(let l=0;l<w.length;l+=1)w[l].d()}}}function Wt(i,o,a){let n,m,b,{collection:r}=o,h=200,g=[];const $=_=>a(2,h=_.code);return i.$$set=_=>{"collection"in _&&a(0,r=_.collection)},i.$$.update=()=>{var _;i.$$.dirty&1&&a(1,m=((_=r==null?void 0:r.passwordAuth)==null?void 0:_.identityFields)||[]),i.$$.dirty&2&&a(4,b=m.length==0?"NONE":"YOUR_"+m.join("_OR_").toUpperCase()),i.$$.dirty&1&&a(3,g=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:dt.dummyCollectionRecord(r)},null,2)},{code:400,body:`
|
||||
`),R.$set(p),(!U||l&1)&&te!==(te=e[0].name+"")&&Z(he,te),l&2){B=L(e[1]);let y;for(y=0;y<B.length;y+=1){const rt=ht(e,B,y);S[y]?S[y].p(rt,l):(S[y]=bt(rt),S[y].c(),S[y].m(E,ge))}for(;y<S.length;y+=1)S[y].d(1);S.length=B.length}l&12&&(le=L(e[3]),P=ct(P,l,lt,1,e,le,xe,J,wt,ft,null,ut)),l&12&&(G=L(e[3]),yt(),w=ct(w,l,at,1,e,G,tt,Q,$t,mt,null,pt),Pt())},i(e){if(!U){K(R.$$.fragment,e),K(F.$$.fragment,e),K(M.$$.fragment,e);for(let l=0;l<G.length;l+=1)K(w[l]);U=!0}},o(e){X(R.$$.fragment,e),X(F.$$.fragment,e),X(M.$$.fragment,e);for(let l=0;l<w.length;l+=1)X(w[l]);U=!1},d(e){e&&(d(o),d(r),d(h),d(ce),d(de),d(q),d(pe),d(C),d(be),d(j),d(fe),d(O),d(Se),d(V),d(we),d(A),d(Te),d(I),d(Ae),d(W)),se(R,e),Rt(S,e),se(F),se(M);for(let l=0;l<P.length;l+=1)P[l].d();for(let l=0;l<w.length;l+=1)w[l].d()}}}function Wt(i,o,a){let n,m,b,{collection:r}=o,h=200,g=[];const $=_=>a(2,h=_.code);return i.$$set=_=>{"collection"in _&&a(0,r=_.collection)},i.$$.update=()=>{var _;i.$$.dirty&1&&a(1,m=((_=r==null?void 0:r.passwordAuth)==null?void 0:_.identityFields)||[]),i.$$.dirty&2&&a(4,b=m.length==0?"NONE":"YOUR_"+m.join("_OR_").toUpperCase()),i.$$.dirty&1&&a(3,g=[{code:200,body:JSON.stringify({token:"JWT_TOKEN",record:dt.dummyCollectionRecord(r)},null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
+14
-14
@@ -1,4 +1,4 @@
|
||||
import{S as It,i as Lt,s as jt,Q as At,R as Ht,T as Y,e as o,w as _,b as i,c as Re,f as b,g as d,h as t,m as Te,j as Mt,l as Nt,x as Ut,U as Pt,V as zt,k as Jt,W as Wt,n as Kt,t as Z,a as x,o as u,d as Pe,C as Ft,p as Gt,r as ee,u as Qt}from"./index-Co7n6kj7.js";function Bt(a,s,n){const c=a.slice();return c[6]=s[n],c}function Ot(a,s,n){const c=a.slice();return c[6]=s[n],c}function Et(a,s){let n,c,y;function h(){return s[5](s[6])}return{key:a,first:null,c(){n=o("button"),n.textContent=`${s[6].code} `,b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,f){d(r,n,f),c||(y=Qt(n,"click",h),c=!0)},p(r,f){s=r,f&10&&ee(n,"active",s[1]===s[6].code)},d(r){r&&u(n),c=!1,y()}}}function St(a,s){let n,c,y,h;return c=new Ht({props:{content:s[6].body}}),{key:a,first:null,c(){n=o("div"),Re(c.$$.fragment),y=i(),b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,f){d(r,n,f),Te(c,n,null),t(n,y),h=!0},p(r,f){s=r,(!h||f&10)&&ee(n,"active",s[1]===s[6].code)},i(r){h||(Z(c.$$.fragment,r),h=!0)},o(r){x(c.$$.fragment,r),h=!1},d(r){r&&u(n),Pe(c)}}}function Vt(a){var pt,mt,bt,ft,ht,_t,yt,kt;let s,n,c=a[0].name+"",y,h,r,f,F,g,U,Fe,P,B,Be,O,Oe,Ee,te,le,w,oe,E,ae,S,se,H,ne,z,ie,q,ce,Se,re,I,J,He,k,W,Ie,de,Le,D,K,je,ue,Ae,G,Me,pe,Ne,v,Ue,me,ze,Je,We,Q,Ke,V,Ge,be,Qe,fe,Ve,he,Xe,p,_e,Ye,ye,Ze,ke,xe,$e,et,ge,tt,Ce,lt,ot,at,De,st,R,ve,L,we,T,j,C=[],nt=new Map,it,A,$=[],ct=new Map,M,qe,rt;w=new At({props:{js:`
|
||||
import{S as Ht,i as Lt,s as jt,U as At,V as St,W as Z,f as o,y as _,h as i,c as Re,j as b,l as d,n as t,m as Te,B as Mt,C as Nt,G as Ut,X as Pt,Y as zt,D as Jt,Z as Wt,E as Gt,t as Q,a as x,u,d as Pe,I as Ft,p as Kt,k as ee,o as Vt}from"./index-0yJTQg7M.js";function Bt(a,s,n){const c=a.slice();return c[6]=s[n],c}function Et(a,s,n){const c=a.slice();return c[6]=s[n],c}function Ot(a,s){let n,c,y;function h(){return s[5](s[6])}return{key:a,first:null,c(){n=o("button"),n.textContent=`${s[6].code} `,b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,f){d(r,n,f),c||(y=Vt(n,"click",h),c=!0)},p(r,f){s=r,f&10&&ee(n,"active",s[1]===s[6].code)},d(r){r&&u(n),c=!1,y()}}}function It(a,s){let n,c,y,h;return c=new St({props:{content:s[6].body}}),{key:a,first:null,c(){n=o("div"),Re(c.$$.fragment),y=i(),b(n,"class","tab-item"),ee(n,"active",s[1]===s[6].code),this.first=n},m(r,f){d(r,n,f),Te(c,n,null),t(n,y),h=!0},p(r,f){s=r,(!h||f&10)&&ee(n,"active",s[1]===s[6].code)},i(r){h||(Q(c.$$.fragment,r),h=!0)},o(r){x(c.$$.fragment,r),h=!1},d(r){r&&u(n),Pe(c)}}}function Xt(a){var pt,mt,bt,ft,ht,_t,yt,kt;let s,n,c=a[0].name+"",y,h,r,f,F,g,U,Fe,P,B,Be,E,Ee,Oe,te,le,w,oe,O,ae,I,se,S,ne,z,ie,q,ce,Ie,re,H,J,Se,k,W,He,de,Le,D,G,je,ue,Ae,K,Me,pe,Ne,v,Ue,me,ze,Je,We,V,Ge,X,Ke,be,Ve,fe,Xe,he,Ye,p,_e,Ze,ye,Qe,ke,xe,$e,et,ge,tt,Ce,lt,ot,at,De,st,R,ve,L,we,T,j,C=[],nt=new Map,it,A,$=[],ct=new Map,M,qe,rt;w=new At({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[2]}');
|
||||
@@ -28,7 +28,7 @@ import{S as It,i as Lt,s as jt,Q as At,R as Ht,T as Y,e as o,w as _,b as i,c as
|
||||
batch.collection('${(kt=a[0])==null?void 0:kt.name}').upsert(body: { ... });
|
||||
|
||||
final result = await batch.send();
|
||||
`}}),R=new Ht({props:{language:"javascript",content:`
|
||||
`}}),R=new St({props:{language:"javascript",content:`
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("@jsonPayload", JSON.stringify({
|
||||
@@ -51,18 +51,18 @@ import{S as It,i as Lt,s as jt,Q as At,R as Ht,T as Y,e as o,w as _,b as i,c as
|
||||
|
||||
// file for the second request
|
||||
formData.append("requests.1.someFileField", new File(...))
|
||||
`}});let X=Y(a[3]);const dt=e=>e[6].code;for(let e=0;e<X.length;e+=1){let l=Ot(a,X,e),m=dt(l);nt.set(m,C[e]=Et(m,l))}let N=Y(a[3]);const ut=e=>e[6].code;for(let e=0;e<N.length;e+=1){let l=Bt(a,N,e),m=ut(l);ct.set(m,$[e]=St(m,l))}return{c(){s=o("h3"),n=_("Batch create/update/upsert/delete ("),y=_(c),h=_(")"),r=i(),f=o("div"),f.innerHTML="<p>Batch and transactional create/update/upsert/delete of multiple records in a single request.</p>",F=i(),g=o("div"),U=o("div"),U.innerHTML='<i class="ri-error-warning-line"></i>',Fe=i(),P=o("div"),B=o("p"),Be=_(`The batch Web API need to be explicitly enabled and configured from the
|
||||
`),O=o("a"),O.textContent="Dashboard settings",Oe=_("."),Ee=i(),te=o("p"),te.textContent=`Because this endpoint process the requests in a single transaction, it could degrade the
|
||||
`}});let Y=Z(a[3]);const dt=e=>e[6].code;for(let e=0;e<Y.length;e+=1){let l=Et(a,Y,e),m=dt(l);nt.set(m,C[e]=Ot(m,l))}let N=Z(a[3]);const ut=e=>e[6].code;for(let e=0;e<N.length;e+=1){let l=Bt(a,N,e),m=ut(l);ct.set(m,$[e]=It(m,l))}return{c(){s=o("h3"),n=_("Batch create/update/upsert/delete ("),y=_(c),h=_(")"),r=i(),f=o("div"),f.innerHTML="<p>Batch and transactional create/update/upsert/delete of multiple records in a single request.</p>",F=i(),g=o("div"),U=o("div"),U.innerHTML='<i class="ri-error-warning-line"></i>',Fe=i(),P=o("div"),B=o("p"),Be=_(`The batch Web API need to be explicitly enabled and configured from the
|
||||
`),E=o("a"),E.textContent="Dashboard settings",Ee=_("."),Oe=i(),te=o("p"),te.textContent=`Because this endpoint process the requests in a single transaction, it could degrade the
|
||||
performance of your application if not used with proper care and configuration (e.g. too large
|
||||
allowed execution timeout, large body size limit, etc.).`,le=i(),Re(w.$$.fragment),oe=i(),E=o("h6"),E.textContent="API details",ae=i(),S=o("div"),S.innerHTML='<strong class="label label-primary">POST</strong> <div class="content">/api/batch</div>',se=i(),H=o("div"),H.textContent="Body Parameters",ne=i(),z=o("p"),z.innerHTML=`Body parameters could be sent as <em>application/json</em> or <em>multipart/form-data</em>.
|
||||
allowed execution timeout, large body size limit, etc.).`,le=i(),Re(w.$$.fragment),oe=i(),O=o("h6"),O.textContent="API details",ae=i(),I=o("div"),I.innerHTML='<strong class="label label-primary">POST</strong> <div class="content">/api/batch</div>',se=i(),S=o("div"),S.textContent="Body Parameters",ne=i(),z=o("p"),z.innerHTML=`Body parameters could be sent as <em>application/json</em> or <em>multipart/form-data</em>.
|
||||
<br/>
|
||||
File upload is supported only via <em>multipart/form-data</em> (see below for more details).`,ie=i(),q=o("table"),ce=o("thead"),ce.innerHTML='<tr><th>Param</th> <th width="80%">Description</th></tr>',Se=i(),re=o("tbody"),I=o("tr"),J=o("td"),J.innerHTML='<div class="flex txt-nowrap"><span class="label label-success">Required</span> <span>requests</span></div>',He=i(),k=o("td"),W=o("span"),W.textContent="Array<Request>",Ie=_(` - List of the requests to process.
|
||||
File upload is supported only via <em>multipart/form-data</em> (see below for more details).`,ie=i(),q=o("table"),ce=o("thead"),ce.innerHTML='<tr><th>Param</th> <th width="80%">Description</th></tr>',Ie=i(),re=o("tbody"),H=o("tr"),J=o("td"),J.innerHTML='<div class="flex txt-nowrap"><span class="label label-success">Required</span> <span>requests</span></div>',Se=i(),k=o("td"),W=o("span"),W.textContent="Array<Request>",He=_(` - List of the requests to process.
|
||||
|
||||
`),de=o("p"),de.textContent="The supported batch request actions are:",Le=i(),D=o("ul"),K=o("li"),je=_("record create - "),ue=o("code"),ue.textContent="POST /api/collections/{collection}/records",Ae=i(),G=o("li"),Me=_(`record update -
|
||||
`),pe=o("code"),pe.textContent="PATCH /api/collections/{collection}/records/{id}",Ne=i(),v=o("li"),Ue=_("record upsert - "),me=o("code"),me.textContent="PUT /api/collections/{collection}/records",ze=i(),Je=o("br"),We=i(),Q=o("small"),Q.innerHTML='(the body must have <code class="txt-sm">id</code> field)',Ke=i(),V=o("li"),Ge=_(`record delete -
|
||||
`),be=o("code"),be.textContent="DELETE /api/collections/{collection}/records/{id}",Qe=i(),fe=o("p"),fe.textContent="Each batch Request element have the following properties:",Ve=i(),he=o("ul"),he.innerHTML=`<li><code>url path</code> <em>(could include query parameters)</em></li> <li><code>method</code> <em>(GET, POST, PUT, PATCH, DELETE)</em></li> <li><code>headers</code> <br/> <em>(if the <code>POST /api/batch</code> call has <code>Authorization</code> header it
|
||||
is forwarded to all batch requests automatically)</em></li> <li><code>body</code></li>`,Xe=i(),p=o("p"),_e=o("strong"),_e.textContent="NB!",Ye=_(` When the batch request is send as
|
||||
`),ye=o("code"),ye.textContent="multipart/form-data",Ze=_(`, the regular batch action fields are expected to be
|
||||
`),de=o("p"),de.textContent="The supported batch request actions are:",Le=i(),D=o("ul"),G=o("li"),je=_("record create - "),ue=o("code"),ue.textContent="POST /api/collections/{collection}/records",Ae=i(),K=o("li"),Me=_(`record update -
|
||||
`),pe=o("code"),pe.textContent="PATCH /api/collections/{collection}/records/{id}",Ne=i(),v=o("li"),Ue=_("record upsert - "),me=o("code"),me.textContent="PUT /api/collections/{collection}/records",ze=i(),Je=o("br"),We=i(),V=o("small"),V.innerHTML='(the body must have <code class="txt-sm">id</code> field)',Ge=i(),X=o("li"),Ke=_(`record delete -
|
||||
`),be=o("code"),be.textContent="DELETE /api/collections/{collection}/records/{id}",Ve=i(),fe=o("p"),fe.textContent="Each batch Request element have the following properties:",Xe=i(),he=o("ul"),he.innerHTML=`<li><code>url path</code> <em>(could include query parameters)</em></li> <li><code>method</code> <em>(GET, POST, PUT, PATCH, DELETE)</em></li> <li><code>headers</code> <br/> <em>(if the <code>POST /api/batch</code> call has <code>Authorization</code> header it
|
||||
is forwarded to all batch requests automatically)</em></li> <li><code>body</code></li>`,Ye=i(),p=o("p"),_e=o("strong"),_e.textContent="NB!",Ze=_(` When the batch request is send as
|
||||
`),ye=o("code"),ye.textContent="multipart/form-data",Qe=_(`, the regular batch action fields are expected to be
|
||||
submitted as serailized json under the `),ke=o("code"),ke.textContent="@jsonPayload",xe=_(` field and file keys need
|
||||
to follow the pattern `),$e=o("code"),$e.textContent="requests.N.fileField",et=_(` or
|
||||
`),ge=o("code"),ge.textContent="requests[N].fileField",tt=i(),Ce=o("em"),Ce.textContent=`(this is usually handled transparently by the SDKs when their specific object notation
|
||||
@@ -71,7 +71,7 @@ import{S as It,i as Lt,s as jt,Q as At,R as Ht,T as Y,e as o,w as _,b as i,c as
|
||||
`),ot=o("br"),at=_(`
|
||||
If you don't use the SDKs or prefer manually to construct the `),De=o("code"),De.textContent="FormData",st=_(`
|
||||
body, then it could look something like:
|
||||
`),Re(R.$$.fragment),ve=i(),L=o("div"),L.textContent="Responses",we=i(),T=o("div"),j=o("div");for(let e=0;e<C.length;e+=1)C[e].c();it=i(),A=o("div");for(let e=0;e<$.length;e+=1)$[e].c();b(s,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(U,"class","icon"),b(O,"href","/settings"),b(P,"class","content"),b(g,"class","alert alert-warning"),b(E,"class","m-b-xs"),b(S,"class","api-route alert alert-success"),b(H,"class","section-title"),b(J,"valign","top"),b(W,"class","label"),b(Q,"class","txt-hint"),b(q,"class","table-compact table-border m-t-xs m-b-base"),b(L,"class","section-title"),b(j,"class","tabs-header compact combined left"),b(A,"class","tabs-content"),b(T,"class","tabs")},m(e,l){d(e,s,l),t(s,n),t(s,y),t(s,h),d(e,r,l),d(e,f,l),d(e,F,l),d(e,g,l),t(g,U),t(g,Fe),t(g,P),t(P,B),t(B,Be),t(B,O),t(B,Oe),t(P,Ee),t(P,te),d(e,le,l),Te(w,e,l),d(e,oe,l),d(e,E,l),d(e,ae,l),d(e,S,l),d(e,se,l),d(e,H,l),d(e,ne,l),d(e,z,l),d(e,ie,l),d(e,q,l),t(q,ce),t(q,Se),t(q,re),t(re,I),t(I,J),t(I,He),t(I,k),t(k,W),t(k,Ie),t(k,de),t(k,Le),t(k,D),t(D,K),t(K,je),t(K,ue),t(D,Ae),t(D,G),t(G,Me),t(G,pe),t(D,Ne),t(D,v),t(v,Ue),t(v,me),t(v,ze),t(v,Je),t(v,We),t(v,Q),t(D,Ke),t(D,V),t(V,Ge),t(V,be),t(k,Qe),t(k,fe),t(k,Ve),t(k,he),t(k,Xe),t(k,p),t(p,_e),t(p,Ye),t(p,ye),t(p,Ze),t(p,ke),t(p,xe),t(p,$e),t(p,et),t(p,ge),t(p,tt),t(p,Ce),t(p,lt),t(p,ot),t(p,at),t(p,De),t(p,st),Te(R,p,null),d(e,ve,l),d(e,L,l),d(e,we,l),d(e,T,l),t(T,j);for(let m=0;m<C.length;m+=1)C[m]&&C[m].m(j,null);t(T,it),t(T,A);for(let m=0;m<$.length;m+=1)$[m]&&$[m].m(A,null);M=!0,qe||(rt=Mt(Nt.call(null,O)),qe=!0)},p(e,[l]){var gt,Ct,Dt,vt,wt,qt,Rt,Tt;(!M||l&1)&&c!==(c=e[0].name+"")&&Ut(y,c);const m={};l&5&&(m.js=`
|
||||
`),Re(R.$$.fragment),ve=i(),L=o("div"),L.textContent="Responses",we=i(),T=o("div"),j=o("div");for(let e=0;e<C.length;e+=1)C[e].c();it=i(),A=o("div");for(let e=0;e<$.length;e+=1)$[e].c();b(s,"class","m-b-sm"),b(f,"class","content txt-lg m-b-sm"),b(U,"class","icon"),b(E,"href","/settings"),b(P,"class","content"),b(g,"class","alert alert-warning"),b(O,"class","m-b-xs"),b(I,"class","api-route alert alert-success"),b(S,"class","section-title"),b(J,"valign","top"),b(W,"class","label"),b(V,"class","txt-hint"),b(q,"class","table-compact table-border m-t-xs m-b-base"),b(L,"class","section-title"),b(j,"class","tabs-header compact combined left"),b(A,"class","tabs-content"),b(T,"class","tabs")},m(e,l){d(e,s,l),t(s,n),t(s,y),t(s,h),d(e,r,l),d(e,f,l),d(e,F,l),d(e,g,l),t(g,U),t(g,Fe),t(g,P),t(P,B),t(B,Be),t(B,E),t(B,Ee),t(P,Oe),t(P,te),d(e,le,l),Te(w,e,l),d(e,oe,l),d(e,O,l),d(e,ae,l),d(e,I,l),d(e,se,l),d(e,S,l),d(e,ne,l),d(e,z,l),d(e,ie,l),d(e,q,l),t(q,ce),t(q,Ie),t(q,re),t(re,H),t(H,J),t(H,Se),t(H,k),t(k,W),t(k,He),t(k,de),t(k,Le),t(k,D),t(D,G),t(G,je),t(G,ue),t(D,Ae),t(D,K),t(K,Me),t(K,pe),t(D,Ne),t(D,v),t(v,Ue),t(v,me),t(v,ze),t(v,Je),t(v,We),t(v,V),t(D,Ge),t(D,X),t(X,Ke),t(X,be),t(k,Ve),t(k,fe),t(k,Xe),t(k,he),t(k,Ye),t(k,p),t(p,_e),t(p,Ze),t(p,ye),t(p,Qe),t(p,ke),t(p,xe),t(p,$e),t(p,et),t(p,ge),t(p,tt),t(p,Ce),t(p,lt),t(p,ot),t(p,at),t(p,De),t(p,st),Te(R,p,null),d(e,ve,l),d(e,L,l),d(e,we,l),d(e,T,l),t(T,j);for(let m=0;m<C.length;m+=1)C[m]&&C[m].m(j,null);t(T,it),t(T,A);for(let m=0;m<$.length;m+=1)$[m]&&$[m].m(A,null);M=!0,qe||(rt=Mt(Nt.call(null,E)),qe=!0)},p(e,[l]){var gt,Ct,Dt,vt,wt,qt,Rt,Tt;(!M||l&1)&&c!==(c=e[0].name+"")&&Ut(y,c);const m={};l&5&&(m.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${e[2]}');
|
||||
@@ -124,7 +124,7 @@ import{S as It,i as Lt,s as jt,Q as At,R as Ht,T as Y,e as o,w as _,b as i,c as
|
||||
|
||||
// file for the second request
|
||||
formData.append("requests.1.someFileField", new File(...))
|
||||
`),R.$set($t),l&10&&(X=Y(e[3]),C=Pt(C,l,dt,1,e,X,nt,j,zt,Et,null,Ot)),l&10&&(N=Y(e[3]),Jt(),$=Pt($,l,ut,1,e,N,ct,A,Wt,St,null,Bt),Kt())},i(e){if(!M){Z(w.$$.fragment,e),Z(R.$$.fragment,e);for(let l=0;l<N.length;l+=1)Z($[l]);M=!0}},o(e){x(w.$$.fragment,e),x(R.$$.fragment,e);for(let l=0;l<$.length;l+=1)x($[l]);M=!1},d(e){e&&(u(s),u(r),u(f),u(F),u(g),u(le),u(oe),u(E),u(ae),u(S),u(se),u(H),u(ne),u(z),u(ie),u(q),u(ve),u(L),u(we),u(T)),Pe(w,e),Pe(R);for(let l=0;l<C.length;l+=1)C[l].d();for(let l=0;l<$.length;l+=1)$[l].d();qe=!1,rt()}}}function Xt(a,s,n){let c,y,{collection:h}=s,r=200,f=[];const F=g=>n(1,r=g.code);return a.$$set=g=>{"collection"in g&&n(0,h=g.collection)},a.$$.update=()=>{a.$$.dirty&1&&n(4,y=Ft.dummyCollectionRecord(h)),a.$$.dirty&17&&h!=null&&h.id&&(f.push({code:200,body:JSON.stringify([y,Object.assign({},y,{id:y+"2"})],null,2)}),f.push({code:400,body:`
|
||||
`),R.$set($t),l&10&&(Y=Z(e[3]),C=Pt(C,l,dt,1,e,Y,nt,j,zt,Ot,null,Et)),l&10&&(N=Z(e[3]),Jt(),$=Pt($,l,ut,1,e,N,ct,A,Wt,It,null,Bt),Gt())},i(e){if(!M){Q(w.$$.fragment,e),Q(R.$$.fragment,e);for(let l=0;l<N.length;l+=1)Q($[l]);M=!0}},o(e){x(w.$$.fragment,e),x(R.$$.fragment,e);for(let l=0;l<$.length;l+=1)x($[l]);M=!1},d(e){e&&(u(s),u(r),u(f),u(F),u(g),u(le),u(oe),u(O),u(ae),u(I),u(se),u(S),u(ne),u(z),u(ie),u(q),u(ve),u(L),u(we),u(T)),Pe(w,e),Pe(R);for(let l=0;l<C.length;l+=1)C[l].d();for(let l=0;l<$.length;l+=1)$[l].d();qe=!1,rt()}}}function Yt(a,s,n){let c,y,{collection:h}=s,r=200,f=[];const F=g=>n(1,r=g.code);return a.$$set=g=>{"collection"in g&&n(0,h=g.collection)},a.$$.update=()=>{a.$$.dirty&1&&n(4,y=Ft.dummyCollectionRecord(h)),a.$$.dirty&17&&h!=null&&h.id&&(f.push({code:200,body:JSON.stringify([y,Object.assign({},y,{id:y+"2"})],null,2)}),f.push({code:400,body:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Batch transaction failed.",
|
||||
@@ -154,4 +154,4 @@ import{S as It,i as Lt,s as jt,Q as At,R as Ht,T as Y,e as o,w as _,b as i,c as
|
||||
"message": "Batch requests are not allowed.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},n(2,c=Ft.getApiExampleUrl(Gt.baseURL)),[h,r,c,f,y,F]}class Zt extends It{constructor(s){super(),Lt(this,s,Xt,Vt,jt,{collection:0})}}export{Zt as default};
|
||||
`}))},n(2,c=Ft.getApiExampleUrl(Kt.baseURL)),[h,r,c,f,y,F]}class Qt extends Ht{constructor(s){super(),Lt(this,s,Yt,Xt,jt,{collection:0})}}export{Qt 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
+12
-12
@@ -1,9 +1,9 @@
|
||||
import{S as $t,i as qt,s as Tt,Q as St,C as ee,T as ue,R as Ct,e as s,w as _,b as p,c as $e,f as w,g as r,h as i,m as qe,x as oe,U as Ve,V as pt,k as Ot,W as Mt,n as Pt,t as ye,a as ve,o as d,d as Te,p as Ft,r as Se,u as Lt,y as we,E as Ht}from"./index-Co7n6kj7.js";import{F as Rt}from"./FieldsQueryParam-DH-ZC4qv.js";function mt(a,e,t){const l=a.slice();return l[10]=e[t],l}function bt(a,e,t){const l=a.slice();return l[10]=e[t],l}function _t(a,e,t){const l=a.slice();return l[15]=e[t],l}function kt(a){let e;return{c(){e=s("p"),e.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",w(e,"class","txt-hint txt-sm txt-right")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function ht(a){let e,t,l,u,c,f,b,m,$,h,g,B,S,O,R,M,U,J,T,W,P,q,k,F,te,K,I,re,Y,x,G;function fe(y,C){var V,z,H;return C&1&&(f=null),f==null&&(f=!!((H=(z=(V=y[0])==null?void 0:V.fields)==null?void 0:z.find(xt))!=null&&H.required)),f?Bt:At}let le=fe(a,-1),E=le(a);function X(y,C){var V,z,H;return C&1&&(U=null),U==null&&(U=!!((H=(z=(V=y[0])==null?void 0:V.fields)==null?void 0:z.find(Yt))!=null&&H.required)),U?Vt:jt}let Z=X(a,-1),L=Z(a);return{c(){e=s("tr"),e.innerHTML='<td colspan="3" class="txt-hint txt-bold">Auth specific fields</td>',t=p(),l=s("tr"),u=s("td"),c=s("div"),E.c(),b=p(),m=s("span"),m.textContent="email",$=p(),h=s("td"),h.innerHTML='<span class="label">String</span>',g=p(),B=s("td"),B.textContent="Auth record email address.",S=p(),O=s("tr"),R=s("td"),M=s("div"),L.c(),J=p(),T=s("span"),T.textContent="emailVisibility",W=p(),P=s("td"),P.innerHTML='<span class="label">Boolean</span>',q=p(),k=s("td"),k.textContent="Whether to show/hide the auth record email when fetching the record data.",F=p(),te=s("tr"),te.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>Auth record password.</td>',K=p(),I=s("tr"),I.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>passwordConfirm</span></div></td> <td><span class="label">String</span></td> <td>Auth record password confirmation.</td>',re=p(),Y=s("tr"),Y.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.
|
||||
import{S as $t,i as qt,s as St,U as Tt,I as ee,W as ue,V as Ct,f as s,y as _,h as p,c as $e,j as w,l as r,n as i,m as qe,G as oe,X as Ve,Y as pt,D as Ot,Z as Mt,E as Pt,t as ye,a as ve,u as d,d as Se,p as Ft,k as Te,o as Ht,H as we,K as Lt}from"./index-0yJTQg7M.js";import{F as Rt}from"./FieldsQueryParam-BrZEETlt.js";function mt(a,e,t){const l=a.slice();return l[10]=e[t],l}function bt(a,e,t){const l=a.slice();return l[10]=e[t],l}function _t(a,e,t){const l=a.slice();return l[15]=e[t],l}function kt(a){let e;return{c(){e=s("p"),e.innerHTML="Requires superuser <code>Authorization:TOKEN</code> header",w(e,"class","txt-hint txt-sm txt-right")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function ht(a){let e,t,l,u,c,f,b,m,$,h,g,A,T,O,R,M,U,J,S,Q,P,q,k,F,te,Y,I,re,z,G,X;function fe(y,C){var V,W,L;return C&1&&(f=null),f==null&&(f=!!((L=(W=(V=y[0])==null?void 0:V.fields)==null?void 0:W.find(Gt))!=null&&L.required)),f?At:jt}let le=fe(a,-1),E=le(a);function Z(y,C){var V,W,L;return C&1&&(U=null),U==null&&(U=!!((L=(W=(V=y[0])==null?void 0:V.fields)==null?void 0:W.find(zt))!=null&&L.required)),U?Vt:Bt}let x=Z(a,-1),H=x(a);return{c(){e=s("tr"),e.innerHTML='<td colspan="3" class="txt-hint txt-bold">Auth specific fields</td>',t=p(),l=s("tr"),u=s("td"),c=s("div"),E.c(),b=p(),m=s("span"),m.textContent="email",$=p(),h=s("td"),h.innerHTML='<span class="label">String</span>',g=p(),A=s("td"),A.textContent="Auth record email address.",T=p(),O=s("tr"),R=s("td"),M=s("div"),H.c(),J=p(),S=s("span"),S.textContent="emailVisibility",Q=p(),P=s("td"),P.innerHTML='<span class="label">Boolean</span>',q=p(),k=s("td"),k.textContent="Whether to show/hide the auth record email when fetching the record data.",F=p(),te=s("tr"),te.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>Auth record password.</td>',Y=p(),I=s("tr"),I.innerHTML='<td><div class="inline-flex"><span class="label label-success">Required</span> <span>passwordConfirm</span></div></td> <td><span class="label">String</span></td> <td>Auth record password confirmation.</td>',re=p(),z=s("tr"),z.innerHTML=`<td><div class="inline-flex"><span class="label label-warning">Optional</span> <span>verified</span></div></td> <td><span class="label">Boolean</span></td> <td>Indicates whether the auth record is verified or not.
|
||||
<br/>
|
||||
This field can be set only by superusers or auth records with "Manage" access.</td>`,x=p(),G=s("tr"),G.innerHTML='<td colspan="3" class="txt-hint txt-bold">Other fields</td>',w(c,"class","inline-flex"),w(M,"class","inline-flex")},m(y,C){r(y,e,C),r(y,t,C),r(y,l,C),i(l,u),i(u,c),E.m(c,null),i(c,b),i(c,m),i(l,$),i(l,h),i(l,g),i(l,B),r(y,S,C),r(y,O,C),i(O,R),i(R,M),L.m(M,null),i(M,J),i(M,T),i(O,W),i(O,P),i(O,q),i(O,k),r(y,F,C),r(y,te,C),r(y,K,C),r(y,I,C),r(y,re,C),r(y,Y,C),r(y,x,C),r(y,G,C)},p(y,C){le!==(le=fe(y,C))&&(E.d(1),E=le(y),E&&(E.c(),E.m(c,b))),Z!==(Z=X(y,C))&&(L.d(1),L=Z(y),L&&(L.c(),L.m(M,J)))},d(y){y&&(d(e),d(t),d(l),d(S),d(O),d(F),d(te),d(K),d(I),d(re),d(Y),d(x),d(G)),E.d(),L.d()}}}function At(a){let e;return{c(){e=s("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Bt(a){let e;return{c(){e=s("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function jt(a){let e;return{c(){e=s("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Vt(a){let e;return{c(){e=s("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Nt(a){let e;return{c(){e=s("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Dt(a){let e;return{c(){e=s("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Jt(a){let e,t=a[15].maxSelect===1?"id":"ids",l,u;return{c(){e=_("Relation record "),l=_(t),u=_(".")},m(c,f){r(c,e,f),r(c,l,f),r(c,u,f)},p(c,f){f&64&&t!==(t=c[15].maxSelect===1?"id":"ids")&&oe(l,t)},d(c){c&&(d(e),d(l),d(u))}}}function Et(a){let e,t,l,u,c,f,b,m,$;return{c(){e=_("File object."),t=s("br"),l=_(`
|
||||
This field can be set only by superusers or auth records with "Manage" access.</td>`,G=p(),X=s("tr"),X.innerHTML='<td colspan="3" class="txt-hint txt-bold">Other fields</td>',w(c,"class","inline-flex"),w(M,"class","inline-flex")},m(y,C){r(y,e,C),r(y,t,C),r(y,l,C),i(l,u),i(u,c),E.m(c,null),i(c,b),i(c,m),i(l,$),i(l,h),i(l,g),i(l,A),r(y,T,C),r(y,O,C),i(O,R),i(R,M),H.m(M,null),i(M,J),i(M,S),i(O,Q),i(O,P),i(O,q),i(O,k),r(y,F,C),r(y,te,C),r(y,Y,C),r(y,I,C),r(y,re,C),r(y,z,C),r(y,G,C),r(y,X,C)},p(y,C){le!==(le=fe(y,C))&&(E.d(1),E=le(y),E&&(E.c(),E.m(c,b))),x!==(x=Z(y,C))&&(H.d(1),H=x(y),H&&(H.c(),H.m(M,J)))},d(y){y&&(d(e),d(t),d(l),d(T),d(O),d(F),d(te),d(Y),d(I),d(re),d(z),d(G),d(X)),E.d(),H.d()}}}function jt(a){let e;return{c(){e=s("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function At(a){let e;return{c(){e=s("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Bt(a){let e;return{c(){e=s("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Vt(a){let e;return{c(){e=s("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Dt(a){let e;return{c(){e=s("span"),e.textContent="Required",w(e,"class","label label-success")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Nt(a){let e;return{c(){e=s("span"),e.textContent="Optional",w(e,"class","label label-warning")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function Jt(a){let e,t=a[15].maxSelect===1?"id":"ids",l,u;return{c(){e=_("Relation record "),l=_(t),u=_(".")},m(c,f){r(c,e,f),r(c,l,f),r(c,u,f)},p(c,f){f&64&&t!==(t=c[15].maxSelect===1?"id":"ids")&&oe(l,t)},d(c){c&&(d(e),d(l),d(u))}}}function Et(a){let e,t,l,u,c,f,b,m,$;return{c(){e=_("File object."),t=s("br"),l=_(`
|
||||
Set to empty value (`),u=s("code"),u.textContent="null",c=_(", "),f=s("code"),f.textContent='""',b=_(" or "),m=s("code"),m.textContent="[]",$=_(`) to delete
|
||||
already uploaded file(s).`)},m(h,g){r(h,e,g),r(h,t,g),r(h,l,g),r(h,u,g),r(h,c,g),r(h,f,g),r(h,b,g),r(h,m,g),r(h,$,g)},p:we,d(h){h&&(d(e),d(t),d(l),d(u),d(c),d(f),d(b),d(m),d($))}}}function Ut(a){let e;return{c(){e=_("URL address.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function It(a){let e;return{c(){e=_("Email address.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function Qt(a){let e;return{c(){e=_("JSON array or object.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function Wt(a){let e;return{c(){e=_("Number value.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function zt(a){let e,t,l=a[15].autogeneratePattern&&yt();return{c(){e=_(`Plain text value.
|
||||
`),l&&l.c(),t=Ht()},m(u,c){r(u,e,c),l&&l.m(u,c),r(u,t,c)},p(u,c){u[15].autogeneratePattern?l||(l=yt(),l.c(),l.m(t.parentNode,t)):l&&(l.d(1),l=null)},d(u){u&&(d(e),d(t)),l&&l.d(u)}}}function yt(a){let e;return{c(){e=_("It is autogenerated if not set.")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function vt(a,e){let t,l,u,c,f,b=e[15].name+"",m,$,h,g,B=ee.getFieldValueType(e[15])+"",S,O,R,M;function U(k,F){return!k[15].required||k[15].type=="text"&&k[15].autogeneratePattern?Dt:Nt}let J=U(e),T=J(e);function W(k,F){if(k[15].type==="text")return zt;if(k[15].type==="number")return Wt;if(k[15].type==="json")return Qt;if(k[15].type==="email")return It;if(k[15].type==="url")return Ut;if(k[15].type==="file")return Et;if(k[15].type==="relation")return Jt}let P=W(e),q=P&&P(e);return{key:a,first:null,c(){t=s("tr"),l=s("td"),u=s("div"),T.c(),c=p(),f=s("span"),m=_(b),$=p(),h=s("td"),g=s("span"),S=_(B),O=p(),R=s("td"),q&&q.c(),M=p(),w(u,"class","inline-flex"),w(g,"class","label"),this.first=t},m(k,F){r(k,t,F),i(t,l),i(l,u),T.m(u,null),i(u,c),i(u,f),i(f,m),i(t,$),i(t,h),i(h,g),i(g,S),i(t,O),i(t,R),q&&q.m(R,null),i(t,M)},p(k,F){e=k,J!==(J=U(e))&&(T.d(1),T=J(e),T&&(T.c(),T.m(u,c))),F&64&&b!==(b=e[15].name+"")&&oe(m,b),F&64&&B!==(B=ee.getFieldValueType(e[15])+"")&&oe(S,B),P===(P=W(e))&&q?q.p(e,F):(q&&q.d(1),q=P&&P(e),q&&(q.c(),q.m(R,null)))},d(k){k&&d(t),T.d(),q&&q.d()}}}function wt(a,e){let t,l=e[10].code+"",u,c,f,b;function m(){return e[9](e[10])}return{key:a,first:null,c(){t=s("button"),u=_(l),c=p(),w(t,"class","tab-item"),Se(t,"active",e[2]===e[10].code),this.first=t},m($,h){r($,t,h),i(t,u),i(t,c),f||(b=Lt(t,"click",m),f=!0)},p($,h){e=$,h&8&&l!==(l=e[10].code+"")&&oe(u,l),h&12&&Se(t,"active",e[2]===e[10].code)},d($){$&&d(t),f=!1,b()}}}function gt(a,e){let t,l,u,c;return l=new Ct({props:{content:e[10].body}}),{key:a,first:null,c(){t=s("div"),$e(l.$$.fragment),u=p(),w(t,"class","tab-item"),Se(t,"active",e[2]===e[10].code),this.first=t},m(f,b){r(f,t,b),qe(l,t,null),i(t,u),c=!0},p(f,b){e=f;const m={};b&8&&(m.content=e[10].body),l.$set(m),(!c||b&12)&&Se(t,"active",e[2]===e[10].code)},i(f){c||(ye(l.$$.fragment,f),c=!0)},o(f){ve(l.$$.fragment,f),c=!1},d(f){f&&d(t),Te(l)}}}function Kt(a){var at,st,ot,rt;let e,t,l=a[0].name+"",u,c,f,b,m,$,h,g=a[0].name+"",B,S,O,R,M,U,J,T,W,P,q,k,F,te,K,I,re,Y,x=a[0].name+"",G,fe,le,E,X,Z,L,y,C,V,z,H=[],Ne=new Map,Oe,pe,Me,ne,Pe,De,me,ie,Fe,Je,Le,Ee,A,Ue,de,Ie,Qe,We,He,ze,Re,Ke,Ye,xe,Ae,Ge,Xe,ce,Be,be,je,ae,_e,Q=[],Ze=new Map,et,ke,N=[],tt=new Map,se;T=new St({props:{js:`
|
||||
already uploaded file(s).`)},m(h,g){r(h,e,g),r(h,t,g),r(h,l,g),r(h,u,g),r(h,c,g),r(h,f,g),r(h,b,g),r(h,m,g),r(h,$,g)},p:we,d(h){h&&(d(e),d(t),d(l),d(u),d(c),d(f),d(b),d(m),d($))}}}function Ut(a){let e;return{c(){e=_("URL address.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function It(a){let e;return{c(){e=_("Email address.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function Kt(a){let e;return{c(){e=_("JSON array or object.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function Qt(a){let e;return{c(){e=_("Number value.")},m(t,l){r(t,e,l)},p:we,d(t){t&&d(e)}}}function Wt(a){let e,t,l=a[15].autogeneratePattern&&yt();return{c(){e=_(`Plain text value.
|
||||
`),l&&l.c(),t=Lt()},m(u,c){r(u,e,c),l&&l.m(u,c),r(u,t,c)},p(u,c){u[15].autogeneratePattern?l||(l=yt(),l.c(),l.m(t.parentNode,t)):l&&(l.d(1),l=null)},d(u){u&&(d(e),d(t)),l&&l.d(u)}}}function yt(a){let e;return{c(){e=_("It is autogenerated if not set.")},m(t,l){r(t,e,l)},d(t){t&&d(e)}}}function vt(a,e){let t,l,u,c,f,b=e[15].name+"",m,$,h,g,A=ee.getFieldValueType(e[15])+"",T,O,R,M;function U(k,F){return!k[15].required||k[15].type=="text"&&k[15].autogeneratePattern?Nt:Dt}let J=U(e),S=J(e);function Q(k,F){if(k[15].type==="text")return Wt;if(k[15].type==="number")return Qt;if(k[15].type==="json")return Kt;if(k[15].type==="email")return It;if(k[15].type==="url")return Ut;if(k[15].type==="file")return Et;if(k[15].type==="relation")return Jt}let P=Q(e),q=P&&P(e);return{key:a,first:null,c(){t=s("tr"),l=s("td"),u=s("div"),S.c(),c=p(),f=s("span"),m=_(b),$=p(),h=s("td"),g=s("span"),T=_(A),O=p(),R=s("td"),q&&q.c(),M=p(),w(u,"class","inline-flex"),w(g,"class","label"),this.first=t},m(k,F){r(k,t,F),i(t,l),i(l,u),S.m(u,null),i(u,c),i(u,f),i(f,m),i(t,$),i(t,h),i(h,g),i(g,T),i(t,O),i(t,R),q&&q.m(R,null),i(t,M)},p(k,F){e=k,J!==(J=U(e))&&(S.d(1),S=J(e),S&&(S.c(),S.m(u,c))),F&64&&b!==(b=e[15].name+"")&&oe(m,b),F&64&&A!==(A=ee.getFieldValueType(e[15])+"")&&oe(T,A),P===(P=Q(e))&&q?q.p(e,F):(q&&q.d(1),q=P&&P(e),q&&(q.c(),q.m(R,null)))},d(k){k&&d(t),S.d(),q&&q.d()}}}function wt(a,e){let t,l=e[10].code+"",u,c,f,b;function m(){return e[9](e[10])}return{key:a,first:null,c(){t=s("button"),u=_(l),c=p(),w(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m($,h){r($,t,h),i(t,u),i(t,c),f||(b=Ht(t,"click",m),f=!0)},p($,h){e=$,h&8&&l!==(l=e[10].code+"")&&oe(u,l),h&12&&Te(t,"active",e[2]===e[10].code)},d($){$&&d(t),f=!1,b()}}}function gt(a,e){let t,l,u,c;return l=new Ct({props:{content:e[10].body}}),{key:a,first:null,c(){t=s("div"),$e(l.$$.fragment),u=p(),w(t,"class","tab-item"),Te(t,"active",e[2]===e[10].code),this.first=t},m(f,b){r(f,t,b),qe(l,t,null),i(t,u),c=!0},p(f,b){e=f;const m={};b&8&&(m.content=e[10].body),l.$set(m),(!c||b&12)&&Te(t,"active",e[2]===e[10].code)},i(f){c||(ye(l.$$.fragment,f),c=!0)},o(f){ve(l.$$.fragment,f),c=!1},d(f){f&&d(t),Se(l)}}}function Yt(a){var at,st,ot,rt;let e,t,l=a[0].name+"",u,c,f,b,m,$,h,g=a[0].name+"",A,T,O,R,M,U,J,S,Q,P,q,k,F,te,Y,I,re,z,G=a[0].name+"",X,fe,le,E,Z,x,H,y,C,V,W,L=[],De=new Map,Oe,pe,Me,ne,Pe,Ne,me,ie,Fe,Je,He,Ee,j,Ue,de,Ie,Ke,Qe,Le,We,Re,Ye,ze,Ge,je,Xe,Ze,ce,Ae,be,Be,ae,_e,K=[],xe=new Map,et,ke,D=[],tt=new Map,se;S=new Tt({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[5]}');
|
||||
@@ -31,18 +31,18 @@ final record = await pb.collection('${(ot=a[0])==null?void 0:ot.name}').create(b
|
||||
`+(a[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(rt=a[0])==null?void 0:rt.name}').requestVerification('test@example.com');
|
||||
`:"")}});let D=a[7]&&kt(),j=a[1]&&ht(a),ge=ue(a[6]);const lt=n=>n[15].name;for(let n=0;n<ge.length;n+=1){let o=_t(a,ge,n),v=lt(o);Ne.set(v,H[n]=vt(v,o))}de=new Ct({props:{content:"?expand=relField1,relField2.subRelField"}}),ce=new Rt({});let Ce=ue(a[3]);const nt=n=>n[10].code;for(let n=0;n<Ce.length;n+=1){let o=bt(a,Ce,n),v=nt(o);Ze.set(v,Q[n]=wt(v,o))}let he=ue(a[3]);const it=n=>n[10].code;for(let n=0;n<he.length;n+=1){let o=mt(a,he,n),v=it(o);tt.set(v,N[n]=gt(v,o))}return{c(){e=s("h3"),t=_("Create ("),u=_(l),c=_(")"),f=p(),b=s("div"),m=s("p"),$=_("Create a new "),h=s("strong"),B=_(g),S=_(" record."),O=p(),R=s("p"),R.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
`:"")}});let N=a[7]&&kt(),B=a[1]&&ht(a),ge=ue(a[6]);const lt=n=>n[15].name;for(let n=0;n<ge.length;n+=1){let o=_t(a,ge,n),v=lt(o);De.set(v,L[n]=vt(v,o))}de=new Ct({props:{content:"?expand=relField1,relField2.subRelField"}}),ce=new Rt({});let Ce=ue(a[3]);const nt=n=>n[10].code;for(let n=0;n<Ce.length;n+=1){let o=bt(a,Ce,n),v=nt(o);xe.set(v,K[n]=wt(v,o))}let he=ue(a[3]);const it=n=>n[10].code;for(let n=0;n<he.length;n+=1){let o=mt(a,he,n),v=it(o);tt.set(v,D[n]=gt(v,o))}return{c(){e=s("h3"),t=_("Create ("),u=_(l),c=_(")"),f=p(),b=s("div"),m=s("p"),$=_("Create a new "),h=s("strong"),A=_(g),T=_(" record."),O=p(),R=s("p"),R.innerHTML=`Body parameters could be sent as <code>application/json</code> or
|
||||
<code>multipart/form-data</code>.`,M=p(),U=s("p"),U.innerHTML=`File upload is supported only via <code>multipart/form-data</code>.
|
||||
<br/>
|
||||
For more info and examples you could check the detailed
|
||||
<a href="https://pocketbase.io/docs/files-handling/" target="_blank" rel="noopener noreferrer">Files upload and handling docs
|
||||
</a>.`,J=p(),$e(T.$$.fragment),W=p(),P=s("h6"),P.textContent="API details",q=p(),k=s("div"),F=s("strong"),F.textContent="POST",te=p(),K=s("div"),I=s("p"),re=_("/api/collections/"),Y=s("strong"),G=_(x),fe=_("/records"),le=p(),D&&D.c(),E=p(),X=s("div"),X.textContent="Body Parameters",Z=p(),L=s("table"),y=s("thead"),y.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',C=p(),V=s("tbody"),j&&j.c(),z=p();for(let n=0;n<H.length;n+=1)H[n].c();Oe=p(),pe=s("div"),pe.textContent="Query parameters",Me=p(),ne=s("table"),Pe=s("thead"),Pe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',De=p(),me=s("tbody"),ie=s("tr"),Fe=s("td"),Fe.textContent="expand",Je=p(),Le=s("td"),Le.innerHTML='<span class="label">String</span>',Ee=p(),A=s("td"),Ue=_(`Auto expand relations when returning the created record. Ex.:
|
||||
</a>.`,J=p(),$e(S.$$.fragment),Q=p(),P=s("h6"),P.textContent="API details",q=p(),k=s("div"),F=s("strong"),F.textContent="POST",te=p(),Y=s("div"),I=s("p"),re=_("/api/collections/"),z=s("strong"),X=_(G),fe=_("/records"),le=p(),N&&N.c(),E=p(),Z=s("div"),Z.textContent="Body Parameters",x=p(),H=s("table"),y=s("thead"),y.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr>',C=p(),V=s("tbody"),B&&B.c(),W=p();for(let n=0;n<L.length;n+=1)L[n].c();Oe=p(),pe=s("div"),pe.textContent="Query parameters",Me=p(),ne=s("table"),Pe=s("thead"),Pe.innerHTML='<tr><th>Param</th> <th>Type</th> <th width="60%">Description</th></tr>',Ne=p(),me=s("tbody"),ie=s("tr"),Fe=s("td"),Fe.textContent="expand",Je=p(),He=s("td"),He.innerHTML='<span class="label">String</span>',Ee=p(),j=s("td"),Ue=_(`Auto expand relations when returning the created record. Ex.:
|
||||
`),$e(de.$$.fragment),Ie=_(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Qe=s("br"),We=_(`
|
||||
Supports up to 6-levels depth nested relations expansion. `),Ke=s("br"),Qe=_(`
|
||||
The expanded relations will be appended to the record under the
|
||||
`),He=s("code"),He.textContent="expand",ze=_(" property (eg. "),Re=s("code"),Re.textContent='"expand": {"relField1": {...}, ...}',Ke=_(`).
|
||||
`),Ye=s("br"),xe=_(`
|
||||
Only the relations to which the request user has permissions to `),Ae=s("strong"),Ae.textContent="view",Ge=_(" will be expanded."),Xe=p(),$e(ce.$$.fragment),Be=p(),be=s("div"),be.textContent="Responses",je=p(),ae=s("div"),_e=s("div");for(let n=0;n<Q.length;n+=1)Q[n].c();et=p(),ke=s("div");for(let n=0;n<N.length;n+=1)N[n].c();w(e,"class","m-b-sm"),w(b,"class","content txt-lg m-b-sm"),w(P,"class","m-b-xs"),w(F,"class","label label-primary"),w(K,"class","content"),w(k,"class","alert alert-success"),w(X,"class","section-title"),w(L,"class","table-compact table-border m-b-base"),w(pe,"class","section-title"),w(ne,"class","table-compact table-border m-b-base"),w(be,"class","section-title"),w(_e,"class","tabs-header compact combined left"),w(ke,"class","tabs-content"),w(ae,"class","tabs")},m(n,o){r(n,e,o),i(e,t),i(e,u),i(e,c),r(n,f,o),r(n,b,o),i(b,m),i(m,$),i(m,h),i(h,B),i(m,S),i(b,O),i(b,R),i(b,M),i(b,U),r(n,J,o),qe(T,n,o),r(n,W,o),r(n,P,o),r(n,q,o),r(n,k,o),i(k,F),i(k,te),i(k,K),i(K,I),i(I,re),i(I,Y),i(Y,G),i(I,fe),i(k,le),D&&D.m(k,null),r(n,E,o),r(n,X,o),r(n,Z,o),r(n,L,o),i(L,y),i(L,C),i(L,V),j&&j.m(V,null),i(V,z);for(let v=0;v<H.length;v+=1)H[v]&&H[v].m(V,null);r(n,Oe,o),r(n,pe,o),r(n,Me,o),r(n,ne,o),i(ne,Pe),i(ne,De),i(ne,me),i(me,ie),i(ie,Fe),i(ie,Je),i(ie,Le),i(ie,Ee),i(ie,A),i(A,Ue),qe(de,A,null),i(A,Ie),i(A,Qe),i(A,We),i(A,He),i(A,ze),i(A,Re),i(A,Ke),i(A,Ye),i(A,xe),i(A,Ae),i(A,Ge),i(me,Xe),qe(ce,me,null),r(n,Be,o),r(n,be,o),r(n,je,o),r(n,ae,o),i(ae,_e);for(let v=0;v<Q.length;v+=1)Q[v]&&Q[v].m(_e,null);i(ae,et),i(ae,ke);for(let v=0;v<N.length;v+=1)N[v]&&N[v].m(ke,null);se=!0},p(n,[o]){var dt,ct,ut,ft;(!se||o&1)&&l!==(l=n[0].name+"")&&oe(u,l),(!se||o&1)&&g!==(g=n[0].name+"")&&oe(B,g);const v={};o&51&&(v.js=`
|
||||
`),Le=s("code"),Le.textContent="expand",We=_(" property (eg. "),Re=s("code"),Re.textContent='"expand": {"relField1": {...}, ...}',Ye=_(`).
|
||||
`),ze=s("br"),Ge=_(`
|
||||
Only the relations to which the request user has permissions to `),je=s("strong"),je.textContent="view",Xe=_(" will be expanded."),Ze=p(),$e(ce.$$.fragment),Ae=p(),be=s("div"),be.textContent="Responses",Be=p(),ae=s("div"),_e=s("div");for(let n=0;n<K.length;n+=1)K[n].c();et=p(),ke=s("div");for(let n=0;n<D.length;n+=1)D[n].c();w(e,"class","m-b-sm"),w(b,"class","content txt-lg m-b-sm"),w(P,"class","m-b-xs"),w(F,"class","label label-primary"),w(Y,"class","content"),w(k,"class","alert alert-success"),w(Z,"class","section-title"),w(H,"class","table-compact table-border m-b-base"),w(pe,"class","section-title"),w(ne,"class","table-compact table-border m-b-base"),w(be,"class","section-title"),w(_e,"class","tabs-header compact combined left"),w(ke,"class","tabs-content"),w(ae,"class","tabs")},m(n,o){r(n,e,o),i(e,t),i(e,u),i(e,c),r(n,f,o),r(n,b,o),i(b,m),i(m,$),i(m,h),i(h,A),i(m,T),i(b,O),i(b,R),i(b,M),i(b,U),r(n,J,o),qe(S,n,o),r(n,Q,o),r(n,P,o),r(n,q,o),r(n,k,o),i(k,F),i(k,te),i(k,Y),i(Y,I),i(I,re),i(I,z),i(z,X),i(I,fe),i(k,le),N&&N.m(k,null),r(n,E,o),r(n,Z,o),r(n,x,o),r(n,H,o),i(H,y),i(H,C),i(H,V),B&&B.m(V,null),i(V,W);for(let v=0;v<L.length;v+=1)L[v]&&L[v].m(V,null);r(n,Oe,o),r(n,pe,o),r(n,Me,o),r(n,ne,o),i(ne,Pe),i(ne,Ne),i(ne,me),i(me,ie),i(ie,Fe),i(ie,Je),i(ie,He),i(ie,Ee),i(ie,j),i(j,Ue),qe(de,j,null),i(j,Ie),i(j,Ke),i(j,Qe),i(j,Le),i(j,We),i(j,Re),i(j,Ye),i(j,ze),i(j,Ge),i(j,je),i(j,Xe),i(me,Ze),qe(ce,me,null),r(n,Ae,o),r(n,be,o),r(n,Be,o),r(n,ae,o),i(ae,_e);for(let v=0;v<K.length;v+=1)K[v]&&K[v].m(_e,null);i(ae,et),i(ae,ke);for(let v=0;v<D.length;v+=1)D[v]&&D[v].m(ke,null);se=!0},p(n,[o]){var dt,ct,ut,ft;(!se||o&1)&&l!==(l=n[0].name+"")&&oe(u,l),(!se||o&1)&&g!==(g=n[0].name+"")&&oe(A,g);const v={};o&51&&(v.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[5]}');
|
||||
@@ -70,7 +70,7 @@ final record = await pb.collection('${(ut=n[0])==null?void 0:ut.name}').create(b
|
||||
`+(n[1]?`
|
||||
// (optional) send an email verification request
|
||||
await pb.collection('${(ft=n[0])==null?void 0:ft.name}').requestVerification('test@example.com');
|
||||
`:"")),T.$set(v),(!se||o&1)&&x!==(x=n[0].name+"")&&oe(G,x),n[7]?D||(D=kt(),D.c(),D.m(k,null)):D&&(D.d(1),D=null),n[1]?j?j.p(n,o):(j=ht(n),j.c(),j.m(V,z)):j&&(j.d(1),j=null),o&64&&(ge=ue(n[6]),H=Ve(H,o,lt,1,n,ge,Ne,V,pt,vt,null,_t)),o&12&&(Ce=ue(n[3]),Q=Ve(Q,o,nt,1,n,Ce,Ze,_e,pt,wt,null,bt)),o&12&&(he=ue(n[3]),Ot(),N=Ve(N,o,it,1,n,he,tt,ke,Mt,gt,null,mt),Pt())},i(n){if(!se){ye(T.$$.fragment,n),ye(de.$$.fragment,n),ye(ce.$$.fragment,n);for(let o=0;o<he.length;o+=1)ye(N[o]);se=!0}},o(n){ve(T.$$.fragment,n),ve(de.$$.fragment,n),ve(ce.$$.fragment,n);for(let o=0;o<N.length;o+=1)ve(N[o]);se=!1},d(n){n&&(d(e),d(f),d(b),d(J),d(W),d(P),d(q),d(k),d(E),d(X),d(Z),d(L),d(Oe),d(pe),d(Me),d(ne),d(Be),d(be),d(je),d(ae)),Te(T,n),D&&D.d(),j&&j.d();for(let o=0;o<H.length;o+=1)H[o].d();Te(de),Te(ce);for(let o=0;o<Q.length;o+=1)Q[o].d();for(let o=0;o<N.length;o+=1)N[o].d()}}}const Yt=a=>a.name=="emailVisibility",xt=a=>a.name=="email";function Gt(a,e,t){let l,u,c,f,b,{collection:m}=e,$=200,h=[],g={};const B=S=>t(2,$=S.code);return a.$$set=S=>{"collection"in S&&t(0,m=S.collection)},a.$$.update=()=>{var S,O,R;a.$$.dirty&1&&t(1,l=m.type==="auth"),a.$$.dirty&1&&t(7,u=(m==null?void 0:m.createRule)===null),a.$$.dirty&2&&t(8,c=l?["password","verified","email","emailVisibility"]:[]),a.$$.dirty&257&&t(6,f=((S=m==null?void 0:m.fields)==null?void 0:S.filter(M=>!M.hidden&&M.type!="autodate"&&!c.includes(M.name)))||[]),a.$$.dirty&1&&t(3,h=[{code:200,body:JSON.stringify(ee.dummyCollectionRecord(m),null,2)},{code:400,body:`
|
||||
`:"")),S.$set(v),(!se||o&1)&&G!==(G=n[0].name+"")&&oe(X,G),n[7]?N||(N=kt(),N.c(),N.m(k,null)):N&&(N.d(1),N=null),n[1]?B?B.p(n,o):(B=ht(n),B.c(),B.m(V,W)):B&&(B.d(1),B=null),o&64&&(ge=ue(n[6]),L=Ve(L,o,lt,1,n,ge,De,V,pt,vt,null,_t)),o&12&&(Ce=ue(n[3]),K=Ve(K,o,nt,1,n,Ce,xe,_e,pt,wt,null,bt)),o&12&&(he=ue(n[3]),Ot(),D=Ve(D,o,it,1,n,he,tt,ke,Mt,gt,null,mt),Pt())},i(n){if(!se){ye(S.$$.fragment,n),ye(de.$$.fragment,n),ye(ce.$$.fragment,n);for(let o=0;o<he.length;o+=1)ye(D[o]);se=!0}},o(n){ve(S.$$.fragment,n),ve(de.$$.fragment,n),ve(ce.$$.fragment,n);for(let o=0;o<D.length;o+=1)ve(D[o]);se=!1},d(n){n&&(d(e),d(f),d(b),d(J),d(Q),d(P),d(q),d(k),d(E),d(Z),d(x),d(H),d(Oe),d(pe),d(Me),d(ne),d(Ae),d(be),d(Be),d(ae)),Se(S,n),N&&N.d(),B&&B.d();for(let o=0;o<L.length;o+=1)L[o].d();Se(de),Se(ce);for(let o=0;o<K.length;o+=1)K[o].d();for(let o=0;o<D.length;o+=1)D[o].d()}}}const zt=a=>a.name=="emailVisibility",Gt=a=>a.name=="email";function Xt(a,e,t){let l,u,c,f,b,{collection:m}=e,$=200,h=[],g={};const A=T=>t(2,$=T.code);return a.$$set=T=>{"collection"in T&&t(0,m=T.collection)},a.$$.update=()=>{var T,O,R;a.$$.dirty&1&&t(1,l=m.type==="auth"),a.$$.dirty&1&&t(7,u=(m==null?void 0:m.createRule)===null),a.$$.dirty&2&&t(8,c=l?["password","verified","email","emailVisibility"]:[]),a.$$.dirty&257&&t(6,f=((T=m==null?void 0:m.fields)==null?void 0:T.filter(M=>!M.hidden&&M.type!="autodate"&&!c.includes(M.name)))||[]),a.$$.dirty&1&&t(3,h=[{code:200,body:JSON.stringify(ee.dummyCollectionRecord(m),null,2)},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "Failed to create record.",
|
||||
@@ -87,4 +87,4 @@ await pb.collection('${(ft=n[0])==null?void 0:ft.name}').requestVerification('te
|
||||
"message": "You are not allowed to perform this request.",
|
||||
"data": {}
|
||||
}
|
||||
`}]),a.$$.dirty&2&&(l?t(4,g={password:"12345678",passwordConfirm:"12345678"}):t(4,g={}))},t(5,b=ee.getApiExampleUrl(Ft.baseURL)),[m,l,$,h,g,b,f,u,c,B]}class el extends $t{constructor(e){super(),qt(this,e,Gt,Kt,Tt,{collection:0})}}export{el as default};
|
||||
`}]),a.$$.dirty&2&&(l?t(4,g={password:"12345678",passwordConfirm:"12345678"}):t(4,g={}))},t(5,b=ee.getApiExampleUrl(Ft.baseURL)),[m,l,$,h,g,b,f,u,c,A]}class el extends $t{constructor(e){super(),qt(this,e,Xt,Yt,St,{collection:0})}}export{el as default};
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import{S as Re,i as Ee,s as Pe,U as Te,W as H,f as c,y,h as k,c as $e,j as m,l as p,n as i,m as Ce,G as ee,X as he,Y as Be,D as Ie,Z as Oe,E as Ae,t as te,a as le,u as f,d as we,I as Me,p as qe,k as z,o as Le,V as Se}from"./index-0yJTQg7M.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 superuser <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,o){p(s,l,o)},d(s){s&&f(l)}}}function ye(a,l){let s,o,h;function r(){return l[5](l[6])}return{key:a,first:null,c(){s=c("button"),s.textContent=`${l[6].code} `,m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(n,d){p(n,s,d),o||(h=Le(s,"click",r),o=!0)},p(n,d){l=n,d&20&&z(s,"active",l[2]===l[6].code)},d(n){n&&f(s),o=!1,h()}}}function De(a,l){let s,o,h,r;return o=new Se({props:{content:l[6].body}}),{key:a,first:null,c(){s=c("div"),$e(o.$$.fragment),h=k(),m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(n,d){p(n,s,d),Ce(o,s,null),i(s,h),r=!0},p(n,d){l=n,(!r||d&20)&&z(s,"active",l[2]===l[6].code)},i(n){r||(te(o.$$.fragment,n),r=!0)},o(n){le(o.$$.fragment,n),r=!1},d(n){n&&f(s),we(o)}}}function Ue(a){var fe,me;let l,s,o=a[0].name+"",h,r,n,d,D,$,F,q=a[0].name+"",G,se,K,C,N,P,V,g,L,ae,S,E,oe,W,U=a[0].name+"",X,ne,Y,ie,Z,T,J,B,Q,I,x,w,O,v=[],ce=new Map,re,A,b=[],de=new Map,R;C=new Te({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=a[0])==null?void 0:fe.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(),j=H(a[4]);const ue=e=>e[6].code;for(let e=0;e<j.length;e+=1){let t=ge(a,j,e),u=ue(t);ce.set(u,v[e]=ye(u,t))}let M=H(a[4]);const pe=e=>e[6].code;for(let e=0;e<M.length;e+=1){let t=ke(a,M,e),u=pe(t);de.set(u,b[e]=De(u,t))}return{c(){l=c("h3"),s=y("Delete ("),h=y(o),r=y(")"),n=k(),d=c("div"),D=c("p"),$=y("Delete a single "),F=c("strong"),G=y(q),se=y(" record."),K=k(),$e(C.$$.fragment),N=k(),P=c("h6"),P.textContent="API details",V=k(),g=c("div"),L=c("strong"),L.textContent="DELETE",ae=k(),S=c("div"),E=c("p"),oe=y("/api/collections/"),W=c("strong"),X=y(U),ne=y("/records/"),Y=c("strong"),Y.textContent=":id",ie=k(),_&&_.c(),Z=k(),T=c("div"),T.textContent="Path parameters",J=k(),B=c("table"),B.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>',Q=k(),I=c("div"),I.textContent="Responses",x=k(),w=c("div"),O=c("div");for(let e=0;e<v.length;e+=1)v[e].c();re=k(),A=c("div");for(let e=0;e<b.length;e+=1)b[e].c();m(l,"class","m-b-sm"),m(d,"class","content txt-lg m-b-sm"),m(P,"class","m-b-xs"),m(L,"class","label label-primary"),m(S,"class","content"),m(g,"class","alert alert-danger"),m(T,"class","section-title"),m(B,"class","table-compact table-border m-b-base"),m(I,"class","section-title"),m(O,"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,r),p(e,n,t),p(e,d,t),i(d,D),i(D,$),i(D,F),i(F,G),i(D,se),p(e,K,t),Ce(C,e,t),p(e,N,t),p(e,P,t),p(e,V,t),p(e,g,t),i(g,L),i(g,ae),i(g,S),i(S,E),i(E,oe),i(E,W),i(W,X),i(E,ne),i(E,Y),i(g,ie),_&&_.m(g,null),p(e,Z,t),p(e,T,t),p(e,J,t),p(e,B,t),p(e,Q,t),p(e,I,t),p(e,x,t),p(e,w,t),i(w,O);for(let u=0;u<v.length;u+=1)v[u]&&v[u].m(O,null);i(w,re),i(w,A);for(let u=0;u<b.length;u+=1)b[u]&&b[u].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)&&q!==(q=e[0].name+"")&&ee(G,q);const u={};t&9&&(u.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&&(u.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(u),(!R||t&1)&&U!==(U=e[0].name+"")&&ee(X,U),e[1]?_||(_=ve(),_.c(),_.m(g,null)):_&&(_.d(1),_=null),t&20&&(j=H(e[4]),v=he(v,t,ue,1,e,j,ce,O,Be,ye,null,ge)),t&20&&(M=H(e[4]),Ie(),b=he(b,t,pe,1,e,M,de,A,Oe,De,null,ke),Ae())},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&&(f(l),f(n),f(d),f(K),f(N),f(P),f(V),f(g),f(Z),f(T),f(J),f(B),f(Q),f(I),f(x),f(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 je(a,l,s){let o,h,{collection:r}=l,n=204,d=[];const D=$=>s(2,n=$.code);return a.$$set=$=>{"collection"in $&&s(0,r=$.collection)},a.$$.update=()=>{a.$$.dirty&1&&s(1,o=(r==null?void 0:r.deleteRule)===null),a.$$.dirty&3&&r!=null&&r.id&&(d.push({code:204,body:`
|
||||
null
|
||||
`}),d.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&&d.push({code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Only superusers can access this action.",
|
||||
"data": {}
|
||||
}
|
||||
`}),d.push({code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},s(3,h=Me.getApiExampleUrl(qe.baseURL)),[r,o,n,h,d,D]}class ze extends Re{constructor(l){super(),Ee(this,l,je,Ue,Pe,{collection:0})}}export{ze as default};
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
import{S as Re,i as Ee,s as Pe,Q as Te,T 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,U as he,V as Be,k as Oe,W as Ie,n as Ae,t as te,a as le,o as f,d as we,C as Me,p as qe,r as z,u as Le,R as Se}from"./index-Co7n6kj7.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 superuser <code>Authorization:TOKEN</code> header",m(l,"class","txt-hint txt-sm txt-right")},m(s,o){p(s,l,o)},d(s){s&&f(l)}}}function ye(a,l){let s,o,h;function r(){return l[5](l[6])}return{key:a,first:null,c(){s=c("button"),s.textContent=`${l[6].code} `,m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(n,d){p(n,s,d),o||(h=Le(s,"click",r),o=!0)},p(n,d){l=n,d&20&&z(s,"active",l[2]===l[6].code)},d(n){n&&f(s),o=!1,h()}}}function $e(a,l){let s,o,h,r;return o=new Se({props:{content:l[6].body}}),{key:a,first:null,c(){s=c("div"),De(o.$$.fragment),h=k(),m(s,"class","tab-item"),z(s,"active",l[2]===l[6].code),this.first=s},m(n,d){p(n,s,d),Ce(o,s,null),i(s,h),r=!0},p(n,d){l=n,(!r||d&20)&&z(s,"active",l[2]===l[6].code)},i(n){r||(te(o.$$.fragment,n),r=!0)},o(n){le(o.$$.fragment,n),r=!1},d(n){n&&f(s),we(o)}}}function Ue(a){var fe,me;let l,s,o=a[0].name+"",h,r,n,d,$,D,F,q=a[0].name+"",K,se,N,C,Q,P,V,g,L,ae,S,E,oe,W,U=a[0].name+"",G,ne,J,ie,X,T,Y,B,Z,O,x,w,I,v=[],ce=new Map,re,A,b=[],de=new Map,R;C=new Te({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${a[3]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(fe=a[0])==null?void 0:fe.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(),H=j(a[4]);const ue=e=>e[6].code;for(let e=0;e<H.length;e+=1){let t=ge(a,H,e),u=ue(t);ce.set(u,v[e]=ye(u,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),u=pe(t);de.set(u,b[e]=$e(u,t))}return{c(){l=c("h3"),s=y("Delete ("),h=y(o),r=y(")"),n=k(),d=c("div"),$=c("p"),D=y("Delete a single "),F=c("strong"),K=y(q),se=y(" record."),N=k(),De(C.$$.fragment),Q=k(),P=c("h6"),P.textContent="API details",V=k(),g=c("div"),L=c("strong"),L.textContent="DELETE",ae=k(),S=c("div"),E=c("p"),oe=y("/api/collections/"),W=c("strong"),G=y(U),ne=y("/records/"),J=c("strong"),J.textContent=":id",ie=k(),_&&_.c(),X=k(),T=c("div"),T.textContent="Path parameters",Y=k(),B=c("table"),B.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(),O=c("div"),O.textContent="Responses",x=k(),w=c("div"),I=c("div");for(let e=0;e<v.length;e+=1)v[e].c();re=k(),A=c("div");for(let e=0;e<b.length;e+=1)b[e].c();m(l,"class","m-b-sm"),m(d,"class","content txt-lg m-b-sm"),m(P,"class","m-b-xs"),m(L,"class","label label-primary"),m(S,"class","content"),m(g,"class","alert alert-danger"),m(T,"class","section-title"),m(B,"class","table-compact table-border m-b-base"),m(O,"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,r),p(e,n,t),p(e,d,t),i(d,$),i($,D),i($,F),i(F,K),i($,se),p(e,N,t),Ce(C,e,t),p(e,Q,t),p(e,P,t),p(e,V,t),p(e,g,t),i(g,L),i(g,ae),i(g,S),i(S,E),i(E,oe),i(E,W),i(W,G),i(E,ne),i(E,J),i(g,ie),_&&_.m(g,null),p(e,X,t),p(e,T,t),p(e,Y,t),p(e,B,t),p(e,Z,t),p(e,O,t),p(e,x,t),p(e,w,t),i(w,I);for(let u=0;u<v.length;u+=1)v[u]&&v[u].m(I,null);i(w,re),i(w,A);for(let u=0;u<b.length;u+=1)b[u]&&b[u].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)&&q!==(q=e[0].name+"")&&ee(K,q);const u={};t&9&&(u.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&&(u.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(u),(!R||t&1)&&U!==(U=e[0].name+"")&&ee(G,U),e[1]?_||(_=ve(),_.c(),_.m(g,null)):_&&(_.d(1),_=null),t&20&&(H=j(e[4]),v=he(v,t,ue,1,e,H,ce,I,Be,ye,null,ge)),t&20&&(M=j(e[4]),Oe(),b=he(b,t,pe,1,e,M,de,A,Ie,$e,null,ke),Ae())},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&&(f(l),f(n),f(d),f(N),f(Q),f(P),f(V),f(g),f(X),f(T),f(Y),f(B),f(Z),f(O),f(x),f(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 He(a,l,s){let o,h,{collection:r}=l,n=204,d=[];const $=D=>s(2,n=D.code);return a.$$set=D=>{"collection"in D&&s(0,r=D.collection)},a.$$.update=()=>{a.$$.dirty&1&&s(1,o=(r==null?void 0:r.deleteRule)===null),a.$$.dirty&3&&r!=null&&r.id&&(d.push({code:204,body:`
|
||||
null
|
||||
`}),d.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&&d.push({code:403,body:`
|
||||
{
|
||||
"code": 403,
|
||||
"message": "Only superusers can access this action.",
|
||||
"data": {}
|
||||
}
|
||||
`}),d.push({code:404,body:`
|
||||
{
|
||||
"code": 404,
|
||||
"message": "The requested resource wasn't found.",
|
||||
"data": {}
|
||||
}
|
||||
`}))},s(3,h=Me.getApiExampleUrl(qe.baseURL)),[r,o,n,h,d,$]}class ze extends Re{constructor(l){super(),Ee(this,l,He,Ue,Pe,{collection:0})}}export{ze as default};
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
import{S as se,i as oe,s as ie,W as G,f as p,h as C,y as U,j as b,l as g,n as u,G as J,X as le,Y as Re,D as ne,Z as Se,E as ae,t as X,a as Z,u as v,k as K,o as ce,V as Oe,c as x,m as ee,d as te,U as Me,_ as _e,I as Be,p as De,$ as be}from"./index-0yJTQg7M.js";function ge(n,e,t){const l=n.slice();return l[4]=e[t],l}function ve(n,e,t){const l=n.slice();return l[4]=e[t],l}function ke(n,e){let t,l=e[4].code+"",d,i,r,a;function m(){return e[3](e[4])}return{key:n,first:null,c(){t=p("button"),d=U(l),i=C(),b(t,"class","tab-item"),K(t,"active",e[1]===e[4].code),this.first=t},m(k,A){g(k,t,A),u(t,d),u(t,i),r||(a=ce(t,"click",m),r=!0)},p(k,A){e=k,A&4&&l!==(l=e[4].code+"")&&J(d,l),A&6&&K(t,"active",e[1]===e[4].code)},d(k){k&&v(t),r=!1,a()}}}function $e(n,e){let t,l,d,i;return l=new Oe({props:{content:e[4].body}}),{key:n,first:null,c(){t=p("div"),x(l.$$.fragment),d=C(),b(t,"class","tab-item"),K(t,"active",e[1]===e[4].code),this.first=t},m(r,a){g(r,t,a),ee(l,t,null),u(t,d),i=!0},p(r,a){e=r;const m={};a&4&&(m.content=e[4].body),l.$set(m),(!i||a&6)&&K(t,"active",e[1]===e[4].code)},i(r){i||(X(l.$$.fragment,r),i=!0)},o(r){Z(l.$$.fragment,r),i=!1},d(r){r&&v(t),te(l)}}}function Ne(n){let e,t,l,d,i,r,a,m=n[0].name+"",k,A,Y,H,V,L,j,B,D,S,N,T=[],O=new Map,P,z,q=[],W=new Map,w,E=G(n[2]);const M=c=>c[4].code;for(let c=0;c<E.length;c+=1){let f=ve(n,E,c),s=M(f);O.set(s,T[c]=ke(s,f))}let _=G(n[2]);const F=c=>c[4].code;for(let c=0;c<_.length;c+=1){let f=ge(n,_,c),s=F(f);W.set(s,q[c]=$e(s,f))}return{c(){e=p("div"),t=p("strong"),t.textContent="POST",l=C(),d=p("div"),i=p("p"),r=U("/api/collections/"),a=p("strong"),k=U(m),A=U("/confirm-email-change"),Y=C(),H=p("div"),H.textContent="Body Parameters",V=C(),L=p("table"),L.innerHTML='<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>token</span></div></td> <td><span class="label">String</span></td> <td>The token from the change email request email.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>The account password to confirm the email change.</td></tr></tbody>',j=C(),B=p("div"),B.textContent="Responses",D=C(),S=p("div"),N=p("div");for(let c=0;c<T.length;c+=1)T[c].c();P=C(),z=p("div");for(let c=0;c<q.length;c+=1)q[c].c();b(t,"class","label label-primary"),b(d,"class","content"),b(e,"class","alert alert-success"),b(H,"class","section-title"),b(L,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(N,"class","tabs-header compact combined left"),b(z,"class","tabs-content"),b(S,"class","tabs")},m(c,f){g(c,e,f),u(e,t),u(e,l),u(e,d),u(d,i),u(i,r),u(i,a),u(a,k),u(i,A),g(c,Y,f),g(c,H,f),g(c,V,f),g(c,L,f),g(c,j,f),g(c,B,f),g(c,D,f),g(c,S,f),u(S,N);for(let s=0;s<T.length;s+=1)T[s]&&T[s].m(N,null);u(S,P),u(S,z);for(let s=0;s<q.length;s+=1)q[s]&&q[s].m(z,null);w=!0},p(c,[f]){(!w||f&1)&&m!==(m=c[0].name+"")&&J(k,m),f&6&&(E=G(c[2]),T=le(T,f,M,1,c,E,O,N,Re,ke,null,ve)),f&6&&(_=G(c[2]),ne(),q=le(q,f,F,1,c,_,W,z,Se,$e,null,ge),ae())},i(c){if(!w){for(let f=0;f<_.length;f+=1)X(q[f]);w=!0}},o(c){for(let f=0;f<q.length;f+=1)Z(q[f]);w=!1},d(c){c&&(v(e),v(Y),v(H),v(V),v(L),v(j),v(B),v(D),v(S));for(let f=0;f<T.length;f+=1)T[f].d();for(let f=0;f<q.length;f+=1)q[f].d()}}}function We(n,e,t){let{collection:l}=e,d=204,i=[];const r=a=>t(1,d=a.code);return n.$$set=a=>{"collection"in a&&t(0,l=a.collection)},t(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[l,d,i,r]}class He extends se{constructor(e){super(),oe(this,e,We,Ne,ie,{collection:0})}}function we(n,e,t){const l=n.slice();return l[4]=e[t],l}function ye(n,e,t){const l=n.slice();return l[4]=e[t],l}function Ce(n,e){let t,l=e[4].code+"",d,i,r,a;function m(){return e[3](e[4])}return{key:n,first:null,c(){t=p("button"),d=U(l),i=C(),b(t,"class","tab-item"),K(t,"active",e[1]===e[4].code),this.first=t},m(k,A){g(k,t,A),u(t,d),u(t,i),r||(a=ce(t,"click",m),r=!0)},p(k,A){e=k,A&4&&l!==(l=e[4].code+"")&&J(d,l),A&6&&K(t,"active",e[1]===e[4].code)},d(k){k&&v(t),r=!1,a()}}}function Ee(n,e){let t,l,d,i;return l=new Oe({props:{content:e[4].body}}),{key:n,first:null,c(){t=p("div"),x(l.$$.fragment),d=C(),b(t,"class","tab-item"),K(t,"active",e[1]===e[4].code),this.first=t},m(r,a){g(r,t,a),ee(l,t,null),u(t,d),i=!0},p(r,a){e=r;const m={};a&4&&(m.content=e[4].body),l.$set(m),(!i||a&6)&&K(t,"active",e[1]===e[4].code)},i(r){i||(X(l.$$.fragment,r),i=!0)},o(r){Z(l.$$.fragment,r),i=!1},d(r){r&&v(t),te(l)}}}function Le(n){let e,t,l,d,i,r,a,m=n[0].name+"",k,A,Y,H,V,L,j,B,D,S,N,T,O,P=[],z=new Map,q,W,w=[],E=new Map,M,_=G(n[2]);const F=s=>s[4].code;for(let s=0;s<_.length;s+=1){let h=ye(n,_,s),R=F(h);z.set(R,P[s]=Ce(R,h))}let c=G(n[2]);const f=s=>s[4].code;for(let s=0;s<c.length;s+=1){let h=we(n,c,s),R=f(h);E.set(R,w[s]=Ee(R,h))}return{c(){e=p("div"),t=p("strong"),t.textContent="POST",l=C(),d=p("div"),i=p("p"),r=U("/api/collections/"),a=p("strong"),k=U(m),A=U("/request-email-change"),Y=C(),H=p("p"),H.innerHTML="Requires <code>Authorization:TOKEN</code>",V=C(),L=p("div"),L.textContent="Body Parameters",j=C(),B=p("table"),B.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>newEmail</span></div></td> <td><span class="label">String</span></td> <td>The new email address to send the change email request.</td></tr></tbody>',D=C(),S=p("div"),S.textContent="Responses",N=C(),T=p("div"),O=p("div");for(let s=0;s<P.length;s+=1)P[s].c();q=C(),W=p("div");for(let s=0;s<w.length;s+=1)w[s].c();b(t,"class","label label-primary"),b(d,"class","content"),b(H,"class","txt-hint txt-sm txt-right"),b(e,"class","alert alert-success"),b(L,"class","section-title"),b(B,"class","table-compact table-border m-b-base"),b(S,"class","section-title"),b(O,"class","tabs-header compact combined left"),b(W,"class","tabs-content"),b(T,"class","tabs")},m(s,h){g(s,e,h),u(e,t),u(e,l),u(e,d),u(d,i),u(i,r),u(i,a),u(a,k),u(i,A),u(e,Y),u(e,H),g(s,V,h),g(s,L,h),g(s,j,h),g(s,B,h),g(s,D,h),g(s,S,h),g(s,N,h),g(s,T,h),u(T,O);for(let R=0;R<P.length;R+=1)P[R]&&P[R].m(O,null);u(T,q),u(T,W);for(let R=0;R<w.length;R+=1)w[R]&&w[R].m(W,null);M=!0},p(s,[h]){(!M||h&1)&&m!==(m=s[0].name+"")&&J(k,m),h&6&&(_=G(s[2]),P=le(P,h,F,1,s,_,z,O,Re,Ce,null,ye)),h&6&&(c=G(s[2]),ne(),w=le(w,h,f,1,s,c,E,W,Se,Ee,null,we),ae())},i(s){if(!M){for(let h=0;h<c.length;h+=1)X(w[h]);M=!0}},o(s){for(let h=0;h<w.length;h+=1)Z(w[h]);M=!1},d(s){s&&(v(e),v(V),v(L),v(j),v(B),v(D),v(S),v(N),v(T));for(let h=0;h<P.length;h+=1)P[h].d();for(let h=0;h<w.length;h+=1)w[h].d()}}}function Ue(n,e,t){let{collection:l}=e,d=204,i=[];const r=a=>t(1,d=a.code);return n.$$set=a=>{"collection"in a&&t(0,l=a.collection)},t(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"newEmail": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{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": {}
|
||||
}
|
||||
`}]),[l,d,i,r]}class Ie extends se{constructor(e){super(),oe(this,e,Ue,Le,ie,{collection:0})}}function Te(n,e,t){const l=n.slice();return l[5]=e[t],l[7]=t,l}function qe(n,e,t){const l=n.slice();return l[5]=e[t],l[7]=t,l}function Ae(n){let e,t,l,d,i;function r(){return n[4](n[7])}return{c(){e=p("button"),t=p("div"),t.textContent=`${n[5].title}`,l=C(),b(t,"class","txt"),b(e,"class","tab-item"),K(e,"active",n[1]==n[7])},m(a,m){g(a,e,m),u(e,t),u(e,l),d||(i=ce(e,"click",r),d=!0)},p(a,m){n=a,m&2&&K(e,"active",n[1]==n[7])},d(a){a&&v(e),d=!1,i()}}}function Pe(n){let e,t,l,d;var i=n[5].component;function r(a,m){return{props:{collection:a[0]}}}return i&&(t=be(i,r(n))),{c(){e=p("div"),t&&x(t.$$.fragment),l=C(),b(e,"class","tab-item"),K(e,"active",n[1]==n[7])},m(a,m){g(a,e,m),t&&ee(t,e,null),u(e,l),d=!0},p(a,m){if(i!==(i=a[5].component)){if(t){ne();const k=t;Z(k.$$.fragment,1,0,()=>{te(k,1)}),ae()}i?(t=be(i,r(a)),x(t.$$.fragment),X(t.$$.fragment,1),ee(t,e,l)):t=null}else if(i){const k={};m&1&&(k.collection=a[0]),t.$set(k)}(!d||m&2)&&K(e,"active",a[1]==a[7])},i(a){d||(t&&X(t.$$.fragment,a),d=!0)},o(a){t&&Z(t.$$.fragment,a),d=!1},d(a){a&&v(e),t&&te(t)}}}function Ge(n){var c,f,s,h,R,re;let e,t,l=n[0].name+"",d,i,r,a,m,k,A,Y=n[0].name+"",H,V,L,j,B,D,S,N,T,O,P,z,q,W;D=new Me({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(c=n[0])==null?void 0:c.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(f=n[0])==null?void 0:f.name}').requestEmailChange('new@example.com');
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(s=n[0])==null?void 0:s.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(h=n[0])==null?void 0:h.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(R=n[0])==null?void 0:R.name}').requestEmailChange('new@example.com');
|
||||
|
||||
...
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(re=n[0])==null?void 0:re.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`}});let w=G(n[3]),E=[];for(let o=0;o<w.length;o+=1)E[o]=Ae(qe(n,w,o));let M=G(n[3]),_=[];for(let o=0;o<M.length;o+=1)_[o]=Pe(Te(n,M,o));const F=o=>Z(_[o],1,1,()=>{_[o]=null});return{c(){e=p("h3"),t=U("Email change ("),d=U(l),i=U(")"),r=C(),a=p("div"),m=p("p"),k=U("Sends "),A=p("strong"),H=U(Y),V=U(" email change request."),L=C(),j=p("p"),j.textContent=`On successful email change all previously issued auth tokens for the specific record will be
|
||||
automatically invalidated.`,B=C(),x(D.$$.fragment),S=C(),N=p("h6"),N.textContent="API details",T=C(),O=p("div"),P=p("div");for(let o=0;o<E.length;o+=1)E[o].c();z=C(),q=p("div");for(let o=0;o<_.length;o+=1)_[o].c();b(e,"class","m-b-sm"),b(a,"class","content txt-lg m-b-sm"),b(N,"class","m-b-xs"),b(P,"class","tabs-header compact"),b(q,"class","tabs-content"),b(O,"class","tabs")},m(o,y){g(o,e,y),u(e,t),u(e,d),u(e,i),g(o,r,y),g(o,a,y),u(a,m),u(m,k),u(m,A),u(A,H),u(m,V),u(a,L),u(a,j),g(o,B,y),ee(D,o,y),g(o,S,y),g(o,N,y),g(o,T,y),g(o,O,y),u(O,P);for(let I=0;I<E.length;I+=1)E[I]&&E[I].m(P,null);u(O,z),u(O,q);for(let I=0;I<_.length;I+=1)_[I]&&_[I].m(q,null);W=!0},p(o,[y]){var de,ue,fe,me,he,pe;(!W||y&1)&&l!==(l=o[0].name+"")&&J(d,l),(!W||y&1)&&Y!==(Y=o[0].name+"")&&J(H,Y);const I={};if(y&5&&(I.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(de=o[0])==null?void 0:de.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(ue=o[0])==null?void 0:ue.name}').requestEmailChange('new@example.com');
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(fe=o[0])==null?void 0:fe.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),y&5&&(I.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=o[0])==null?void 0:me.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(he=o[0])==null?void 0:he.name}').requestEmailChange('new@example.com');
|
||||
|
||||
...
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(pe=o[0])==null?void 0:pe.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),D.$set(I),y&10){w=G(o[3]);let $;for($=0;$<w.length;$+=1){const Q=qe(o,w,$);E[$]?E[$].p(Q,y):(E[$]=Ae(Q),E[$].c(),E[$].m(P,null))}for(;$<E.length;$+=1)E[$].d(1);E.length=w.length}if(y&11){M=G(o[3]);let $;for($=0;$<M.length;$+=1){const Q=Te(o,M,$);_[$]?(_[$].p(Q,y),X(_[$],1)):(_[$]=Pe(Q),_[$].c(),X(_[$],1),_[$].m(q,null))}for(ne(),$=M.length;$<_.length;$+=1)F($);ae()}},i(o){if(!W){X(D.$$.fragment,o);for(let y=0;y<M.length;y+=1)X(_[y]);W=!0}},o(o){Z(D.$$.fragment,o),_=_.filter(Boolean);for(let y=0;y<_.length;y+=1)Z(_[y]);W=!1},d(o){o&&(v(e),v(r),v(a),v(B),v(S),v(N),v(T),v(O)),te(D,o),_e(E,o),_e(_,o)}}}function Ke(n,e,t){let l,{collection:d}=e;const i=[{title:"Request email change",component:Ie},{title:"Confirm email change",component:He}];let r=0;const a=m=>t(1,r=m);return n.$$set=m=>{"collection"in m&&t(0,d=m.collection)},t(2,l=Be.getApiExampleUrl(De.baseURL)),[d,r,l,i,a]}class je extends se{constructor(e){super(),oe(this,e,Ke,Ge,ie,{collection:0})}}export{je as default};
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
import{S as se,i as oe,s as ie,T as K,e as p,b as y,w as U,f as b,g,h as u,x as J,U as le,V as Re,k as ne,W as Se,n as ae,t as V,a as X,o as v,r as Y,u as ce,R as Oe,c as x,m as ee,d as te,Q as Me,X as _e,C as Be,p as De,Y as be}from"./index-Co7n6kj7.js";function ge(n,e,t){const l=n.slice();return l[4]=e[t],l}function ve(n,e,t){const l=n.slice();return l[4]=e[t],l}function ke(n,e){let t,l=e[4].code+"",d,i,r,a;function m(){return e[3](e[4])}return{key:n,first:null,c(){t=p("button"),d=U(l),i=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(k,A){g(k,t,A),u(t,d),u(t,i),r||(a=ce(t,"click",m),r=!0)},p(k,A){e=k,A&4&&l!==(l=e[4].code+"")&&J(d,l),A&6&&Y(t,"active",e[1]===e[4].code)},d(k){k&&v(t),r=!1,a()}}}function $e(n,e){let t,l,d,i;return l=new Oe({props:{content:e[4].body}}),{key:n,first:null,c(){t=p("div"),x(l.$$.fragment),d=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(r,a){g(r,t,a),ee(l,t,null),u(t,d),i=!0},p(r,a){e=r;const m={};a&4&&(m.content=e[4].body),l.$set(m),(!i||a&6)&&Y(t,"active",e[1]===e[4].code)},i(r){i||(V(l.$$.fragment,r),i=!0)},o(r){X(l.$$.fragment,r),i=!1},d(r){r&&v(t),te(l)}}}function Ne(n){let e,t,l,d,i,r,a,m=n[0].name+"",k,A,G,H,Q,L,z,B,D,S,N,T=[],O=new Map,P,j,q=[],W=new Map,w,E=K(n[2]);const M=c=>c[4].code;for(let c=0;c<E.length;c+=1){let f=ve(n,E,c),s=M(f);O.set(s,T[c]=ke(s,f))}let _=K(n[2]);const F=c=>c[4].code;for(let c=0;c<_.length;c+=1){let f=ge(n,_,c),s=F(f);W.set(s,q[c]=$e(s,f))}return{c(){e=p("div"),t=p("strong"),t.textContent="POST",l=y(),d=p("div"),i=p("p"),r=U("/api/collections/"),a=p("strong"),k=U(m),A=U("/confirm-email-change"),G=y(),H=p("div"),H.textContent="Body Parameters",Q=y(),L=p("table"),L.innerHTML='<thead><tr><th>Param</th> <th>Type</th> <th width="50%">Description</th></tr></thead> <tbody><tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>token</span></div></td> <td><span class="label">String</span></td> <td>The token from the change email request email.</td></tr> <tr><td><div class="inline-flex"><span class="label label-success">Required</span> <span>password</span></div></td> <td><span class="label">String</span></td> <td>The account password to confirm the email change.</td></tr></tbody>',z=y(),B=p("div"),B.textContent="Responses",D=y(),S=p("div"),N=p("div");for(let c=0;c<T.length;c+=1)T[c].c();P=y(),j=p("div");for(let c=0;c<q.length;c+=1)q[c].c();b(t,"class","label label-primary"),b(d,"class","content"),b(e,"class","alert alert-success"),b(H,"class","section-title"),b(L,"class","table-compact table-border m-b-base"),b(B,"class","section-title"),b(N,"class","tabs-header compact combined left"),b(j,"class","tabs-content"),b(S,"class","tabs")},m(c,f){g(c,e,f),u(e,t),u(e,l),u(e,d),u(d,i),u(i,r),u(i,a),u(a,k),u(i,A),g(c,G,f),g(c,H,f),g(c,Q,f),g(c,L,f),g(c,z,f),g(c,B,f),g(c,D,f),g(c,S,f),u(S,N);for(let s=0;s<T.length;s+=1)T[s]&&T[s].m(N,null);u(S,P),u(S,j);for(let s=0;s<q.length;s+=1)q[s]&&q[s].m(j,null);w=!0},p(c,[f]){(!w||f&1)&&m!==(m=c[0].name+"")&&J(k,m),f&6&&(E=K(c[2]),T=le(T,f,M,1,c,E,O,N,Re,ke,null,ve)),f&6&&(_=K(c[2]),ne(),q=le(q,f,F,1,c,_,W,j,Se,$e,null,ge),ae())},i(c){if(!w){for(let f=0;f<_.length;f+=1)V(q[f]);w=!0}},o(c){for(let f=0;f<q.length;f+=1)X(q[f]);w=!1},d(c){c&&(v(e),v(G),v(H),v(Q),v(L),v(z),v(B),v(D),v(S));for(let f=0;f<T.length;f+=1)T[f].d();for(let f=0;f<q.length;f+=1)q[f].d()}}}function We(n,e,t){let{collection:l}=e,d=204,i=[];const r=a=>t(1,d=a.code);return n.$$set=a=>{"collection"in a&&t(0,l=a.collection)},t(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"token": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`}]),[l,d,i,r]}class He extends se{constructor(e){super(),oe(this,e,We,Ne,ie,{collection:0})}}function we(n,e,t){const l=n.slice();return l[4]=e[t],l}function Ce(n,e,t){const l=n.slice();return l[4]=e[t],l}function ye(n,e){let t,l=e[4].code+"",d,i,r,a;function m(){return e[3](e[4])}return{key:n,first:null,c(){t=p("button"),d=U(l),i=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(k,A){g(k,t,A),u(t,d),u(t,i),r||(a=ce(t,"click",m),r=!0)},p(k,A){e=k,A&4&&l!==(l=e[4].code+"")&&J(d,l),A&6&&Y(t,"active",e[1]===e[4].code)},d(k){k&&v(t),r=!1,a()}}}function Ee(n,e){let t,l,d,i;return l=new Oe({props:{content:e[4].body}}),{key:n,first:null,c(){t=p("div"),x(l.$$.fragment),d=y(),b(t,"class","tab-item"),Y(t,"active",e[1]===e[4].code),this.first=t},m(r,a){g(r,t,a),ee(l,t,null),u(t,d),i=!0},p(r,a){e=r;const m={};a&4&&(m.content=e[4].body),l.$set(m),(!i||a&6)&&Y(t,"active",e[1]===e[4].code)},i(r){i||(V(l.$$.fragment,r),i=!0)},o(r){X(l.$$.fragment,r),i=!1},d(r){r&&v(t),te(l)}}}function Le(n){let e,t,l,d,i,r,a,m=n[0].name+"",k,A,G,H,Q,L,z,B,D,S,N,T,O,P=[],j=new Map,q,W,w=[],E=new Map,M,_=K(n[2]);const F=s=>s[4].code;for(let s=0;s<_.length;s+=1){let h=Ce(n,_,s),R=F(h);j.set(R,P[s]=ye(R,h))}let c=K(n[2]);const f=s=>s[4].code;for(let s=0;s<c.length;s+=1){let h=we(n,c,s),R=f(h);E.set(R,w[s]=Ee(R,h))}return{c(){e=p("div"),t=p("strong"),t.textContent="POST",l=y(),d=p("div"),i=p("p"),r=U("/api/collections/"),a=p("strong"),k=U(m),A=U("/request-email-change"),G=y(),H=p("p"),H.innerHTML="Requires <code>Authorization:TOKEN</code>",Q=y(),L=p("div"),L.textContent="Body Parameters",z=y(),B=p("table"),B.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>newEmail</span></div></td> <td><span class="label">String</span></td> <td>The new email address to send the change email request.</td></tr></tbody>',D=y(),S=p("div"),S.textContent="Responses",N=y(),T=p("div"),O=p("div");for(let s=0;s<P.length;s+=1)P[s].c();q=y(),W=p("div");for(let s=0;s<w.length;s+=1)w[s].c();b(t,"class","label label-primary"),b(d,"class","content"),b(H,"class","txt-hint txt-sm txt-right"),b(e,"class","alert alert-success"),b(L,"class","section-title"),b(B,"class","table-compact table-border m-b-base"),b(S,"class","section-title"),b(O,"class","tabs-header compact combined left"),b(W,"class","tabs-content"),b(T,"class","tabs")},m(s,h){g(s,e,h),u(e,t),u(e,l),u(e,d),u(d,i),u(i,r),u(i,a),u(a,k),u(i,A),u(e,G),u(e,H),g(s,Q,h),g(s,L,h),g(s,z,h),g(s,B,h),g(s,D,h),g(s,S,h),g(s,N,h),g(s,T,h),u(T,O);for(let R=0;R<P.length;R+=1)P[R]&&P[R].m(O,null);u(T,q),u(T,W);for(let R=0;R<w.length;R+=1)w[R]&&w[R].m(W,null);M=!0},p(s,[h]){(!M||h&1)&&m!==(m=s[0].name+"")&&J(k,m),h&6&&(_=K(s[2]),P=le(P,h,F,1,s,_,j,O,Re,ye,null,Ce)),h&6&&(c=K(s[2]),ne(),w=le(w,h,f,1,s,c,E,W,Se,Ee,null,we),ae())},i(s){if(!M){for(let h=0;h<c.length;h+=1)V(w[h]);M=!0}},o(s){for(let h=0;h<w.length;h+=1)X(w[h]);M=!1},d(s){s&&(v(e),v(Q),v(L),v(z),v(B),v(D),v(S),v(N),v(T));for(let h=0;h<P.length;h+=1)P[h].d();for(let h=0;h<w.length;h+=1)w[h].d()}}}function Ue(n,e,t){let{collection:l}=e,d=204,i=[];const r=a=>t(1,d=a.code);return n.$$set=a=>{"collection"in a&&t(0,l=a.collection)},t(2,i=[{code:204,body:"null"},{code:400,body:`
|
||||
{
|
||||
"code": 400,
|
||||
"message": "An error occurred while validating the submitted data.",
|
||||
"data": {
|
||||
"newEmail": {
|
||||
"code": "validation_required",
|
||||
"message": "Missing required value."
|
||||
}
|
||||
}
|
||||
}
|
||||
`},{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": {}
|
||||
}
|
||||
`}]),[l,d,i,r]}class Ie extends se{constructor(e){super(),oe(this,e,Ue,Le,ie,{collection:0})}}function Te(n,e,t){const l=n.slice();return l[5]=e[t],l[7]=t,l}function qe(n,e,t){const l=n.slice();return l[5]=e[t],l[7]=t,l}function Ae(n){let e,t,l,d,i;function r(){return n[4](n[7])}return{c(){e=p("button"),t=p("div"),t.textContent=`${n[5].title}`,l=y(),b(t,"class","txt"),b(e,"class","tab-item"),Y(e,"active",n[1]==n[7])},m(a,m){g(a,e,m),u(e,t),u(e,l),d||(i=ce(e,"click",r),d=!0)},p(a,m){n=a,m&2&&Y(e,"active",n[1]==n[7])},d(a){a&&v(e),d=!1,i()}}}function Pe(n){let e,t,l,d;var i=n[5].component;function r(a,m){return{props:{collection:a[0]}}}return i&&(t=be(i,r(n))),{c(){e=p("div"),t&&x(t.$$.fragment),l=y(),b(e,"class","tab-item"),Y(e,"active",n[1]==n[7])},m(a,m){g(a,e,m),t&&ee(t,e,null),u(e,l),d=!0},p(a,m){if(i!==(i=a[5].component)){if(t){ne();const k=t;X(k.$$.fragment,1,0,()=>{te(k,1)}),ae()}i?(t=be(i,r(a)),x(t.$$.fragment),V(t.$$.fragment,1),ee(t,e,l)):t=null}else if(i){const k={};m&1&&(k.collection=a[0]),t.$set(k)}(!d||m&2)&&Y(e,"active",a[1]==a[7])},i(a){d||(t&&V(t.$$.fragment,a),d=!0)},o(a){t&&X(t.$$.fragment,a),d=!1},d(a){a&&v(e),t&&te(t)}}}function Ke(n){var c,f,s,h,R,re;let e,t,l=n[0].name+"",d,i,r,a,m,k,A,G=n[0].name+"",H,Q,L,z,B,D,S,N,T,O,P,j,q,W;D=new Me({props:{js:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(c=n[0])==null?void 0:c.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(f=n[0])==null?void 0:f.name}').requestEmailChange('new@example.com');
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(s=n[0])==null?void 0:s.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`,dart:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${n[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(h=n[0])==null?void 0:h.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(R=n[0])==null?void 0:R.name}').requestEmailChange('new@example.com');
|
||||
|
||||
...
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(re=n[0])==null?void 0:re.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`}});let w=K(n[3]),E=[];for(let o=0;o<w.length;o+=1)E[o]=Ae(qe(n,w,o));let M=K(n[3]),_=[];for(let o=0;o<M.length;o+=1)_[o]=Pe(Te(n,M,o));const F=o=>X(_[o],1,1,()=>{_[o]=null});return{c(){e=p("h3"),t=U("Email change ("),d=U(l),i=U(")"),r=y(),a=p("div"),m=p("p"),k=U("Sends "),A=p("strong"),H=U(G),Q=U(" email change request."),L=y(),z=p("p"),z.textContent=`On successful email change all previously issued auth tokens for the specific record will be
|
||||
automatically invalidated.`,B=y(),x(D.$$.fragment),S=y(),N=p("h6"),N.textContent="API details",T=y(),O=p("div"),P=p("div");for(let o=0;o<E.length;o+=1)E[o].c();j=y(),q=p("div");for(let o=0;o<_.length;o+=1)_[o].c();b(e,"class","m-b-sm"),b(a,"class","content txt-lg m-b-sm"),b(N,"class","m-b-xs"),b(P,"class","tabs-header compact"),b(q,"class","tabs-content"),b(O,"class","tabs")},m(o,C){g(o,e,C),u(e,t),u(e,d),u(e,i),g(o,r,C),g(o,a,C),u(a,m),u(m,k),u(m,A),u(A,H),u(m,Q),u(a,L),u(a,z),g(o,B,C),ee(D,o,C),g(o,S,C),g(o,N,C),g(o,T,C),g(o,O,C),u(O,P);for(let I=0;I<E.length;I+=1)E[I]&&E[I].m(P,null);u(O,j),u(O,q);for(let I=0;I<_.length;I+=1)_[I]&&_[I].m(q,null);W=!0},p(o,[C]){var de,ue,fe,me,he,pe;(!W||C&1)&&l!==(l=o[0].name+"")&&J(d,l),(!W||C&1)&&G!==(G=o[0].name+"")&&J(H,G);const I={};if(C&5&&(I.js=`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${o[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(de=o[0])==null?void 0:de.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(ue=o[0])==null?void 0:ue.name}').requestEmailChange('new@example.com');
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(fe=o[0])==null?void 0:fe.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),C&5&&(I.dart=`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${o[2]}');
|
||||
|
||||
...
|
||||
|
||||
await pb.collection('${(me=o[0])==null?void 0:me.name}').authWithPassword('test@example.com', '1234567890');
|
||||
|
||||
await pb.collection('${(he=o[0])==null?void 0:he.name}').requestEmailChange('new@example.com');
|
||||
|
||||
...
|
||||
|
||||
// ---
|
||||
// (optional) in your custom confirmation page:
|
||||
// ---
|
||||
|
||||
// note: after this call all previously issued auth tokens are invalidated
|
||||
await pb.collection('${(pe=o[0])==null?void 0:pe.name}').confirmEmailChange(
|
||||
'EMAIL_CHANGE_TOKEN',
|
||||
'YOUR_PASSWORD',
|
||||
);
|
||||
`),D.$set(I),C&10){w=K(o[3]);let $;for($=0;$<w.length;$+=1){const Z=qe(o,w,$);E[$]?E[$].p(Z,C):(E[$]=Ae(Z),E[$].c(),E[$].m(P,null))}for(;$<E.length;$+=1)E[$].d(1);E.length=w.length}if(C&11){M=K(o[3]);let $;for($=0;$<M.length;$+=1){const Z=Te(o,M,$);_[$]?(_[$].p(Z,C),V(_[$],1)):(_[$]=Pe(Z),_[$].c(),V(_[$],1),_[$].m(q,null))}for(ne(),$=M.length;$<_.length;$+=1)F($);ae()}},i(o){if(!W){V(D.$$.fragment,o);for(let C=0;C<M.length;C+=1)V(_[C]);W=!0}},o(o){X(D.$$.fragment,o),_=_.filter(Boolean);for(let C=0;C<_.length;C+=1)X(_[C]);W=!1},d(o){o&&(v(e),v(r),v(a),v(B),v(S),v(N),v(T),v(O)),te(D,o),_e(E,o),_e(_,o)}}}function Ye(n,e,t){let l,{collection:d}=e;const i=[{title:"Request email change",component:Ie},{title:"Confirm email change",component:He}];let r=0;const a=m=>t(1,r=m);return n.$$set=m=>{"collection"in m&&t(0,d=m.collection)},t(2,l=Be.getApiExampleUrl(De.baseURL)),[d,r,l,i,a]}class ze extends se{constructor(e){super(),oe(this,e,Ye,Ke,ie,{collection:0})}}export{ze as default};
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import{S as I,i as J,s as N,V as O,f as t,h as c,y as i,c as P,j as Q,l as R,n as e,m as V,G as z,t as A,a as D,u as K,d as U}from"./index-0yJTQg7M.js";function W(f){let n,o,u,d,k,s,p,v,g,F,r,S,_,w,b,E,C,a,$,L,q,H,M,T,m,j,y,B,x;return r=new O({props:{content:"?fields=*,"+f[0]+"expand.relField.name"}}),{c(){n=t("tr"),o=t("td"),o.textContent="fields",u=c(),d=t("td"),d.innerHTML='<span class="label">String</span>',k=c(),s=t("td"),p=t("p"),v=i(`Comma separated string of the fields to return in the JSON response
|
||||
`),g=t("em"),g.textContent="(by default returns all fields)",F=i(`. Ex.:
|
||||
`),P(r.$$.fragment),S=c(),_=t("p"),_.innerHTML="<code>*</code> targets all keys from the specific depth level.",w=c(),b=t("p"),b.textContent="In addition, the following field modifiers are also supported:",E=c(),C=t("ul"),a=t("li"),$=t("code"),$.textContent=":excerpt(maxLength, withEllipsis?)",L=c(),q=t("br"),H=i(`
|
||||
Returns a short plain text version of the field string value.
|
||||
`),M=t("br"),T=i(`
|
||||
Ex.:
|
||||
`),m=t("code"),j=i("?fields=*,"),y=i(f[0]),B=i("description:excerpt(200,true)"),Q(o,"id","query-page")},m(l,h){R(l,n,h),e(n,o),e(n,u),e(n,d),e(n,k),e(n,s),e(s,p),e(p,v),e(p,g),e(p,F),V(r,p,null),e(s,S),e(s,_),e(s,w),e(s,b),e(s,E),e(s,C),e(C,a),e(a,$),e(a,L),e(a,q),e(a,H),e(a,M),e(a,T),e(a,m),e(m,j),e(m,y),e(m,B),x=!0},p(l,[h]){const G={};h&1&&(G.content="?fields=*,"+l[0]+"expand.relField.name"),r.$set(G),(!x||h&1)&&z(y,l[0])},i(l){x||(A(r.$$.fragment,l),x=!0)},o(l){D(r.$$.fragment,l),x=!1},d(l){l&&K(n),U(r)}}}function X(f,n,o){let{prefix:u=""}=n;return f.$$set=d=>{"prefix"in d&&o(0,u=d.prefix)},[u]}class Z extends I{constructor(n){super(),J(this,n,X,W,N,{prefix:0})}}export{Z as F};
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
import{S as J,i as N,s as O,R as P,e as t,b as c,w as i,c as Q,f as j,g as z,h as e,m as A,x as D,t as G,a as K,o as U,d as V}from"./index-Co7n6kj7.js";function W(f){let n,o,u,d,v,s,p,w,h,y,r,F,_,S,b,E,C,a,$,L,q,H,M,R,m,T,k,B,x;return r=new P({props:{content:"?fields=*,"+f[0]+"expand.relField.name"}}),{c(){n=t("tr"),o=t("td"),o.textContent="fields",u=c(),d=t("td"),d.innerHTML='<span class="label">String</span>',v=c(),s=t("td"),p=t("p"),w=i(`Comma separated string of the fields to return in the JSON response
|
||||
`),h=t("em"),h.textContent="(by default returns all fields)",y=i(`. Ex.:
|
||||
`),Q(r.$$.fragment),F=c(),_=t("p"),_.innerHTML="<code>*</code> targets all keys from the specific depth level.",S=c(),b=t("p"),b.textContent="In addition, the following field modifiers are also supported:",E=c(),C=t("ul"),a=t("li"),$=t("code"),$.textContent=":excerpt(maxLength, withEllipsis?)",L=c(),q=t("br"),H=i(`
|
||||
Returns a short plain text version of the field string value.
|
||||
`),M=t("br"),R=i(`
|
||||
Ex.:
|
||||
`),m=t("code"),T=i("?fields=*,"),k=i(f[0]),B=i("description:excerpt(200,true)"),j(o,"id","query-page")},m(l,g){z(l,n,g),e(n,o),e(n,u),e(n,d),e(n,v),e(n,s),e(s,p),e(p,w),e(p,h),e(p,y),A(r,p,null),e(s,F),e(s,_),e(s,S),e(s,b),e(s,E),e(s,C),e(C,a),e(a,$),e(a,L),e(a,q),e(a,H),e(a,M),e(a,R),e(a,m),e(m,T),e(m,k),e(m,B),x=!0},p(l,[g]){const I={};g&1&&(I.content="?fields=*,"+l[0]+"expand.relField.name"),r.$set(I),(!x||g&1)&&D(k,l[0])},i(l){x||(G(r.$$.fragment,l),x=!0)},o(l){K(r.$$.fragment,l),x=!1},d(l){l&&U(n),V(r)}}}function X(f,n,o){let{prefix:u=""}=n;return f.$$set=d=>{"prefix"in d&&o(0,u=d.prefix)},[u]}class Z extends J{constructor(n){super(),N(this,n,X,W,O,{prefix:0})}}export{Z as F};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user