Compare commits

...
38 Commits
Author SHA1 Message Date
Gani Georgiev 099230a552 added missing time import 2022-11-02 21:52:47 +02:00
Gani Georgiev 5e0718176d added db pool size limits and update the min go release action version to 1.19.3 2022-11-02 21:44:23 +02:00
Jan Lauber 9cbb2e750e [#794] fixed comment typos 2022-10-17 20:17:44 +03:00
Piotr Rogowski 6385c5e646 [#789] fixed typo in realtime debug log 2022-10-17 08:21:56 +03:00
Gani Georgiev 32393990bb preserve records pagination on delete/update and fix reactivity regression 2022-10-04 22:42:51 +03:00
Gani Georgiev 838ed661ce fixed formatted date reactivity 2022-10-02 23:56:24 +03:00
Gani Georgiev d84e57174b updated code comments formatting 2022-10-02 13:38:59 +03:00
Gani Georgiev a6cafd1ed7 [#677] unset the X-Frame-Options when serving static files to allow files embedding 2022-10-02 13:28:33 +03:00
Gani Georgiev b0db2399b8 updated filesystem tests 2022-10-02 12:38:14 +03:00
Gani Georgiev 81d0af6e80 [#693] added media-src to the default files CSP 2022-10-02 12:33:31 +03:00
Gani Georgiev 5f5f0ed793 added Open Collective to the funding options 2022-09-30 11:39:38 +03:00
Gani Georgiev 353248c34a updated ui/dist 2022-09-29 12:54:58 +03:00
Gani Georgiev 93d48a85ac added fallback handling when both contains operands are table columns 2022-09-29 12:33:53 +03:00
Gani Georgiev b84930f21a records listing optimizations 2022-09-28 22:17:24 +03:00
Rohan Verma 3cbab96f51 [#661] serve css files with text/css content-type
Currently, css files are served as text/plain by the server. It is not
trivial to detect css file types similar to the issue with svg files.

When the css files are served as text/plain instead of
text/css they become unusable as stylesheets in the browser when served
via the api.

In this commit we generalize the svg detection to also detect css files
and serve specific extensions with their respective mimetypes.
2022-09-28 21:25:50 +03:00
Gani Georgiev 6c005c4a9a remove OrderBy nil variadic argument 2022-09-22 20:35:20 +03:00
Gani Georgiev ccc3707fb6 replaced empty slice literal with nil 2022-09-22 20:23:50 +03:00
Gani Georgiev 3d36ff7e96 unset ORDER BY for search count queries 2022-09-22 20:18:17 +03:00
Gani Georgiev 0b2eb24c6f updated go deps 2022-09-21 21:29:23 +03:00
Gani Georgiev 954067860c [#590] fixed realtime events bind order by adding hooks.PreAdd 2022-09-21 14:41:20 +03:00
Gani Georgiev 9a8c9dd115 [#586] fixed multiple nested relation expansions with shared base path 2022-09-21 13:34:34 +03:00
Aaron Schmied a1ad5004f8 [#585] respect the EXIF orientation tag when generating thumbs
@see: https://github.com/disintegration/imaging#incorrect-image-orientation-after-processing-eg-an-image-appears-rotated-after-resizing
2022-09-21 13:13:26 +03:00
Gani Georgiev 7006e1f5d7 [#567] resolve the direct user profile fields from the profiles table and not from the static auth model 2022-09-20 11:20:30 +03:00
Gani Georgiev 8be8f3f3cb updated search provider tests 2022-09-18 08:49:51 +03:00
Gani Georgiev 00fd007d50 raised MaxPerPage limit to 400 2022-09-18 08:41:42 +03:00
Gani Georgiev 96bfc69c8e updated ui/dist 2022-09-18 01:57:18 +03:00
Gani Georgiev e542b0d8c5 include only the words selection keymap for code inputs 2022-09-18 01:55:33 +03:00
Gani Georgiev 9814dda8e4 [#478] load selected relation items before the other options 2022-09-18 01:18:54 +03:00
Gani Georgiev 978fdd3ce7 [#478] preserve multiple selection order 2022-09-18 00:29:32 +03:00
Gani Georgiev 9cf89870e7 [#519] improved query performance for relations lookup 2022-09-17 22:55:56 +03:00
Gani Georgiev b8c54568e3 fixed readme typo 2022-09-16 13:24:02 +03:00
Gani Georgiev 843bbf99cc added note about pull requests 2022-09-16 13:06:28 +03:00
Gani Georgiev daffb0f86e [#488] added X-Accel-Buffering:no sse header 2022-09-16 11:19:31 +03:00
Gani Georgiev 1f5c3328e5 [#470] added --pbPublic flag 2022-09-16 11:18:15 +03:00
Gani Georgiev ed5f3b86f5 [#470] don't rely on the cwd and look for pb_public relative to pb_data 2022-09-15 22:52:24 +03:00
Travis Ray 1ba2d14231 [#446] Fixed spelling error on Realtime API page 2022-09-14 21:31:12 +03:00
Gani Georgiev 6cda610ede updated ui deps and generated ui/dist 2022-09-14 20:53:09 +03:00
Gani Georgiev 2fa5233fa6 [#440] try to use the original image format when creating thumbs 2022-09-14 17:12:47 +03:00
62 changed files with 1544 additions and 1180 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
open_collective: pocketbase
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: '>=1.19.0'
go-version: '>=1.19.3'
# This step usually is not needed because the /ui/dist is pregenerated locally
# but its here to ensure that each release embeds the latest admin ui artifacts.
+4
View File
@@ -121,7 +121,11 @@ PocketBase is free and open source project licensed under the [MIT License](LICE
You could help continuing its development by:
- [Contribute to the source code](CONTRIBUTING.md)
> **NB! PR for new features will not be merged until we manage to complete the users refactoring in [#376](https://github.com/pocketbase/pocketbase/issues/376)!**
- [Suggest new features and report issues](https://github.com/pocketbase/pocketbase/issues)
- [Donate a small amount](https://pocketbase.io/support-us)
> Please note that PocketBase was initially created to serve as a new backend for my other open source project - [Presentator](https://presentator.io) (see [#183](https://github.com/presentator/presentator/issues/183)),
+8 -5
View File
@@ -48,6 +48,9 @@ func (api *realtimeApi) connect(c echo.Context) error {
c.Response().Header().Set("Content-Type", "text/event-stream; charset=UTF-8")
c.Response().Header().Set("Cache-Control", "no-store")
c.Response().Header().Set("Connection", "keep-alive")
// https://github.com/pocketbase/pocketbase/discussions/480#discussioncomment-3657640
// https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering
c.Response().Header().Set("X-Accel-Buffering", "no")
event := &core.RealtimeConnectEvent{
HttpContext: c,
@@ -59,7 +62,7 @@ func (api *realtimeApi) connect(c echo.Context) error {
}
if api.app.IsDebug() {
log.Printf("Realtime connection establisehd: %s\n", client.Id())
log.Printf("Realtime connection established: %s\n", client.Id())
}
// signalize established connection (aka. fire "connect" message)
@@ -163,7 +166,7 @@ func (api *realtimeApi) bindEvents() {
adminTable := (&models.Admin{}).TableName()
// update user/admin auth state
api.app.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error {
api.app.OnModelAfterUpdate().PreAdd(func(e *core.ModelEvent) error {
modelTable := e.Model.TableName()
var contextKey string
@@ -187,7 +190,7 @@ func (api *realtimeApi) bindEvents() {
})
// remove user/admin client(s)
api.app.OnModelAfterDelete().Add(func(e *core.ModelEvent) error {
api.app.OnModelAfterDelete().PreAdd(func(e *core.ModelEvent) error {
modelTable := e.Model.TableName()
var contextKey string
@@ -210,14 +213,14 @@ func (api *realtimeApi) bindEvents() {
return nil
})
api.app.OnModelAfterCreate().Add(func(e *core.ModelEvent) error {
api.app.OnModelAfterCreate().PreAdd(func(e *core.ModelEvent) error {
if record, ok := e.Model.(*models.Record); ok {
api.broadcastRecord("create", record)
}
return nil
})
api.app.OnModelAfterUpdate().Add(func(e *core.ModelEvent) error {
api.app.OnModelAfterUpdate().PreAdd(func(e *core.ModelEvent) error {
if record, ok := e.Model.(*models.Record); ok {
api.broadcastRecord("update", record)
}
+4 -4
View File
@@ -125,7 +125,7 @@ type BaseApp struct {
// NewBaseApp creates and returns a new BaseApp instance
// configured with the provided arguments.
//
// To initialize the app, you need to call `app.Bootsrap()`.
// To initialize the app, you need to call `app.Bootstrap()`.
func NewBaseApp(dataDir string, encryptionEnv string, isDebug bool) *BaseApp {
app := &BaseApp{
dataDir: dataDir,
@@ -716,7 +716,7 @@ func (app *BaseApp) initLogsDB() error {
return connectErr
}
app.logsDao = app.createDao(app.logsDB)
app.logsDao = daos.New(app.logsDB)
return nil
}
@@ -740,12 +740,12 @@ func (app *BaseApp) initDataDB() error {
}
}
app.dao = app.createDao(app.db)
app.dao = app.createDaoWithHooks(app.db)
return nil
}
func (app *BaseApp) createDao(db dbx.Builder) *daos.Dao {
func (app *BaseApp) createDaoWithHooks(db dbx.Builder) *daos.Dao {
dao := daos.New(db)
dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
+13 -2
View File
@@ -4,19 +4,30 @@ package core
import (
"fmt"
"time"
_ "github.com/mattn/go-sqlite3"
"github.com/pocketbase/dbx"
_ "github.com/mattn/go-sqlite3"
)
func connectDB(dbPath string) (*dbx.DB, error) {
pragmas := "_foreign_keys=1&_journal_mode=WAL&_synchronous=NORMAL&_busy_timeout=8000"
// note: the busy_timeout pragma must be first because
// the connection needs to be set to block on busy before WAL mode
// is set in case it hasn't been already set by another connection
pragmas := "_busy_timeout=10000&_journal_mode=WAL&_foreign_keys=1&_synchronous=NORMAL"
db, openErr := dbx.MustOpen("sqlite3", fmt.Sprintf("%s?%s", dbPath, pragmas))
if openErr != nil {
return nil, openErr
}
// use a fixed connection pool to limit the SQLITE_BUSY errors
// and reduce the open file descriptors
// (the limits are arbitrary and may change in the future)
db.DB().SetMaxOpenConns(500)
db.DB().SetMaxIdleConns(30)
db.DB().SetConnMaxIdleTime(5 * time.Minute)
// additional pragmas not supported through the dsn string
_, err := db.NewQuery(`
pragma journal_size_limit = 100000000;
+20 -2
View File
@@ -4,13 +4,31 @@ package core
import (
"fmt"
"time"
"github.com/pocketbase/dbx"
_ "modernc.org/sqlite"
)
func connectDB(dbPath string) (*dbx.DB, error) {
pragmas := "_pragma=foreign_keys(1)&_pragma=journal_mode(WAL)&_pragma=synchronous(NORMAL)&_pragma=busy_timeout(8000)&_pragma=journal_size_limit(100000000)"
// note: the busy_timeout pragma must be first because
// the connection needs to be set to block on busy before WAL mode
// is set in case it hasn't been already set by another connection
pragmas := "_pragma=busy_timeout(10000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)&_pragma=synchronous(NORMAL)&_pragma=journal_size_limit(100000000)"
return dbx.MustOpen("sqlite", fmt.Sprintf("%s?%s", dbPath, pragmas))
db, err := dbx.MustOpen("sqlite", fmt.Sprintf("%s?%s", dbPath, pragmas))
if err != nil {
return nil, err
}
// use a fixed connection pool to limit the SQLITE_BUSY errors and
// reduce the open file descriptors
// (the limits are arbitrary and may change in the future)
//
// @see https://gitlab.com/cznic/sqlite/-/issues/115
db.DB().SetMaxOpenConns(500)
db.DB().SetMaxIdleConns(30)
db.DB().SetConnMaxIdleTime(5 * time.Minute)
return db, nil
}
+1 -1
View File
@@ -76,7 +76,7 @@ func (dao *Dao) FindCollectionsWithUserFields() ([]*models.Collection, error) {
// relation schema fields referencing the provided collection.
//
// If the provided collection has reference to itself then it will be
// also included in the result. To exlude it, pass the collection id
// also included in the result. To exclude it, pass the collection id
// as the excludeId argument.
func (dao *Dao) FindCollectionReferences(collection *models.Collection, excludeId string) (map[*models.Collection][]*schema.SchemaField, error) {
collections := []*models.Collection{}
+30 -1
View File
@@ -114,12 +114,41 @@ func (dao *Dao) expandRecords(records []*models.Record, expandPath string, fetch
expandData := model.GetExpand()
// normalize and set the expanded relations
// normalize access to the previously expanded rel records (if any)
var oldExpandedRels []*models.Record
switch v := expandData[relField.Name].(type) {
case nil:
// no old expands
case *models.Record:
oldExpandedRels = []*models.Record{v}
case []*models.Record:
oldExpandedRels = v
}
// merge expands
for _, oldExpandedRel := range oldExpandedRels {
// find a matching rel record
for _, rel := range validRels {
if rel.Id != oldExpandedRel.Id {
continue
}
oldRelExpand := oldExpandedRel.GetExpand()
newRelExpand := rel.GetExpand()
for k, v := range oldRelExpand {
newRelExpand[k] = v
}
rel.SetExpand(newRelExpand)
}
}
// update the expanded data
if relFieldOptions.MaxSelect == 1 {
expandData[relField.Name] = validRels[0]
} else {
expandData[relField.Name] = validRels
}
model.SetExpand(expandData)
}
+12
View File
@@ -108,6 +108,18 @@ func TestExpandRecords(t *testing.T) {
9,
0,
},
// expand multiple relations sharing a common root path
{
[]string{
"i15r5aa28ad06c8",
},
[]string{"manyrels.onerel.manyrels.onerel", "manyrels.onerel.onerel"},
func(c *models.Collection, ids []string) ([]*models.Record, error) {
return app.Dao().FindRecordsByIds(c, ids, nil)
},
4,
0,
},
// single expand
{
[]string{
+1 -1
View File
@@ -35,7 +35,7 @@ func (dao *Dao) LoadProfile(user *models.User) error {
return nil
}
// LoadProfiles loads the profile records associated to the provied users list.
// LoadProfiles loads the profile records associated to the provided users list.
func (dao *Dao) LoadProfiles(users []*models.User) error {
collection, err := dao.FindCollectionByNameOrId(models.ProfileCollectionName)
if err != nil {
+23 -3
View File
@@ -2,20 +2,40 @@ package main
import (
"log"
"os"
"path/filepath"
"strings"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
)
func defaultPublicDir() string {
if strings.HasPrefix(os.Args[0], os.TempDir()) {
// most likely ran with go run
return "./pb_public"
}
return filepath.Join(os.Args[0], "../pb_public")
}
func main() {
app := pocketbase.New()
var publicDirFlag string
// add "--publicDir" option flag
app.RootCmd.PersistentFlags().StringVar(
&publicDirFlag,
"publicDir",
defaultPublicDir(),
"the directory to serve static files",
)
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
// serves static files from the provided public dir (if exists)
subFs := echo.MustSubFS(e.Router.Filesystem, "pb_public")
e.Router.GET("/*", apis.StaticDirectoryHandler(subFs, false))
e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS(publicDirFlag), false))
return nil
})
+2 -2
View File
@@ -241,7 +241,7 @@ func TestAdminUpsertSubmit(t *testing.T) {
true,
},
{
// update succcess - new email
// update success - new email
"2b4a97cc-3f83-4d01-a26b-3d77bc842d3c",
`{
"email": "test_update@example.com"
@@ -249,7 +249,7 @@ func TestAdminUpsertSubmit(t *testing.T) {
false,
},
{
// update succcess - new password
// update success - new password
"2b4a97cc-3f83-4d01-a26b-3d77bc842d3c",
`{
"password": "1234567890",
+37 -36
View File
@@ -3,8 +3,8 @@ module github.com/pocketbase/pocketbase
go 1.18
require (
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/aws/aws-sdk-go v1.44.85
github.com/AlecAivazis/survey/v2 v2.3.6
github.com/aws/aws-sdk-go v1.44.102
github.com/disintegration/imaging v1.6.2
github.com/domodwyer/mailyak/v3 v3.3.4
github.com/fatih/color v1.13.0
@@ -18,32 +18,33 @@ require (
github.com/spf13/cast v1.5.0
github.com/spf13/cobra v1.5.0
gocloud.dev v0.26.0
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
modernc.org/sqlite v1.18.1
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0
golang.org/x/net v0.0.0-20220921155015-db77216a4ee9
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
modernc.org/sqlite v1.19.1
)
require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go-v2 v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.17.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.12.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.19 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.12 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.12 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.13 // indirect
github.com/aws/smithy-go v1.12.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.16.16 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 // indirect
github.com/aws/aws-sdk-go-v2/config v1.17.7 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.12.20 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 // indirect
github.com/aws/smithy-go v1.13.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
@@ -60,26 +61,26 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
go.opencensus.io v0.23.0 // indirect
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 // indirect
golang.org/x/tools v0.1.12 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
google.golang.org/api v0.94.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.96.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc // indirect
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 // indirect
google.golang.org/grpc v1.49.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.36.3 // indirect
modernc.org/cc/v3 v3.39.0 // indirect
modernc.org/ccgo/v3 v3.16.9 // indirect
modernc.org/libc v1.17.0 // indirect
modernc.org/libc v1.19.0 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.2.0 // indirect
modernc.org/memory v1.4.0 // indirect
modernc.org/opt v0.1.3 // indirect
modernc.org/strutil v1.1.2 // indirect
modernc.org/token v1.0.0 // indirect
modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.0.1 // indirect
)
+76
View File
@@ -78,6 +78,8 @@ contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0Wk
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
github.com/AlecAivazis/survey/v2 v2.3.6 h1:NvTuVHISgTHEHeBFqt6BHOe4Ny/NwGZr7w+F8S9ziyw=
github.com/AlecAivazis/survey/v2 v2.3.6/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
github.com/Azure/azure-amqp-common-go/v3 v3.2.1/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI=
github.com/Azure/azure-amqp-common-go/v3 v3.2.2/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
@@ -124,51 +126,83 @@ github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zK
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.85 h1:JM2rkKY/GtTDCQXW0StkImbLn6n4Q/Dm2bj+u1rm7Kw=
github.com/aws/aws-sdk-go v1.44.85/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.102 h1:6tUCTGL2UDbFZae1TLGk8vTgeXuzkb8KbAe2FiAeKHc=
github.com/aws/aws-sdk-go v1.44.102/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2 v1.16.11 h1:xM1ZPSvty3xVmdxiGr7ay/wlqv+MWhH0rMlyLdbC0YQ=
github.com/aws/aws-sdk-go-v2 v1.16.11/go.mod h1:WTACcleLz6VZTp7fak4EO5b9Q4foxbn+8PIz3PmyKlo=
github.com/aws/aws-sdk-go-v2 v1.16.16 h1:M1fj4FE2lB4NzRb9Y0xdWsn2P0+2UHVxwKyOa4YJNjk=
github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.4 h1:zfT11pa7ifu/VlLDpmc5OY2W4nYmnKkFDGeMVnmqAI0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.4/go.mod h1:ES0I1GBs+YYgcDS1ek47Erbn4TOL811JKqBXtgzqyZ8=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 h1:tcFliCWne+zOuUfKNRn8JdFBuWPDuISDH08wD2ULkhk=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8/go.mod h1:JTnlBSot91steJeti4ryyu/tLd4Sk84O5W22L7O2EQU=
github.com/aws/aws-sdk-go-v2/config v1.15.3/go.mod h1:9YL3v07Xc/ohTsxFXzan9ZpFpdTOFl4X65BAKYaz8jg=
github.com/aws/aws-sdk-go-v2/config v1.17.1 h1:BWxTjokU/69BZ4DnLrZco6OvBDii6ToEdfBL/y5I1nA=
github.com/aws/aws-sdk-go-v2/config v1.17.1/go.mod h1:uOxDHjBemNTF2Zos+fgG0NNfE86wn1OAHDTGxjMEYi0=
github.com/aws/aws-sdk-go-v2/config v1.17.7 h1:odVM52tFHhpqZBKNjVW5h+Zt1tKHbhdTQRb+0WHrNtw=
github.com/aws/aws-sdk-go-v2/config v1.17.7/go.mod h1:dN2gja/QXxFF15hQreyrqYhLBaQo1d9ZKe/v/uplQoI=
github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g=
github.com/aws/aws-sdk-go-v2/credentials v1.12.14 h1:AtVG/amkjbDBfnPr/tuW2IG18HGNznP6L12Dx0rLz+Q=
github.com/aws/aws-sdk-go-v2/credentials v1.12.14/go.mod h1:opAndTyq+YN7IpVG57z2CeNuXSQMqTYxGGlYH0m0RMY=
github.com/aws/aws-sdk-go-v2/credentials v1.12.20 h1:9+ZhlDY7N9dPnUmf7CDfW9In4sW5Ff3bh7oy4DzS1IE=
github.com/aws/aws-sdk-go-v2/credentials v1.12.20/go.mod h1:UKY5HyIux08bbNA7Blv4PcXQ8cTkGh7ghHMFklaviR4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12 h1:wgJBHO58Pc1V1QAnzdVM3JK3WbE/6eUF0JxCZ+/izz0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12/go.mod h1:aZ4vZnyUuxedC7eD4JyEHpGnCz+O2sHQEx3VvAwklSE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 h1:r08j4sbZu/RVi+BNxkBJwPMUYY3P8mgSDuKkZ/ZN1lE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17/go.mod h1:yIkQcCDYNsZfXpd5UX2Cy+sWA1jPgIhGTw9cOBzfVnQ=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.3/go.mod h1:0dHuD2HZZSiwfJSy1FO5bX1hQ1TxVV1QXXjpn3XUE44=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.27 h1:xFXIMBci0UXStoOHq/8w0XIZPB2hgb9CD7uATJhqt10=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.27/go.mod h1:+tj2cHQkChanggNZn1J2fJ1Cv6RO1TV0AA3472do31I=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33 h1:fAoVmNGhir6BR+RU0/EI+6+D7abM+MCwWf8v4ip5jNI=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.33/go.mod h1:84XgODVR8uRhmOnUkKGUZKqIMxmjmLOR8Uyp7G/TPwc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18 h1:OmiwoVyLKEqqD5GvB683dbSqxiOfvx4U2lDZhG2Esc4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18/go.mod h1:348MLhzV1GSlZSMusdwQpXKbhD7X2gbI/TxwAPKkYZQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23 h1:s4g/wnzMf+qepSNgTvaQQHNxyMLKSawNhKCPNy++2xY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12 h1:5mvQDtNWtI6H56+E4LUnLWEmATMB7oEh+Z9RurtIuC0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12/go.mod h1:ckaCVTEdGAxO6KwTGzgskxR1xM+iJW4lxMyDFVda2Fc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17 h1:/K482T5A3623WJgWT8w1yRAFK4RzGzEl7y39yhtn9eA=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.19 h1:g5qq9sgtEzt2szMaDqQO6fqKe026T6dHTFJp5NsPzkQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.19/go.mod h1:cVHo8KTuHjShb9V8/VjH3S/8+xPu16qx8fdGwmotJhE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 h1:wj5Rwc05hvUSvKuOF29IYb9QrCLjU+rHAy/x/o0DK2c=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24/go.mod h1:jULHjqqjDlbyTa7pfM7WICATnOv+iOhjletM3N0Xbu8=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.9 h1:agLpf3vtYX1rtKTrOGpevdP3iC2W0hKDmzmhhxJzL+A=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.9/go.mod h1:cv+n1mdyh+0B8tAtlEBzTYFA2Uv15SISEn6kabYhIgE=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 h1:ZSIPAkAsCCjYrhqfw2+lNzWDzxzHXEckFkTePL5RSWQ=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14/go.mod h1:AyGgqiKv9ECM6IZeNQtdT8NnMvUb3/2wokeq2Fgryto=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:GeUru+8VzrTXV/83XyMJ80KpH8xO89VPoUileyNQ+tc=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.5 h1:g1ITJ9i9ixa+/WVggLNK20KyliAA8ltnuxfZEDfo2hM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.5/go.mod h1:oehQLbMQkppKLXvpx/1Eo0X47Fe+0971DXC9UjGnKcI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 h1:Lh1AShsuIJTwMkoxVCAYPJgNG5H+eN6SmoUn8nOZ5wE=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9/go.mod h1:a9j48l6yL5XINLHLcOKInjdvknN+vWqPBxqeIDw7ktw=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3/go.mod h1:Seb8KNmD6kVTjwRjVEgOT5hPin6sq+v4C2ycJQDwuH8=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.13 h1:3GamN8jcdz/a3nvL/ZVtoH/6xxeshfsiXj5O+6GW4Rg=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.13/go.mod h1:89CSPn69UECDLVn0H6FwKNgbtirksl8C8i3aBeeeihw=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 h1:BBYoNQt2kUZUUK4bIPsKrCcjVPUMNsgQpNAwhznK/zo=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18/go.mod h1:NS55eQ4YixUJPTC+INxi2/jCqe1y2Uw3rnh9wEOVJxY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3/go.mod h1:wlY6SVjuwvh3TVRpTqdy4I1JpBFLX4UGeKZdWntaocw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.12 h1:7iPTTX4SAI2U2VOogD7/gmHlsgnYSgoNHt7MSQXtG2M=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.12/go.mod h1:1TODGhheLWjpQWSuhYuAUWYTCKwEjx2iblIFKDHjeTc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 h1:Jrd/oMh0PKQc6+BowB+pLEwLIgaQF29eYbe7E1Av9Ug=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3/go.mod h1:Bm/v2IaN6rZ+Op7zX+bOUMdL4fsrYZiD0dsjLhNKwZc=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.12 h1:QFjSOmHSb77qRTv7KI9UFon9X5wLWY5/M+6la3dTcZc=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.12/go.mod h1:MADjAN0GHFDuc5lRa5Y5ki+oIO/w7X4qczHy+OUx0IA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 h1:HfVVR1vItaG6le+Bpw6P4midjBDMKnjMyZnw9MXYUcE=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17/go.mod h1:YqMdV+gEKCQ59NrB7rzrJdALeBIsYiVi8Inj3+KcqHI=
github.com/aws/aws-sdk-go-v2/service/kms v1.16.3/go.mod h1:QuiHPBqlOFCi4LqdSskYYAWpQlx3PKmohy+rE2F+o5g=
github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z4KJ7DtFFCx8fJD2a491Ak=
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5 h1:h9qqTedYnA9JcWjKyLV6UYIMSdp91ExLCUbjbpDLH7A=
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5/go.mod h1:J8SS5Tp/zeLxaubB0xGfKnVrvssNBNLwTipreTKLhjQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 h1:3/gm/JTX9bX8CpzTgIlrtYpB3EVBDxyg/GY/QdcIEZw=
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11/go.mod h1:fmgDANqTUCxciViKl9hb/zD5LFbvPINFRgWhDbR+vZo=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4/go.mod h1:PJc8s+lxyU8rrre0/4a0pn2wgwiDvOEzoOjcJUBr67o=
github.com/aws/aws-sdk-go-v2/service/sns v1.17.4/go.mod h1:kElt+uCcXxcqFyc+bQqZPFD9DME/eC6oHBXvFzQ9Bcw=
github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3/go.mod h1:skmQo0UPvsjsuYYSYMVmrPc1HWCbHUJyrCEp+ZaLzqM=
@@ -176,12 +210,20 @@ github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1/go.mod h1:NR/xoKjdbRJ+qx0pMR4mI
github.com/aws/aws-sdk-go-v2/service/sso v1.11.3/go.mod h1:7UQ/e69kU7LDPtY40OyoHYgRmgfGM4mgsLYtcObdveU=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.17 h1:pXxu9u2z1UqSbjO9YA8kmFJBhFc1EVTDaf7A+S+Ivq8=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.17/go.mod h1:mS5xqLZc/6kc06IpXn5vRxdLaED+jEuaSRv5BxtnsiY=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 h1:pwvCchFUEnlceKIgPUouBJwK81aCkQ8UDMORfeFtW10=
github.com/aws/aws-sdk-go-v2/service/sso v1.11.23/go.mod h1:/w0eg9IhFGjGyyncHIQrXtU8wvNsTJOP0R6PPj0wf80=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5 h1:GUnZ62TevLqIoDyHeiWj2P7EqaosgakBKVvWriIdLQY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.5/go.mod h1:csZuQY65DAdFBt1oIjO5hhBR49kQqop4+lcuCjf2arA=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.3/go.mod h1:bfBj0iVmsUyUg4weDB4NxktD9rDGeKSVWnjTnwbx9b8=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.13 h1:dl8T0PJlN92rvEGOEUiD0+YPYdPEaCZK0TqHukvSfII=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.13/go.mod h1:Ru3QVMLygVs/07UQ3YDur1AQZZp2tUNje8wfloFttC0=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 h1:9pPi0PsFNAGILFfPCk8Y0iyEBGc6lu6OQ97U7hmdesg=
github.com/aws/aws-sdk-go-v2/service/sts v1.16.19/go.mod h1:h4J3oPZQbxLhzGnk+j9dfYHi5qIOVJ5kczZd658/ydM=
github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
github.com/aws/smithy-go v1.12.1 h1:yQRC55aXN/y1W10HgwHle01DRuV9Dpf31iGkotjt3Ag=
github.com/aws/smithy-go v1.12.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/aws/smithy-go v1.13.3 h1:l7LYxGuzK6/K+NzJ2mC+VvLUbae0sL3bXU//04MkmnA=
github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -581,6 +623,8 @@ golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to=
golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0 h1:a5Yg6ylndHHYJqIPrdq0AhvR6KTvDTAvgBtaidhEevY=
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -596,6 +640,8 @@ golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+o
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -672,6 +718,9 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes=
golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220921155015-db77216a4ee9 h1:SdDGdqRuKrF2R4XGcnPzcvZ63c/55GvhoHUus0o+BNI=
golang.org/x/net v0.0.0-20220921155015-db77216a4ee9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -696,6 +745,8 @@ golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA=
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -784,15 +835,20 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220919170432-7a66f970e087 h1:tPwmk4vmvVCMdr98VgL4JH+qZxPL8fqlUOHnyOM8N3w=
golang.org/x/term v0.0.0-20220919170432-7a66f970e087/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -810,6 +866,8 @@ golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45 h1:yuLAip3bfURHClMG9VBdzPrQvCWjWiWUTBGV+/fCbUs=
golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -880,6 +938,8 @@ golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -928,6 +988,8 @@ google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/api v0.94.0 h1:KtKM9ru3nzQioV1HLlUf1cR7vMYJIpgls5VhAYQXIwA=
google.golang.org/api v0.94.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
google.golang.org/api v0.96.0 h1:F60cuQPJq7K7FzsxMYHAUJSiXh2oKctHxBMbDygxhfM=
google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
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.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1032,6 +1094,8 @@ google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljW
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc h1:Nf+EdcTLHR8qDNN/KfkQL0u0ssxt9OhbaWCl5C0ucEI=
google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006 h1:mmbq5q8M1t7dhkLw320YK4PsOXm6jdnUAkErImaIqOg=
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1108,6 +1172,8 @@ lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg=
modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.39.0 h1:sd+UyMj63acEV1jaFqxFGPQfllSncJgL+roJjFlo6lI=
modernc.org/cc/v3 v3.39.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20=
modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM=
modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
@@ -1116,23 +1182,33 @@ modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.17.0 h1:nbL2Lv0I323wLc1GmTh/AqVtI9JeBVc7Nhapdg9EONs=
modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0=
modernc.org/libc v1.19.0 h1:bXyVhGQg6KIClTr8FMVIDPl7jtbcs7aS5WP7vLDaxPs=
modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.2.0 h1:zXehBrt9n+Pjn+4RoRCZ0KqRA/0ePFqcecxZ/hXCIVw=
modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.18.1 h1:ko32eKt3jf7eqIkCgPAeHMBXw3riNSLhl2f3loEF7o8=
modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4=
modernc.org/sqlite v1.19.1 h1:8xmS5oLnZtAK//vnd4aTVj8VOeTAccEFOtUnIzfSw+4=
modernc.org/sqlite v1.19.1/go.mod h1:UfQ83woKMaPW/ZBruK0T7YaFCrI+IE0LeWVY6pmnVms=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/strutil v1.1.2 h1:iFBDH6j1Z0bN/Q9udJnnFoFpENA4252qe/7/5woE5MI=
modernc.org/strutil v1.1.2/go.mod h1:OYajnUAcI/MX+XD/Wx7v1bbdvcQSvxgtb0gC+u3d3eg=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+1 -1
View File
@@ -133,7 +133,7 @@ func (f SchemaField) Validate() error {
// init field options (if not already)
f.InitOptions()
// add commonly used filter literals to the exlude names list
// add commonly used filter literals to the exclude names list
excludeNames := ReservedFieldNames()
excludeNames = append(excludeNames, "null", "true", "false")
+35 -28
View File
@@ -96,7 +96,7 @@ func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, plac
props := strings.Split(fieldName, ".")
currentCollectionName := r.baseCollection.Name
currentTableAlias := currentCollectionName
currentTableAlias := inflector.Columnify(currentCollectionName)
// check for @collection field (aka. non-relational join)
// must be in the format "@collection.COLLECTION_NAME.FIELD[.FIELD2....]"
@@ -106,14 +106,14 @@ func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, plac
}
currentCollectionName = props[1]
currentTableAlias = "__collection_" + currentCollectionName
currentTableAlias = inflector.Columnify("__collection_" + currentCollectionName)
collection, err := r.loadCollection(currentCollectionName)
if err != nil {
return "", nil, fmt.Errorf("Failed to load collection %q from field path %q.", currentCollectionName, fieldName)
}
r.addJoin(collection.Name, currentTableAlias, nil)
r.addJoin(inflector.Columnify(collection.Name), currentTableAlias, nil)
props = props[2:] // leave only the collection fields
} else if props[0] == "@request" {
@@ -123,13 +123,13 @@ func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, plac
}
// not a profile relational field
if len(props) <= 4 || !strings.HasPrefix(fieldName, "@request.user.profile.") {
if !strings.HasPrefix(fieldName, "@request.user.profile.") {
return r.resolveStaticRequestField(props[1:]...)
}
// resolve the profile collection fields
currentCollectionName = models.ProfileCollectionName
currentTableAlias = "__user_" + currentCollectionName
currentTableAlias = inflector.Columnify("__user_" + currentCollectionName)
collection, err := r.loadCollection(currentCollectionName)
if err != nil {
@@ -146,12 +146,16 @@ func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, plac
}
// join the profile collection
r.addJoin(collection.Name, currentTableAlias, dbx.NewExp(fmt.Sprintf(
// aka. profiles.id = profileId
"[[%s.id]] = %s",
inflector.Columnify(currentTableAlias),
profileIdPlaceholder,
), profileIdPlaceholderParam))
r.addJoin(
inflector.Columnify(collection.Name),
currentTableAlias,
dbx.NewExp(fmt.Sprintf(
// aka. profiles.id = profileId
"[[%s.id]] = %s",
currentTableAlias,
profileIdPlaceholder,
), profileIdPlaceholderParam),
)
props = props[3:] // leave only the profile fields
}
@@ -168,7 +172,7 @@ func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, plac
// base model prop (always available but not part of the collection schema)
if list.ExistInSlice(prop, baseModelFields) {
return fmt.Sprintf("[[%s.%s]]", inflector.Columnify(currentTableAlias), inflector.Columnify(prop)), nil, nil
return fmt.Sprintf("[[%s.%s]]", currentTableAlias, inflector.Columnify(prop)), nil, nil
}
field := collection.Schema.GetFieldByName(prop)
@@ -178,7 +182,7 @@ func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, plac
// last prop
if i == totalProps-1 {
return fmt.Sprintf("[[%s.%s]]", inflector.Columnify(currentTableAlias), inflector.Columnify(prop)), nil, nil
return fmt.Sprintf("[[%s.%s]]", currentTableAlias, inflector.Columnify(prop)), nil, nil
}
// check if it is a relation field
@@ -198,20 +202,27 @@ func (r *RecordFieldResolver) Resolve(fieldName string) (resultName string, plac
if relErr != nil {
return "", nil, fmt.Errorf("Failed to find field %q collection.", prop)
}
cleanFieldName := inflector.Columnify(field.Name)
newCollectionName := relCollection.Name
newTableAlias := currentTableAlias + "_" + field.Name
newTableAlias := currentTableAlias + "_" + cleanFieldName
jeTable := currentTableAlias + "_" + cleanFieldName + "_je"
jePair := currentTableAlias + "." + cleanFieldName
r.addJoin(
newCollectionName,
fmt.Sprintf(
// note: the case is used to normalize value access for single and multiple relations.
`json_each(CASE WHEN json_valid([[%s]]) THEN [[%s]] ELSE json_array([[%s]]) END)`,
jePair, jePair, jePair,
),
jeTable,
nil,
)
r.addJoin(
inflector.Columnify(newCollectionName),
newTableAlias,
dbx.NewExp(fmt.Sprintf(
// 'LIKE' expr is used to handle the case when the reference field supports multiple values (aka. is json array)
"[[%s.%s]] LIKE ('%%' || [[%s.%s]] || '%%')",
inflector.Columnify(currentTableAlias),
inflector.Columnify(field.Name),
inflector.Columnify(newTableAlias),
inflector.Columnify("id"),
)),
dbx.NewExp(fmt.Sprintf("[[%s.id]] = [[%s.value]]", newTableAlias, jeTable)),
)
currentCollectionName = newCollectionName
@@ -296,11 +307,7 @@ func (r *RecordFieldResolver) loadCollection(collectionNameOrId string) (*models
}
func (r *RecordFieldResolver) addJoin(tableName string, tableAlias string, on dbx.Expression) {
tableExpr := fmt.Sprintf(
"%s %s",
inflector.Columnify(tableName),
inflector.Columnify(tableAlias),
)
tableExpr := fmt.Sprintf("%s %s", tableName, tableAlias)
join := join{
id: tableAlias,
+14 -12
View File
@@ -52,32 +52,32 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
{
"single rel",
[]string{"onerel.title"},
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `demo4_onerel` ON [[demo4.onerel]] LIKE ('%' || [[demo4_onerel.id]] || '%')",
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN json_each(CASE WHEN json_valid([[demo4.onerel]]) THEN [[demo4.onerel]] ELSE json_array([[demo4.onerel]]) END) `demo4_onerel_je` LEFT JOIN `demo4` `demo4_onerel` ON [[demo4_onerel.id]] = [[demo4_onerel_je.value]]",
},
{
"non-relation field + single rel",
[]string{"title", "onerel.title"},
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `demo4_onerel` ON [[demo4.onerel]] LIKE ('%' || [[demo4_onerel.id]] || '%')",
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN json_each(CASE WHEN json_valid([[demo4.onerel]]) THEN [[demo4.onerel]] ELSE json_array([[demo4.onerel]]) END) `demo4_onerel_je` LEFT JOIN `demo4` `demo4_onerel` ON [[demo4_onerel.id]] = [[demo4_onerel_je.value]]",
},
{
"nested incomplete rels",
[]string{"manyrels.onerel"},
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4.manyrels]] LIKE ('%' || [[demo4_manyrels.id]] || '%')",
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN json_each(CASE WHEN json_valid([[demo4.manyrels]]) THEN [[demo4.manyrels]] ELSE json_array([[demo4.manyrels]]) END) `demo4_manyrels_je` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4_manyrels.id]] = [[demo4_manyrels_je.value]]",
},
{
"nested complete rels",
[]string{"manyrels.onerel.title"},
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4.manyrels]] LIKE ('%' || [[demo4_manyrels.id]] || '%') LEFT JOIN `demo4` `demo4_manyrels_onerel` ON [[demo4_manyrels.onerel]] LIKE ('%' || [[demo4_manyrels_onerel.id]] || '%')",
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN json_each(CASE WHEN json_valid([[demo4.manyrels]]) THEN [[demo4.manyrels]] ELSE json_array([[demo4.manyrels]]) END) `demo4_manyrels_je` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4_manyrels.id]] = [[demo4_manyrels_je.value]] LEFT JOIN json_each(CASE WHEN json_valid([[demo4_manyrels.onerel]]) THEN [[demo4_manyrels.onerel]] ELSE json_array([[demo4_manyrels.onerel]]) END) `demo4_manyrels_onerel_je` LEFT JOIN `demo4` `demo4_manyrels_onerel` ON [[demo4_manyrels_onerel.id]] = [[demo4_manyrels_onerel_je.value]]",
},
{
"repeated nested rels",
[]string{"manyrels.onerel.manyrels.onerel.title"},
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4.manyrels]] LIKE ('%' || [[demo4_manyrels.id]] || '%') LEFT JOIN `demo4` `demo4_manyrels_onerel` ON [[demo4_manyrels.onerel]] LIKE ('%' || [[demo4_manyrels_onerel.id]] || '%') LEFT JOIN `demo4` `demo4_manyrels_onerel_manyrels` ON [[demo4_manyrels_onerel.manyrels]] LIKE ('%' || [[demo4_manyrels_onerel_manyrels.id]] || '%') LEFT JOIN `demo4` `demo4_manyrels_onerel_manyrels_onerel` ON [[demo4_manyrels_onerel_manyrels.onerel]] LIKE ('%' || [[demo4_manyrels_onerel_manyrels_onerel.id]] || '%')",
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN json_each(CASE WHEN json_valid([[demo4.manyrels]]) THEN [[demo4.manyrels]] ELSE json_array([[demo4.manyrels]]) END) `demo4_manyrels_je` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4_manyrels.id]] = [[demo4_manyrels_je.value]] LEFT JOIN json_each(CASE WHEN json_valid([[demo4_manyrels.onerel]]) THEN [[demo4_manyrels.onerel]] ELSE json_array([[demo4_manyrels.onerel]]) END) `demo4_manyrels_onerel_je` LEFT JOIN `demo4` `demo4_manyrels_onerel` ON [[demo4_manyrels_onerel.id]] = [[demo4_manyrels_onerel_je.value]] LEFT JOIN json_each(CASE WHEN json_valid([[demo4_manyrels_onerel.manyrels]]) THEN [[demo4_manyrels_onerel.manyrels]] ELSE json_array([[demo4_manyrels_onerel.manyrels]]) END) `demo4_manyrels_onerel_manyrels_je` LEFT JOIN `demo4` `demo4_manyrels_onerel_manyrels` ON [[demo4_manyrels_onerel_manyrels.id]] = [[demo4_manyrels_onerel_manyrels_je.value]] LEFT JOIN json_each(CASE WHEN json_valid([[demo4_manyrels_onerel_manyrels.onerel]]) THEN [[demo4_manyrels_onerel_manyrels.onerel]] ELSE json_array([[demo4_manyrels_onerel_manyrels.onerel]]) END) `demo4_manyrels_onerel_manyrels_onerel_je` LEFT JOIN `demo4` `demo4_manyrels_onerel_manyrels_onerel` ON [[demo4_manyrels_onerel_manyrels_onerel.id]] = [[demo4_manyrels_onerel_manyrels_onerel_je.value]]",
},
{
"multiple rels",
[]string{"manyrels.title", "onerel.onefile"},
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4.manyrels]] LIKE ('%' || [[demo4_manyrels.id]] || '%') LEFT JOIN `demo4` `demo4_onerel` ON [[demo4.onerel]] LIKE ('%' || [[demo4_onerel.id]] || '%')",
"SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN json_each(CASE WHEN json_valid([[demo4.manyrels]]) THEN [[demo4.manyrels]] ELSE json_array([[demo4.manyrels]]) END) `demo4_manyrels_je` LEFT JOIN `demo4` `demo4_manyrels` ON [[demo4_manyrels.id]] = [[demo4_manyrels_je.value]] LEFT JOIN json_each(CASE WHEN json_valid([[demo4.onerel]]) THEN [[demo4.onerel]] ELSE json_array([[demo4.onerel]]) END) `demo4_onerel_je` LEFT JOIN `demo4` `demo4_onerel` ON [[demo4_onerel.id]] = [[demo4_onerel_je.value]]",
},
{
"@collection join",
@@ -87,7 +87,9 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
{
"static @request.user.profile fields",
[]string{"@request.user.id", "@request.user.profile.id", "@request.data.demo"},
"SELECT `demo4`.* FROM `demo4`",
"^" +
regexp.QuoteMeta("SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `profiles` `__user_profiles` ON [[__user_profiles.id]] =") +
" {:.*}$",
},
{
"relational @request.user.profile fields",
@@ -95,12 +97,12 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
"^" +
regexp.QuoteMeta("SELECT DISTINCT `demo4`.* FROM `demo4` LEFT JOIN `profiles` `__user_profiles` ON [[__user_profiles.id]] =") +
" {:.*} " +
regexp.QuoteMeta("LEFT JOIN `profiles` `__user_profiles_rel` ON [[__user_profiles.rel]] LIKE ('%' || [[__user_profiles_rel.id]] || '%')") +
regexp.QuoteMeta("LEFT JOIN json_each(CASE WHEN json_valid([[__user_profiles.rel]]) THEN [[__user_profiles.rel]] ELSE json_array([[__user_profiles.rel]]) END) `__user_profiles_rel_je` LEFT JOIN `profiles` `__user_profiles_rel` ON [[__user_profiles_rel.id]] = [[__user_profiles_rel_je.value]]") +
"$",
},
}
for i, s := range scenarios {
for _, s := range scenarios {
query := app.Dao().RecordQuery(collection)
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestData)
@@ -109,14 +111,14 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
}
if err := r.UpdateQuery(query); err != nil {
t.Errorf("(%d) UpdateQuery failed with error %v", i, err)
t.Errorf("(%s) UpdateQuery failed with error %v", s.name, err)
continue
}
rawQuery := query.Build().SQL()
if !list.ExistInSliceWithRegex(rawQuery, []string{s.expectQuery}) {
t.Errorf("(%d) Expected query\n %v \ngot:\n %v", i, s.expectQuery, rawQuery)
t.Errorf("(%s) Expected query\n %v \ngot:\n %v", s.name, s.expectQuery, rawQuery)
}
}
}
@@ -161,6 +163,7 @@ func TestRecordFieldResolverResolveSchemaFields(t *testing.T) {
{"manyrels.title", false, "[[demo4_manyrels.title]]"},
{"manyrels.onerel.manyrels.onefile", false, "[[demo4_manyrels_onerel_manyrels.onefile]]"},
// @request.user.profile relation join:
{"@request.user.profile.rel", false, "[[__user_profiles.rel]]"},
{"@request.user.profile.rel.name", false, "[[__user_profiles_rel.name]]"},
// @collection fieds:
{"@collect", true, ""},
@@ -247,7 +250,6 @@ func TestRecordFieldResolverResolveStaticRequestDataFields(t *testing.T) {
{"@request.user", true, ""},
{"@request.user.id", false, `"4d0197cc-2b4a-3f83-a26b-d77bc8423d3c"`},
{"@request.user.profile", false, `"{\"id\":\"d13f60a4-5765-48c7-9e1d-3e782340f833\",\"name\":\"test\"}"`},
{"@request.user.profile.name", false, `"test"`},
}
for i, s := range scenarios {
Binary file not shown.
+26 -9
View File
@@ -196,6 +196,12 @@ var inlineServeContentTypes = []string{
"application/pdf", "application/x-pdf",
}
// manualExtensionContentTypes is a map of file extensions to content types.
var manualExtensionContentTypes = map[string]string{
".svg": "image/svg+xml", // (see https://github.com/whatwg/mimesniff/issues/7)
".css": "text/css", // (see https://github.com/gabriel-vasile/mimetype/pull/113)
}
// Serve serves the file at fileKey location to an HTTP response.
func (s *System) Serve(response http.ResponseWriter, fileKey string, name string) error {
r, readErr := s.bucket.NewReader(s.ctx, fileKey, nil)
@@ -210,18 +216,22 @@ func (s *System) Serve(response http.ResponseWriter, fileKey string, name string
disposition = "inline"
}
// make an exception for svg and force a custom content type
// to send in the response so that it can be loaded in an img tag
// (see https://github.com/whatwg/mimesniff/issues/7)
// make an exception for specific content types and force a custom
// content type to send in the response so that it can be loaded directly
extContentType := realContentType
if extContentType != "image/svg+xml" && filepath.Ext(name) == ".svg" {
extContentType = "image/svg+xml"
if ct, found := manualExtensionContentTypes[filepath.Ext(name)]; found && extContentType != ct {
extContentType = ct
}
// clickjacking shouldn't be a concern when serving uploaded files,
// so it safe to unset the global X-Frame-Options to allow files embedding
// (see https://github.com/pocketbase/pocketbase/issues/677)
response.Header().Del("X-Frame-Options")
response.Header().Set("Content-Disposition", disposition+"; filename="+name)
response.Header().Set("Content-Type", extContentType)
response.Header().Set("Content-Length", strconv.FormatInt(r.Size(), 10))
response.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
response.Header().Set("Content-Security-Policy", "default-src 'none'; media-src 'self'; style-src 'unsafe-inline'; sandbox")
// All HTTP date/time stamps MUST be represented in Greenwich Mean Time (GMT)
// (see https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1)
@@ -271,8 +281,8 @@ func (s *System) CreateThumb(originalKey string, thumbKey, thumbSize string) err
}
defer r.Close()
// create imaging object from the origial reader
img, decodeErr := imaging.Decode(r)
// create imaging object from the original reader
img, decodeErr := imaging.Decode(r, imaging.AutoOrientation(true))
if decodeErr != nil {
return decodeErr
}
@@ -305,8 +315,15 @@ func (s *System) CreateThumb(originalKey string, thumbKey, thumbSize string) err
return writerErr
}
// try to detect the thumb format based on the original file name
// (fallbacks to png on error)
format, err := imaging.FormatFromFilename(thumbKey)
if err != nil {
format = imaging.PNG
}
// thumb encode (aka. upload)
if err := imaging.Encode(w, thumbImg, imaging.PNG); err != nil {
if err := imaging.Encode(w, thumbImg, format); err != nil {
w.Close()
return err
}
+26 -4
View File
@@ -180,7 +180,7 @@ func TestFileSystemServe(t *testing.T) {
"Content-Disposition": "attachment; filename=test_name.txt",
"Content-Type": "application/octet-stream",
"Content-Length": "0",
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
"Content-Security-Policy": "default-src 'none'; media-src 'self'; style-src 'unsafe-inline'; sandbox",
},
},
{
@@ -192,7 +192,7 @@ func TestFileSystemServe(t *testing.T) {
"Content-Disposition": "inline; filename=test_name.png",
"Content-Type": "image/png",
"Content-Length": "73",
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
"Content-Security-Policy": "default-src 'none'; media-src 'self'; style-src 'unsafe-inline'; sandbox",
},
},
{
@@ -204,7 +204,19 @@ func TestFileSystemServe(t *testing.T) {
"Content-Disposition": "attachment; filename=test_name.svg",
"Content-Type": "image/svg+xml",
"Content-Length": "0",
"Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
"Content-Security-Policy": "default-src 'none'; media-src 'self'; style-src 'unsafe-inline'; sandbox",
},
},
{
// css exception
"style.css",
"test_name.css",
false,
map[string]string{
"Content-Disposition": "attachment; filename=test_name.css",
"Content-Type": "text/css",
"Content-Length": "0",
"Content-Security-Policy": "default-src 'none'; media-src 'self'; style-src 'unsafe-inline'; sandbox",
},
},
}
@@ -216,7 +228,7 @@ func TestFileSystemServe(t *testing.T) {
hasErr := err != nil
if hasErr != scenario.expectError {
t.Errorf("(%s) Expected hasError %v, got %v", scenario.path, scenario.expectError, hasErr)
t.Errorf("(%s) Expected hasError %v, got %v (%v)", scenario.path, scenario.expectError, hasErr, err)
continue
}
@@ -228,6 +240,10 @@ func TestFileSystemServe(t *testing.T) {
t.Errorf("(%s) Expected value %q for header %q, got %q", scenario.path, hValue, hName, v)
}
}
if v := result.Header.Get("X-Frame-Options"); v != "" {
t.Errorf("(%s) Expected the X-Frame-Options header to be unset, got %v", scenario.path, v)
}
}
}
@@ -321,5 +337,11 @@ func createTestDir(t *testing.T) string {
}
file4.Close()
file5, err := os.OpenFile(filepath.Join(dir, "style.css"), os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatal(err)
}
file5.Close()
return dir
}
+12 -1
View File
@@ -17,7 +17,18 @@ type Hook[T any] struct {
handlers []Handler[T]
}
// Add registers a new handler to the hook.
// PreAdd registers a new handler to the hook by prepending it to the existing queue.
func (h *Hook[T]) PreAdd(fn Handler[T]) {
h.mux.Lock()
defer h.mux.Unlock()
// minimize allocations by shifting the slice
h.handlers = append(h.handlers, nil)
copy(h.handlers[1:], h.handlers)
h.handlers[0] = fn
}
// Add registers a new handler to the hook by appending it to the existing queue.
func (h *Hook[T]) Add(fn Handler[T]) {
h.mux.Lock()
defer h.mux.Unlock()
+21 -5
View File
@@ -5,18 +5,34 @@ import (
"testing"
)
func TestAdd(t *testing.T) {
func TestAddAndPreAdd(t *testing.T) {
h := Hook[int]{}
if total := len(h.handlers); total != 0 {
t.Fatalf("Expected no handlers, found %d", total)
}
h.Add(func(data int) error { return nil })
h.Add(func(data int) error { return nil })
triggerSequence := ""
if total := len(h.handlers); total != 2 {
t.Fatalf("Expected 2 handlers, found %d", total)
f1 := func(data int) error { triggerSequence += "f1"; return nil }
f2 := func(data int) error { triggerSequence += "f2"; return nil }
f3 := func(data int) error { triggerSequence += "f3"; return nil }
f4 := func(data int) error { triggerSequence += "f4"; return nil }
h.Add(f1)
h.Add(f2)
h.PreAdd(f3)
h.PreAdd(f4)
h.Trigger(1)
if total := len(h.handlers); total != 4 {
t.Fatalf("Expected %d handlers, found %d", 4, total)
}
expectedTriggerSequence := "f4f3f1f2"
if triggerSequence != expectedTriggerSequence {
t.Fatalf("Expected trigger sequence %s, got %s", expectedTriggerSequence, triggerSequence)
}
}
+15 -2
View File
@@ -13,16 +13,17 @@ import (
"github.com/spf13/cast"
)
// FilterData is a filter expession string following the `fexpr` package grammar.
// FilterData is a filter expression string following the `fexpr` package grammar.
//
// Example:
//
// var filter FilterData = "id = null || (name = 'test' && status = true)"
// resolver := search.NewSimpleFieldResolver("id", "name", "status")
// expr, err := filter.BuildExpr(resolver)
type FilterData string
// parsedFilterData holds a cache with previously parsed filter data expressions
// (initialized with some prealocated empty data map)
// (initialized with some preallocated empty data map)
var parsedFilterData = store.New(make(map[string][]fexpr.ExprGroup, 50))
// BuildExpr parses the current filter data and returns a new db WHERE expression.
@@ -103,16 +104,28 @@ func (f FilterData) resolveTokenizedExpr(expr fexpr.Expr, fieldResolver FieldRes
case fexpr.SignNeq:
return dbx.NewExp(fmt.Sprintf("COALESCE(%s, '') != COALESCE(%s, '')", lName, rName), params), nil
case fexpr.SignLike:
// both sides are columns and therefore wrap the right side with "%" for contains like behavior
if len(params) == 0 {
return dbx.NewExp(fmt.Sprintf("%s LIKE ('%%' || %s || '%%')", lName, rName), params), nil
}
// normalize operands and switch sides if the left operand is a number or text
if len(lParams) > 0 {
return dbx.NewExp(fmt.Sprintf("%s LIKE %s", rName, lName), f.normalizeLikeParams(params)), nil
}
return dbx.NewExp(fmt.Sprintf("%s LIKE %s", lName, rName), f.normalizeLikeParams(params)), nil
case fexpr.SignNlike:
// both sides are columns and therefore wrap the right side with "%" for not-contains like behavior
if len(params) == 0 {
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE ('%%' || %s || '%%')", lName, rName), params), nil
}
// normalize operands and switch sides if the left operand is a number or text
if len(lParams) > 0 {
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s", rName, lName), f.normalizeLikeParams(params)), nil
}
return dbx.NewExp(fmt.Sprintf("%s NOT LIKE %s", lName, rName), f.normalizeLikeParams(params)), nil
case fexpr.SignLt:
return dbx.NewExp(fmt.Sprintf("%s < %s", lName, rName), params), nil
+28
View File
@@ -32,6 +32,34 @@ func TestFilterDataBuildExpr(t *testing.T) {
regexp.QuoteMeta("}") +
"$",
},
// like with 2 columns
{"test1 ~ test2", false,
"^" +
regexp.QuoteMeta("[[test1]] LIKE ('%' || [[test2]] || '%')") +
"$",
},
// reversed like with text
{"'lorem' ~ test1", false,
"^" +
regexp.QuoteMeta("[[test1]] LIKE {:") +
".+" +
regexp.QuoteMeta("}") +
"$",
},
// not like with 2 columns
{"test1 !~ test2", false,
"^" +
regexp.QuoteMeta("[[test1]] NOT LIKE ('%' || [[test2]] || '%')") +
"$",
},
// reversed not like with text
{"'lorem' !~ test1", false,
"^" +
regexp.QuoteMeta("[[test1]] NOT LIKE {:") +
".+" +
regexp.QuoteMeta("}") +
"$",
},
// current datetime constant
{"test1 > @now", false,
"^" +
+4 -3
View File
@@ -13,7 +13,7 @@ import (
const DefaultPerPage int = 30
// MaxPerPage specifies the maximum allowed search result items returned in a single page.
const MaxPerPage int = 200
const MaxPerPage int = 400
// url search query params
const (
@@ -46,6 +46,7 @@ type Provider struct {
// NewProvider creates and returns a new search provider.
//
// Example:
//
// baseQuery := db.Select("*").From("user")
// fieldResolver := search.NewSimpleFieldResolver("id", "name")
// models := []*YourDataStruct{}
@@ -199,7 +200,7 @@ func (s *Provider) Exec(items any) (*Result, error) {
// count
var totalCount int64
countQuery := modelsQuery
countQuery.Distinct(false).Select("COUNT(*)")
countQuery.Distinct(false).Select("COUNT(*)").OrderBy() // unset ORDER BY statements
if s.countColumn != "" {
countQuery.Select("COUNT(DISTINCT(" + s.countColumn + "))")
}
@@ -241,7 +242,7 @@ func (s *Provider) Exec(items any) (*Result, error) {
}, nil
}
// ParseAndExec is a short conventient method to trigger both
// ParseAndExec is a short convenient method to trigger both
// `Parse()` and `Exec()` in a single call.
func (s *Provider) ParseAndExec(urlQuery string, modelsSlice any) (*Result, error) {
if err := s.Parse(urlQuery); err != nil {
+9 -9
View File
@@ -238,7 +238,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":10,"totalItems":2,"totalPages":1,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 10",
},
},
@@ -252,7 +252,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":30,"totalItems":2,"totalPages":1,"items":[{"test1":1,"test2":"test2.1","test3":""},{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 30",
},
},
@@ -288,8 +288,8 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":` + fmt.Sprint(MaxPerPage) + `,"totalItems":1,"totalPages":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (COALESCE(test2, '') != COALESCE(null, ''))) AND (test1 >= 2) ORDER BY `test1` ASC, `test2` DESC",
"SELECT * FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (COALESCE(test2, '') != COALESCE(null, ''))) AND (test1 >= 2) ORDER BY `test1` ASC, `test2` DESC LIMIT 200",
"SELECT COUNT(*) FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (COALESCE(test2, '') != COALESCE(null, ''))) AND (test1 >= 2)",
"SELECT * FROM `test` WHERE ((NOT (`test1` IS NULL)) AND (COALESCE(test2, '') != COALESCE(null, ''))) AND (test1 >= 2) ORDER BY `test1` ASC, `test2` DESC LIMIT 400",
},
},
// valid sort and filter fields (zero results)
@@ -302,7 +302,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":1,"perPage":10,"totalItems":0,"totalPages":0,"items":[]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE (NOT (`test1` IS NULL)) AND (COALESCE(test3, '') != COALESCE('', '')) ORDER BY `test1` ASC, `test3` ASC",
"SELECT COUNT(*) FROM `test` WHERE (NOT (`test1` IS NULL)) AND (COALESCE(test3, '') != COALESCE('', ''))",
"SELECT * FROM `test` WHERE (NOT (`test1` IS NULL)) AND (COALESCE(test3, '') != COALESCE('', '')) ORDER BY `test1` ASC, `test3` ASC LIMIT 10",
},
},
@@ -316,7 +316,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":2,"perPage":1,"totalItems":2,"totalPages":2,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT COUNT(*) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 1 OFFSET 1",
},
},
@@ -330,7 +330,7 @@ func TestProviderExecNonEmptyQuery(t *testing.T) {
false,
`{"page":2,"perPage":1,"totalItems":2,"totalPages":2,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
[]string{
"SELECT COUNT(DISTINCT(test.test1)) FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC",
"SELECT COUNT(DISTINCT(test.test1)) FROM `test` WHERE NOT (`test1` IS NULL)",
"SELECT * FROM `test` WHERE NOT (`test1` IS NULL) ORDER BY `test1` ASC LIMIT 1 OFFSET 1",
},
},
@@ -437,9 +437,9 @@ func TestProviderParseAndExec(t *testing.T) {
},
// valid query params
{
"page=3&perPage=555&filter=test1>1&sort=-test2,test3",
"page=3&perPage=9999&filter=test1>1&sort=-test2,test3",
false,
`{"page":1,"perPage":200,"totalItems":1,"totalPages":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
`{"page":1,"perPage":400,"totalItems":1,"totalPages":1,"items":[{"test1":2,"test2":"test2.2","test3":""}]}`,
},
}
+1 -1
View File
@@ -107,7 +107,7 @@ func (c *DefaultClient) Unsubscribe(subs ...string) {
delete(c.subscriptions, s)
}
} else {
// unsubsribe all
// unsubscribe all
for s := range c.subscriptions {
delete(c.subscriptions, s)
}
+1 -1
View File
@@ -40,7 +40,7 @@ func (d DateTime) IsZero() bool {
return d.Time().IsZero()
}
// String serializes the current DateTime instance into a formated
// String serializes the current DateTime instance into a formatted
// UTC date string.
//
// The zero value is serialized to an empty string.
+1 -1
View File
@@ -13,7 +13,7 @@ type JsonArray []any
func (m JsonArray) MarshalJSON() ([]byte, error) {
type alias JsonArray // prevent recursion
// inialize an empty map to ensure that `[]` is returned as json
// initialize an empty map to ensure that `[]` is returned as json
if m == nil {
m = JsonArray{}
}
+1 -1
View File
@@ -13,7 +13,7 @@ type JsonMap map[string]any
func (m JsonMap) MarshalJSON() ([]byte, error) {
type alias JsonMap // prevent recursion
// inialize an empty map to ensure that `{}` is returned as json
// initialize an empty map to ensure that `{}` is returned as json
if m == nil {
m = JsonMap{}
}
+1 -1
View File
@@ -4,4 +4,4 @@ PB_PROFILE_COLLECTION = "profiles"
PB_INSTALLER_PARAM = "installer"
PB_RULES_SYNTAX_DOCS = "https://pocketbase.io/docs/manage-collections#rules-filters-syntax"
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
PB_VERSION = "v0.7.2"
PB_VERSION = "v0.7.10"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
import{S as E,i as G,s as I,F as K,c as A,m as B,t as H,a as N,d as T,C as M,q as J,e as c,w as q,b as k,f as u,r as L,g as b,h as _,u as h,v as O,j as Q,l as U,o as w,A as V,p as W,B as X,D as Y,x as Z,z as S}from"./index.994990a4.js";function y(f){let e,o,s;return{c(){e=q("for "),o=c("strong"),s=q(f[3]),u(o,"class","txt-nowrap")},m(l,t){b(l,e,t),b(l,o,t),_(o,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&w(e),l&&w(o)}}}function x(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password"),l=k(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0,t.autofocus=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[0]),t.focus(),p||(d=h(t,"input",f[6]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&1&&t.value!==n[0]&&S(t,n[0])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function ee(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password confirm"),l=k(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[1]),p||(d=h(t,"input",f[7]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&2&&t.value!==n[1]&&S(t,n[1])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function te(f){let e,o,s,l,t,r,p,d,n,i,g,R,C,v,P,F,j,m=f[3]&&y(f);return r=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),d=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),{c(){e=c("form"),o=c("div"),s=c("h4"),l=q(`Reset your admin password
`),m&&m.c(),t=k(),A(r.$$.fragment),p=k(),A(d.$$.fragment),n=k(),i=c("button"),g=c("span"),g.textContent="Set new password",R=k(),C=c("div"),v=c("a"),v.textContent="Back to login",u(s,"class","m-b-xs"),u(o,"class","content txt-center m-b-sm"),u(g,"class","txt"),u(i,"type","submit"),u(i,"class","btn btn-lg btn-block"),i.disabled=f[2],L(i,"btn-loading",f[2]),u(e,"class","m-b-base"),u(v,"href","/login"),u(v,"class","link-hint"),u(C,"class","content txt-center")},m(a,$){b(a,e,$),_(e,o),_(o,s),_(s,l),m&&m.m(s,null),_(e,t),B(r,e,null),_(e,p),B(d,e,null),_(e,n),_(e,i),_(i,g),b(a,R,$),b(a,C,$),_(C,v),P=!0,F||(j=[h(e,"submit",O(f[4])),Q(U.call(null,v))],F=!0)},p(a,$){a[3]?m?m.p(a,$):(m=y(a),m.c(),m.m(s,null)):m&&(m.d(1),m=null);const z={};$&769&&(z.$$scope={dirty:$,ctx:a}),r.$set(z);const D={};$&770&&(D.$$scope={dirty:$,ctx:a}),d.$set(D),(!P||$&4)&&(i.disabled=a[2]),$&4&&L(i,"btn-loading",a[2])},i(a){P||(H(r.$$.fragment,a),H(d.$$.fragment,a),P=!0)},o(a){N(r.$$.fragment,a),N(d.$$.fragment,a),P=!1},d(a){a&&w(e),m&&m.d(),T(r),T(d),a&&w(R),a&&w(C),F=!1,V(j)}}}function se(f){let e,o;return e=new K({props:{$$slots:{default:[te]},$$scope:{ctx:f}}}),{c(){A(e.$$.fragment)},m(s,l){B(e,s,l),o=!0},p(s,[l]){const t={};l&527&&(t.$$scope={dirty:l,ctx:s}),e.$set(t)},i(s){o||(H(e.$$.fragment,s),o=!0)},o(s){N(e.$$.fragment,s),o=!1},d(s){T(e,s)}}}function le(f,e,o){let s,{params:l}=e,t="",r="",p=!1;async function d(){if(!p){o(2,p=!0);try{await W.admins.confirmPasswordReset(l==null?void 0:l.token,t,r),X("Successfully set a new admin password."),Y("/")}catch(g){W.errorResponseHandler(g)}o(2,p=!1)}}function n(){t=this.value,o(0,t)}function i(){r=this.value,o(1,r)}return f.$$set=g=>{"params"in g&&o(5,l=g.params)},f.$$.update=()=>{f.$$.dirty&32&&o(3,s=M.getJWTPayload(l==null?void 0:l.token).email||"")},[t,r,p,s,d,l,n,i]}class ae extends E{constructor(e){super(),G(this,e,le,se,I,{params:5})}}export{ae as default};
import{S as E,i as G,s as I,F as K,c as A,m as B,t as H,a as N,d as T,C as M,q as J,e as c,w as q,b as C,f as u,r as L,g as b,h as _,u as h,v as O,j as Q,l as U,o as w,A as V,p as W,B as X,D as Y,x as Z,z as S}from"./index.21c960f7.js";function y(f){let e,o,s;return{c(){e=q("for "),o=c("strong"),s=q(f[3]),u(o,"class","txt-nowrap")},m(l,t){b(l,e,t),b(l,o,t),_(o,s)},p(l,t){t&8&&Z(s,l[3])},d(l){l&&w(e),l&&w(o)}}}function x(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0,t.autofocus=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[0]),t.focus(),p||(d=h(t,"input",f[6]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&1&&t.value!==n[0]&&S(t,n[0])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function ee(f){let e,o,s,l,t,r,p,d;return{c(){e=c("label"),o=q("New password confirm"),l=C(),t=c("input"),u(e,"for",s=f[8]),u(t,"type","password"),u(t,"id",r=f[8]),t.required=!0},m(n,i){b(n,e,i),_(e,o),b(n,l,i),b(n,t,i),S(t,f[1]),p||(d=h(t,"input",f[7]),p=!0)},p(n,i){i&256&&s!==(s=n[8])&&u(e,"for",s),i&256&&r!==(r=n[8])&&u(t,"id",r),i&2&&t.value!==n[1]&&S(t,n[1])},d(n){n&&w(e),n&&w(l),n&&w(t),p=!1,d()}}}function te(f){let e,o,s,l,t,r,p,d,n,i,g,R,P,v,k,F,j,m=f[3]&&y(f);return r=new J({props:{class:"form-field required",name:"password",$$slots:{default:[x,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),d=new J({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[ee,({uniqueId:a})=>({8:a}),({uniqueId:a})=>a?256:0]},$$scope:{ctx:f}}}),{c(){e=c("form"),o=c("div"),s=c("h4"),l=q(`Reset your admin password
`),m&&m.c(),t=C(),A(r.$$.fragment),p=C(),A(d.$$.fragment),n=C(),i=c("button"),g=c("span"),g.textContent="Set new password",R=C(),P=c("div"),v=c("a"),v.textContent="Back to login",u(s,"class","m-b-xs"),u(o,"class","content txt-center m-b-sm"),u(g,"class","txt"),u(i,"type","submit"),u(i,"class","btn btn-lg btn-block"),i.disabled=f[2],L(i,"btn-loading",f[2]),u(e,"class","m-b-base"),u(v,"href","/login"),u(v,"class","link-hint"),u(P,"class","content txt-center")},m(a,$){b(a,e,$),_(e,o),_(o,s),_(s,l),m&&m.m(s,null),_(e,t),B(r,e,null),_(e,p),B(d,e,null),_(e,n),_(e,i),_(i,g),b(a,R,$),b(a,P,$),_(P,v),k=!0,F||(j=[h(e,"submit",O(f[4])),Q(U.call(null,v))],F=!0)},p(a,$){a[3]?m?m.p(a,$):(m=y(a),m.c(),m.m(s,null)):m&&(m.d(1),m=null);const z={};$&769&&(z.$$scope={dirty:$,ctx:a}),r.$set(z);const D={};$&770&&(D.$$scope={dirty:$,ctx:a}),d.$set(D),(!k||$&4)&&(i.disabled=a[2]),(!k||$&4)&&L(i,"btn-loading",a[2])},i(a){k||(H(r.$$.fragment,a),H(d.$$.fragment,a),k=!0)},o(a){N(r.$$.fragment,a),N(d.$$.fragment,a),k=!1},d(a){a&&w(e),m&&m.d(),T(r),T(d),a&&w(R),a&&w(P),F=!1,V(j)}}}function se(f){let e,o;return e=new K({props:{$$slots:{default:[te]},$$scope:{ctx:f}}}),{c(){A(e.$$.fragment)},m(s,l){B(e,s,l),o=!0},p(s,[l]){const t={};l&527&&(t.$$scope={dirty:l,ctx:s}),e.$set(t)},i(s){o||(H(e.$$.fragment,s),o=!0)},o(s){N(e.$$.fragment,s),o=!1},d(s){T(e,s)}}}function le(f,e,o){let s,{params:l}=e,t="",r="",p=!1;async function d(){if(!p){o(2,p=!0);try{await W.admins.confirmPasswordReset(l==null?void 0:l.token,t,r),X("Successfully set a new admin password."),Y("/")}catch(g){W.errorResponseHandler(g)}o(2,p=!1)}}function n(){t=this.value,o(0,t)}function i(){r=this.value,o(1,r)}return f.$$set=g=>{"params"in g&&o(5,l=g.params)},f.$$.update=()=>{f.$$.dirty&32&&o(3,s=M.getJWTPayload(l==null?void 0:l.token).email||"")},[t,r,p,s,d,l,n,i]}class ae extends E{constructor(e){super(),G(this,e,le,se,I,{params:5})}}export{ae as default};
@@ -0,0 +1,2 @@
import{S as M,i as T,s as j,F as z,c as H,m as L,t as w,a as y,d as S,b as g,e as _,f as p,g as k,h as d,j as A,l as B,k as N,n as D,o as v,p as C,q as G,r as F,u as E,v as I,w as h,x as J,y as P,z as R}from"./index.21c960f7.js";function K(c){let e,s,n,l,t,o,f,m,i,a,b,u;return l=new G({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:r})=>({5:r}),({uniqueId:r})=>r?32:0]},$$scope:{ctx:c}}}),{c(){e=_("form"),s=_("div"),s.innerHTML=`<h4 class="m-b-xs">Forgotten admin password</h4>
<p>Enter the email associated with your account and we\u2019ll send you a recovery link:</p>`,n=g(),H(l.$$.fragment),t=g(),o=_("button"),f=_("i"),m=g(),i=_("span"),i.textContent="Send recovery link",p(s,"class","content txt-center m-b-sm"),p(f,"class","ri-mail-send-line"),p(i,"class","txt"),p(o,"type","submit"),p(o,"class","btn btn-lg btn-block"),o.disabled=c[1],F(o,"btn-loading",c[1]),p(e,"class","m-b-base")},m(r,$){k(r,e,$),d(e,s),d(e,n),L(l,e,null),d(e,t),d(e,o),d(o,f),d(o,m),d(o,i),a=!0,b||(u=E(e,"submit",I(c[3])),b=!0)},p(r,$){const q={};$&97&&(q.$$scope={dirty:$,ctx:r}),l.$set(q),(!a||$&2)&&(o.disabled=r[1]),(!a||$&2)&&F(o,"btn-loading",r[1])},i(r){a||(w(l.$$.fragment,r),a=!0)},o(r){y(l.$$.fragment,r),a=!1},d(r){r&&v(e),S(l),b=!1,u()}}}function O(c){let e,s,n,l,t,o,f,m,i;return{c(){e=_("div"),s=_("div"),s.innerHTML='<i class="ri-checkbox-circle-line"></i>',n=g(),l=_("div"),t=_("p"),o=h("Check "),f=_("strong"),m=h(c[0]),i=h(" for the recovery link."),p(s,"class","icon"),p(f,"class","txt-nowrap"),p(l,"class","content"),p(e,"class","alert alert-success")},m(a,b){k(a,e,b),d(e,s),d(e,n),d(e,l),d(l,t),d(t,o),d(t,f),d(f,m),d(t,i)},p(a,b){b&1&&J(m,a[0])},i:P,o:P,d(a){a&&v(e)}}}function Q(c){let e,s,n,l,t,o,f,m;return{c(){e=_("label"),s=h("Email"),l=g(),t=_("input"),p(e,"for",n=c[5]),p(t,"type","email"),p(t,"id",o=c[5]),t.required=!0,t.autofocus=!0},m(i,a){k(i,e,a),d(e,s),k(i,l,a),k(i,t,a),R(t,c[0]),t.focus(),f||(m=E(t,"input",c[4]),f=!0)},p(i,a){a&32&&n!==(n=i[5])&&p(e,"for",n),a&32&&o!==(o=i[5])&&p(t,"id",o),a&1&&t.value!==i[0]&&R(t,i[0])},d(i){i&&v(e),i&&v(l),i&&v(t),f=!1,m()}}}function U(c){let e,s,n,l,t,o,f,m;const i=[O,K],a=[];function b(u,r){return u[2]?0:1}return e=b(c),s=a[e]=i[e](c),{c(){s.c(),n=g(),l=_("div"),t=_("a"),t.textContent="Back to login",p(t,"href","/login"),p(t,"class","link-hint"),p(l,"class","content txt-center")},m(u,r){a[e].m(u,r),k(u,n,r),k(u,l,r),d(l,t),o=!0,f||(m=A(B.call(null,t)),f=!0)},p(u,r){let $=e;e=b(u),e===$?a[e].p(u,r):(N(),y(a[$],1,1,()=>{a[$]=null}),D(),s=a[e],s?s.p(u,r):(s=a[e]=i[e](u),s.c()),w(s,1),s.m(n.parentNode,n))},i(u){o||(w(s),o=!0)},o(u){y(s),o=!1},d(u){a[e].d(u),u&&v(n),u&&v(l),f=!1,m()}}}function V(c){let e,s;return e=new z({props:{$$slots:{default:[U]},$$scope:{ctx:c}}}),{c(){H(e.$$.fragment)},m(n,l){L(e,n,l),s=!0},p(n,[l]){const t={};l&71&&(t.$$scope={dirty:l,ctx:n}),e.$set(t)},i(n){s||(w(e.$$.fragment,n),s=!0)},o(n){y(e.$$.fragment,n),s=!1},d(n){S(e,n)}}}function W(c,e,s){let n="",l=!1,t=!1;async function o(){if(!l){s(1,l=!0);try{await C.admins.requestPasswordReset(n),s(2,t=!0)}catch(m){C.errorResponseHandler(m)}s(1,l=!1)}}function f(){n=this.value,s(0,n)}return[n,l,t,o,f]}class Y extends M{constructor(e){super(),T(this,e,W,V,j,{})}}export{Y as default};
@@ -1,2 +0,0 @@
import{S as M,i as T,s as j,F as z,c as H,m as L,t as w,a as y,d as S,b as g,e as _,f as p,g as k,h as d,j as A,l as B,k as N,n as D,o as v,p as C,q as G,r as F,u as E,v as I,w as h,x as J,y as P,z as R}from"./index.994990a4.js";function K(c){let e,s,n,l,t,o,f,m,i,a,b,u;return l=new G({props:{class:"form-field required",name:"email",$$slots:{default:[Q,({uniqueId:r})=>({5:r}),({uniqueId:r})=>r?32:0]},$$scope:{ctx:c}}}),{c(){e=_("form"),s=_("div"),s.innerHTML=`<h4 class="m-b-xs">Forgotten admin password</h4>
<p>Enter the email associated with your account and we\u2019ll send you a recovery link:</p>`,n=g(),H(l.$$.fragment),t=g(),o=_("button"),f=_("i"),m=g(),i=_("span"),i.textContent="Send recovery link",p(s,"class","content txt-center m-b-sm"),p(f,"class","ri-mail-send-line"),p(i,"class","txt"),p(o,"type","submit"),p(o,"class","btn btn-lg btn-block"),o.disabled=c[1],F(o,"btn-loading",c[1]),p(e,"class","m-b-base")},m(r,$){k(r,e,$),d(e,s),d(e,n),L(l,e,null),d(e,t),d(e,o),d(o,f),d(o,m),d(o,i),a=!0,b||(u=E(e,"submit",I(c[3])),b=!0)},p(r,$){const q={};$&97&&(q.$$scope={dirty:$,ctx:r}),l.$set(q),(!a||$&2)&&(o.disabled=r[1]),$&2&&F(o,"btn-loading",r[1])},i(r){a||(w(l.$$.fragment,r),a=!0)},o(r){y(l.$$.fragment,r),a=!1},d(r){r&&v(e),S(l),b=!1,u()}}}function O(c){let e,s,n,l,t,o,f,m,i;return{c(){e=_("div"),s=_("div"),s.innerHTML='<i class="ri-checkbox-circle-line"></i>',n=g(),l=_("div"),t=_("p"),o=h("Check "),f=_("strong"),m=h(c[0]),i=h(" for the recovery link."),p(s,"class","icon"),p(f,"class","txt-nowrap"),p(l,"class","content"),p(e,"class","alert alert-success")},m(a,b){k(a,e,b),d(e,s),d(e,n),d(e,l),d(l,t),d(t,o),d(t,f),d(f,m),d(t,i)},p(a,b){b&1&&J(m,a[0])},i:P,o:P,d(a){a&&v(e)}}}function Q(c){let e,s,n,l,t,o,f,m;return{c(){e=_("label"),s=h("Email"),l=g(),t=_("input"),p(e,"for",n=c[5]),p(t,"type","email"),p(t,"id",o=c[5]),t.required=!0,t.autofocus=!0},m(i,a){k(i,e,a),d(e,s),k(i,l,a),k(i,t,a),R(t,c[0]),t.focus(),f||(m=E(t,"input",c[4]),f=!0)},p(i,a){a&32&&n!==(n=i[5])&&p(e,"for",n),a&32&&o!==(o=i[5])&&p(t,"id",o),a&1&&t.value!==i[0]&&R(t,i[0])},d(i){i&&v(e),i&&v(l),i&&v(t),f=!1,m()}}}function U(c){let e,s,n,l,t,o,f,m;const i=[O,K],a=[];function b(u,r){return u[2]?0:1}return e=b(c),s=a[e]=i[e](c),{c(){s.c(),n=g(),l=_("div"),t=_("a"),t.textContent="Back to login",p(t,"href","/login"),p(t,"class","link-hint"),p(l,"class","content txt-center")},m(u,r){a[e].m(u,r),k(u,n,r),k(u,l,r),d(l,t),o=!0,f||(m=A(B.call(null,t)),f=!0)},p(u,r){let $=e;e=b(u),e===$?a[e].p(u,r):(N(),y(a[$],1,1,()=>{a[$]=null}),D(),s=a[e],s?s.p(u,r):(s=a[e]=i[e](u),s.c()),w(s,1),s.m(n.parentNode,n))},i(u){o||(w(s),o=!0)},o(u){y(s),o=!1},d(u){a[e].d(u),u&&v(n),u&&v(l),f=!1,m()}}}function V(c){let e,s;return e=new z({props:{$$slots:{default:[U]},$$scope:{ctx:c}}}),{c(){H(e.$$.fragment)},m(n,l){L(e,n,l),s=!0},p(n,[l]){const t={};l&71&&(t.$$scope={dirty:l,ctx:n}),e.$set(t)},i(n){s||(w(e.$$.fragment,n),s=!0)},o(n){y(e.$$.fragment,n),s=!1},d(n){S(e,n)}}}function W(c,e,s){let n="",l=!1,t=!1;async function o(){if(!l){s(1,l=!0);try{await C.admins.requestPasswordReset(n),s(2,t=!0)}catch(m){C.errorResponseHandler(m)}s(1,l=!1)}}function f(){n=this.value,s(0,n)}return[n,l,t,o,f]}class Y extends M{constructor(e){super(),T(this,e,W,V,j,{})}}export{Y as default};
@@ -1,4 +1,4 @@
import{S as G,i as J,s as M,F as N,c as T,m as L,t as v,a as y,d as z,C as R,E as U,g as _,k as W,n as Y,o as b,G as j,p as A,q as B,e as m,w as C,b as h,f as d,r as F,h as k,u as q,v as D,y as E,x as I,z as H}from"./index.994990a4.js";function K(r){let e,t,s,l,n,o,c,a,i,u,g,$,p=r[3]&&S(r);return o=new B({props:{class:"form-field required",name:"password",$$slots:{default:[Q,({uniqueId:f})=>({8:f}),({uniqueId:f})=>f?256:0]},$$scope:{ctx:r}}}),{c(){e=m("form"),t=m("div"),s=m("h5"),l=C(`Type your password to confirm changing your email address
`),p&&p.c(),n=h(),T(o.$$.fragment),c=h(),a=m("button"),i=m("span"),i.textContent="Confirm new email",d(t,"class","content txt-center m-b-base"),d(i,"class","txt"),d(a,"type","submit"),d(a,"class","btn btn-lg btn-block"),a.disabled=r[1],F(a,"btn-loading",r[1])},m(f,w){_(f,e,w),k(e,t),k(t,s),k(s,l),p&&p.m(s,null),k(e,n),L(o,e,null),k(e,c),k(e,a),k(a,i),u=!0,g||($=q(e,"submit",D(r[4])),g=!0)},p(f,w){f[3]?p?p.p(f,w):(p=S(f),p.c(),p.m(s,null)):p&&(p.d(1),p=null);const P={};w&769&&(P.$$scope={dirty:w,ctx:f}),o.$set(P),(!u||w&2)&&(a.disabled=f[1]),w&2&&F(a,"btn-loading",f[1])},i(f){u||(v(o.$$.fragment,f),u=!0)},o(f){y(o.$$.fragment,f),u=!1},d(f){f&&b(e),p&&p.d(),z(o),g=!1,$()}}}function O(r){let e,t,s,l,n;return{c(){e=m("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
import{S as G,i as J,s as M,F as N,c as T,m as L,t as v,a as y,d as z,C as R,E as U,g as _,k as W,n as Y,o as b,G as j,p as A,q as B,e as m,w as C,b as h,f as d,r as F,h as k,u as q,v as D,y as E,x as I,z as H}from"./index.21c960f7.js";function K(r){let e,t,s,l,n,o,c,a,i,u,g,$,p=r[3]&&S(r);return o=new B({props:{class:"form-field required",name:"password",$$slots:{default:[Q,({uniqueId:f})=>({8:f}),({uniqueId:f})=>f?256:0]},$$scope:{ctx:r}}}),{c(){e=m("form"),t=m("div"),s=m("h5"),l=C(`Type your password to confirm changing your email address
`),p&&p.c(),n=h(),T(o.$$.fragment),c=h(),a=m("button"),i=m("span"),i.textContent="Confirm new email",d(t,"class","content txt-center m-b-base"),d(i,"class","txt"),d(a,"type","submit"),d(a,"class","btn btn-lg btn-block"),a.disabled=r[1],F(a,"btn-loading",r[1])},m(f,w){_(f,e,w),k(e,t),k(t,s),k(s,l),p&&p.m(s,null),k(e,n),L(o,e,null),k(e,c),k(e,a),k(a,i),u=!0,g||($=q(e,"submit",D(r[4])),g=!0)},p(f,w){f[3]?p?p.p(f,w):(p=S(f),p.c(),p.m(s,null)):p&&(p.d(1),p=null);const P={};w&769&&(P.$$scope={dirty:w,ctx:f}),o.$set(P),(!u||w&2)&&(a.disabled=f[1]),(!u||w&2)&&F(a,"btn-loading",f[1])},i(f){u||(v(o.$$.fragment,f),u=!0)},o(f){y(o.$$.fragment,f),u=!1},d(f){f&&b(e),p&&p.d(),z(o),g=!1,$()}}}function O(r){let e,t,s,l,n;return{c(){e=m("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
<div class="content txt-bold"><p>Successfully changed the user email address.</p>
<p>You can now sign in with your new email address.</p></div>`,t=h(),s=m("button"),s.textContent="Close",d(e,"class","alert alert-success"),d(s,"type","button"),d(s,"class","btn btn-secondary btn-block")},m(o,c){_(o,e,c),_(o,t,c),_(o,s,c),l||(n=q(s,"click",r[6]),l=!0)},p:E,i:E,o:E,d(o){o&&b(e),o&&b(t),o&&b(s),l=!1,n()}}}function S(r){let e,t,s;return{c(){e=C("to "),t=m("strong"),s=C(r[3]),d(t,"class","txt-nowrap")},m(l,n){_(l,e,n),_(l,t,n),k(t,s)},p(l,n){n&8&&I(s,l[3])},d(l){l&&b(e),l&&b(t)}}}function Q(r){let e,t,s,l,n,o,c,a;return{c(){e=m("label"),t=C("Password"),l=h(),n=m("input"),d(e,"for",s=r[8]),d(n,"type","password"),d(n,"id",o=r[8]),n.required=!0,n.autofocus=!0},m(i,u){_(i,e,u),k(e,t),_(i,l,u),_(i,n,u),H(n,r[0]),n.focus(),c||(a=q(n,"input",r[7]),c=!0)},p(i,u){u&256&&s!==(s=i[8])&&d(e,"for",s),u&256&&o!==(o=i[8])&&d(n,"id",o),u&1&&n.value!==i[0]&&H(n,i[0])},d(i){i&&b(e),i&&b(l),i&&b(n),c=!1,a()}}}function V(r){let e,t,s,l;const n=[O,K],o=[];function c(a,i){return a[2]?0:1}return e=c(r),t=o[e]=n[e](r),{c(){t.c(),s=U()},m(a,i){o[e].m(a,i),_(a,s,i),l=!0},p(a,i){let u=e;e=c(a),e===u?o[e].p(a,i):(W(),y(o[u],1,1,()=>{o[u]=null}),Y(),t=o[e],t?t.p(a,i):(t=o[e]=n[e](a),t.c()),v(t,1),t.m(s.parentNode,s))},i(a){l||(v(t),l=!0)},o(a){y(t),l=!1},d(a){o[e].d(a),a&&b(s)}}}function X(r){let e,t;return e=new N({props:{nobranding:!0,$$slots:{default:[V]},$$scope:{ctx:r}}}),{c(){T(e.$$.fragment)},m(s,l){L(e,s,l),t=!0},p(s,[l]){const n={};l&527&&(n.$$scope={dirty:l,ctx:s}),e.$set(n)},i(s){t||(v(e.$$.fragment,s),t=!0)},o(s){y(e.$$.fragment,s),t=!1},d(s){z(e,s)}}}function Z(r,e,t){let s,{params:l}=e,n="",o=!1,c=!1;async function a(){if(o)return;t(1,o=!0);const g=new j("../");try{await g.users.confirmEmailChange(l==null?void 0:l.token,n),t(2,c=!0)}catch($){A.errorResponseHandler($)}t(1,o=!1)}const i=()=>window.close();function u(){n=this.value,t(0,n)}return r.$$set=g=>{"params"in g&&t(5,l=g.params)},r.$$.update=()=>{r.$$.dirty&32&&t(3,s=R.getJWTPayload(l==null?void 0:l.token).newEmail||"")},[n,o,c,s,a,l,i,u]}class ee extends G{constructor(e){super(),J(this,e,Z,X,M,{params:5})}}export{ee as default};
@@ -1,4 +1,4 @@
import{S as M,i as U,s as W,F as Y,c as H,m as N,t as P,a as q,d as L,C as j,E as A,g as _,k as B,n as D,o as m,G as I,p as K,q as E,e as b,w as y,b as C,f as c,r as G,h as w,u as S,v as O,y as F,x as Q,z as R}from"./index.994990a4.js";function V(i){let e,l,s,n,t,o,p,u,r,a,v,g,k,h,d=i[4]&&J(i);return o=new E({props:{class:"form-field required",name:"password",$$slots:{default:[Z,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:i}}}),u=new E({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:i}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=y(`Reset your user password
`),d&&d.c(),t=C(),H(o.$$.fragment),p=C(),H(u.$$.fragment),r=C(),a=b("button"),v=b("span"),v.textContent="Set new password",c(l,"class","content txt-center m-b-base"),c(v,"class","txt"),c(a,"type","submit"),c(a,"class","btn btn-lg btn-block"),a.disabled=i[2],G(a,"btn-loading",i[2])},m(f,$){_(f,e,$),w(e,l),w(l,s),w(s,n),d&&d.m(s,null),w(e,t),N(o,e,null),w(e,p),N(u,e,null),w(e,r),w(e,a),w(a,v),g=!0,k||(h=S(e,"submit",O(i[5])),k=!0)},p(f,$){f[4]?d?d.p(f,$):(d=J(f),d.c(),d.m(s,null)):d&&(d.d(1),d=null);const T={};$&3073&&(T.$$scope={dirty:$,ctx:f}),o.$set(T);const z={};$&3074&&(z.$$scope={dirty:$,ctx:f}),u.$set(z),(!g||$&4)&&(a.disabled=f[2]),$&4&&G(a,"btn-loading",f[2])},i(f){g||(P(o.$$.fragment,f),P(u.$$.fragment,f),g=!0)},o(f){q(o.$$.fragment,f),q(u.$$.fragment,f),g=!1},d(f){f&&m(e),d&&d.d(),L(o),L(u),k=!1,h()}}}function X(i){let e,l,s,n,t;return{c(){e=b("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
import{S as M,i as U,s as W,F as Y,c as H,m as N,t as P,a as q,d as L,C as j,E as A,g as _,k as B,n as D,o as m,G as I,p as K,q as E,e as b,w as y,b as C,f as c,r as G,h as w,u as S,v as O,y as F,x as Q,z as R}from"./index.21c960f7.js";function V(i){let e,l,s,n,t,o,p,u,r,a,v,k,g,h,d=i[4]&&J(i);return o=new E({props:{class:"form-field required",name:"password",$$slots:{default:[Z,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:i}}}),u=new E({props:{class:"form-field required",name:"passwordConfirm",$$slots:{default:[x,({uniqueId:f})=>({10:f}),({uniqueId:f})=>f?1024:0]},$$scope:{ctx:i}}}),{c(){e=b("form"),l=b("div"),s=b("h5"),n=y(`Reset your user password
`),d&&d.c(),t=C(),H(o.$$.fragment),p=C(),H(u.$$.fragment),r=C(),a=b("button"),v=b("span"),v.textContent="Set new password",c(l,"class","content txt-center m-b-base"),c(v,"class","txt"),c(a,"type","submit"),c(a,"class","btn btn-lg btn-block"),a.disabled=i[2],G(a,"btn-loading",i[2])},m(f,$){_(f,e,$),w(e,l),w(l,s),w(s,n),d&&d.m(s,null),w(e,t),N(o,e,null),w(e,p),N(u,e,null),w(e,r),w(e,a),w(a,v),k=!0,g||(h=S(e,"submit",O(i[5])),g=!0)},p(f,$){f[4]?d?d.p(f,$):(d=J(f),d.c(),d.m(s,null)):d&&(d.d(1),d=null);const T={};$&3073&&(T.$$scope={dirty:$,ctx:f}),o.$set(T);const z={};$&3074&&(z.$$scope={dirty:$,ctx:f}),u.$set(z),(!k||$&4)&&(a.disabled=f[2]),(!k||$&4)&&G(a,"btn-loading",f[2])},i(f){k||(P(o.$$.fragment,f),P(u.$$.fragment,f),k=!0)},o(f){q(o.$$.fragment,f),q(u.$$.fragment,f),k=!1},d(f){f&&m(e),d&&d.d(),L(o),L(u),g=!1,h()}}}function X(i){let e,l,s,n,t;return{c(){e=b("div"),e.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
<div class="content txt-bold"><p>Successfully changed the user password.</p>
<p>You can now sign in with your new password.</p></div>`,l=C(),s=b("button"),s.textContent="Close",c(e,"class","alert alert-success"),c(s,"type","button"),c(s,"class","btn btn-secondary btn-block")},m(o,p){_(o,e,p),_(o,l,p),_(o,s,p),n||(t=S(s,"click",i[7]),n=!0)},p:F,i:F,o:F,d(o){o&&m(e),o&&m(l),o&&m(s),n=!1,t()}}}function J(i){let e,l,s;return{c(){e=y("for "),l=b("strong"),s=y(i[4])},m(n,t){_(n,e,t),_(n,l,t),w(l,s)},p(n,t){t&16&&Q(s,n[4])},d(n){n&&m(e),n&&m(l)}}}function Z(i){let e,l,s,n,t,o,p,u;return{c(){e=b("label"),l=y("New password"),n=C(),t=b("input"),c(e,"for",s=i[10]),c(t,"type","password"),c(t,"id",o=i[10]),t.required=!0,t.autofocus=!0},m(r,a){_(r,e,a),w(e,l),_(r,n,a),_(r,t,a),R(t,i[0]),t.focus(),p||(u=S(t,"input",i[8]),p=!0)},p(r,a){a&1024&&s!==(s=r[10])&&c(e,"for",s),a&1024&&o!==(o=r[10])&&c(t,"id",o),a&1&&t.value!==r[0]&&R(t,r[0])},d(r){r&&m(e),r&&m(n),r&&m(t),p=!1,u()}}}function x(i){let e,l,s,n,t,o,p,u;return{c(){e=b("label"),l=y("New password confirm"),n=C(),t=b("input"),c(e,"for",s=i[10]),c(t,"type","password"),c(t,"id",o=i[10]),t.required=!0},m(r,a){_(r,e,a),w(e,l),_(r,n,a),_(r,t,a),R(t,i[1]),p||(u=S(t,"input",i[9]),p=!0)},p(r,a){a&1024&&s!==(s=r[10])&&c(e,"for",s),a&1024&&o!==(o=r[10])&&c(t,"id",o),a&2&&t.value!==r[1]&&R(t,r[1])},d(r){r&&m(e),r&&m(n),r&&m(t),p=!1,u()}}}function ee(i){let e,l,s,n;const t=[X,V],o=[];function p(u,r){return u[3]?0:1}return e=p(i),l=o[e]=t[e](i),{c(){l.c(),s=A()},m(u,r){o[e].m(u,r),_(u,s,r),n=!0},p(u,r){let a=e;e=p(u),e===a?o[e].p(u,r):(B(),q(o[a],1,1,()=>{o[a]=null}),D(),l=o[e],l?l.p(u,r):(l=o[e]=t[e](u),l.c()),P(l,1),l.m(s.parentNode,s))},i(u){n||(P(l),n=!0)},o(u){q(l),n=!1},d(u){o[e].d(u),u&&m(s)}}}function te(i){let e,l;return e=new Y({props:{nobranding:!0,$$slots:{default:[ee]},$$scope:{ctx:i}}}),{c(){H(e.$$.fragment)},m(s,n){N(e,s,n),l=!0},p(s,[n]){const t={};n&2079&&(t.$$scope={dirty:n,ctx:s}),e.$set(t)},i(s){l||(P(e.$$.fragment,s),l=!0)},o(s){q(e.$$.fragment,s),l=!1},d(s){L(e,s)}}}function se(i,e,l){let s,{params:n}=e,t="",o="",p=!1,u=!1;async function r(){if(p)return;l(2,p=!0);const k=new I("../");try{await k.users.confirmPasswordReset(n==null?void 0:n.token,t,o),l(3,u=!0)}catch(h){K.errorResponseHandler(h)}l(2,p=!1)}const a=()=>window.close();function v(){t=this.value,l(0,t)}function g(){o=this.value,l(1,o)}return i.$$set=k=>{"params"in k&&l(6,n=k.params)},i.$$.update=()=>{i.$$.dirty&64&&l(4,s=j.getJWTPayload(n==null?void 0:n.token).email||"")},[t,o,p,u,s,r,n,a,v,g]}class ne extends M{constructor(e){super(),U(this,e,se,te,W,{params:6})}}export{ne as default};
<p>You can now sign in with your new password.</p></div>`,l=C(),s=b("button"),s.textContent="Close",c(e,"class","alert alert-success"),c(s,"type","button"),c(s,"class","btn btn-secondary btn-block")},m(o,p){_(o,e,p),_(o,l,p),_(o,s,p),n||(t=S(s,"click",i[7]),n=!0)},p:F,i:F,o:F,d(o){o&&m(e),o&&m(l),o&&m(s),n=!1,t()}}}function J(i){let e,l,s;return{c(){e=y("for "),l=b("strong"),s=y(i[4])},m(n,t){_(n,e,t),_(n,l,t),w(l,s)},p(n,t){t&16&&Q(s,n[4])},d(n){n&&m(e),n&&m(l)}}}function Z(i){let e,l,s,n,t,o,p,u;return{c(){e=b("label"),l=y("New password"),n=C(),t=b("input"),c(e,"for",s=i[10]),c(t,"type","password"),c(t,"id",o=i[10]),t.required=!0,t.autofocus=!0},m(r,a){_(r,e,a),w(e,l),_(r,n,a),_(r,t,a),R(t,i[0]),t.focus(),p||(u=S(t,"input",i[8]),p=!0)},p(r,a){a&1024&&s!==(s=r[10])&&c(e,"for",s),a&1024&&o!==(o=r[10])&&c(t,"id",o),a&1&&t.value!==r[0]&&R(t,r[0])},d(r){r&&m(e),r&&m(n),r&&m(t),p=!1,u()}}}function x(i){let e,l,s,n,t,o,p,u;return{c(){e=b("label"),l=y("New password confirm"),n=C(),t=b("input"),c(e,"for",s=i[10]),c(t,"type","password"),c(t,"id",o=i[10]),t.required=!0},m(r,a){_(r,e,a),w(e,l),_(r,n,a),_(r,t,a),R(t,i[1]),p||(u=S(t,"input",i[9]),p=!0)},p(r,a){a&1024&&s!==(s=r[10])&&c(e,"for",s),a&1024&&o!==(o=r[10])&&c(t,"id",o),a&2&&t.value!==r[1]&&R(t,r[1])},d(r){r&&m(e),r&&m(n),r&&m(t),p=!1,u()}}}function ee(i){let e,l,s,n;const t=[X,V],o=[];function p(u,r){return u[3]?0:1}return e=p(i),l=o[e]=t[e](i),{c(){l.c(),s=A()},m(u,r){o[e].m(u,r),_(u,s,r),n=!0},p(u,r){let a=e;e=p(u),e===a?o[e].p(u,r):(B(),q(o[a],1,1,()=>{o[a]=null}),D(),l=o[e],l?l.p(u,r):(l=o[e]=t[e](u),l.c()),P(l,1),l.m(s.parentNode,s))},i(u){n||(P(l),n=!0)},o(u){q(l),n=!1},d(u){o[e].d(u),u&&m(s)}}}function te(i){let e,l;return e=new Y({props:{nobranding:!0,$$slots:{default:[ee]},$$scope:{ctx:i}}}),{c(){H(e.$$.fragment)},m(s,n){N(e,s,n),l=!0},p(s,[n]){const t={};n&2079&&(t.$$scope={dirty:n,ctx:s}),e.$set(t)},i(s){l||(P(e.$$.fragment,s),l=!0)},o(s){q(e.$$.fragment,s),l=!1},d(s){L(e,s)}}}function se(i,e,l){let s,{params:n}=e,t="",o="",p=!1,u=!1;async function r(){if(p)return;l(2,p=!0);const g=new I("../");try{await g.users.confirmPasswordReset(n==null?void 0:n.token,t,o),l(3,u=!0)}catch(h){K.errorResponseHandler(h)}l(2,p=!1)}const a=()=>window.close();function v(){t=this.value,l(0,t)}function k(){o=this.value,l(1,o)}return i.$$set=g=>{"params"in g&&l(6,n=g.params)},i.$$.update=()=>{i.$$.dirty&64&&l(4,s=j.getJWTPayload(n==null?void 0:n.token).email||"")},[t,o,p,u,s,r,n,a,v,k]}class ne extends M{constructor(e){super(),U(this,e,se,te,W,{params:6})}}export{ne as default};
@@ -1,3 +1,3 @@
import{S as k,i as v,s as y,F as w,c as x,m as C,t as g,a as $,d as L,G as H,E as M,g as r,o as a,e as u,b as m,f,u as _,y as p}from"./index.994990a4.js";function P(c){let t,s,e,n,i;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-error-warning-line"></i></div>
import{S as k,i as v,s as y,F as w,c as x,m as C,t as g,a as $,d as L,G as H,E as M,g as r,o as a,e as u,b as m,f,u as _,y as p}from"./index.21c960f7.js";function P(c){let t,s,e,n,i;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-error-warning-line"></i></div>
<div class="content txt-bold"><p>Invalid or expired verification token.</p></div>`,s=m(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-danger"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(l,o){r(l,t,o),r(l,s,o),r(l,e,o),n||(i=_(e,"click",c[4]),n=!0)},p,d(l){l&&a(t),l&&a(s),l&&a(e),n=!1,i()}}}function S(c){let t,s,e,n,i;return{c(){t=u("div"),t.innerHTML=`<div class="icon"><i class="ri-checkbox-circle-line"></i></div>
<div class="content txt-bold"><p>Successfully verified email address.</p></div>`,s=m(),e=u("button"),e.textContent="Close",f(t,"class","alert alert-success"),f(e,"type","button"),f(e,"class","btn btn-secondary btn-block")},m(l,o){r(l,t,o),r(l,s,o),r(l,e,o),n||(i=_(e,"click",c[3]),n=!0)},p,d(l){l&&a(t),l&&a(s),l&&a(e),n=!1,i()}}}function T(c){let t;return{c(){t=u("div"),t.innerHTML='<div class="loader loader-lg"><em>Please wait...</em></div>',f(t,"class","txt-center")},m(s,e){r(s,t,e)},p,d(s){s&&a(t)}}}function F(c){let t;function s(i,l){return i[1]?T:i[0]?S:P}let e=s(c),n=e(c);return{c(){n.c(),t=M()},m(i,l){n.m(i,l),r(i,t,l)},p(i,l){e===(e=s(i))&&n?n.p(i,l):(n.d(1),n=e(i),n&&(n.c(),n.m(t.parentNode,t)))},d(i){n.d(i),i&&a(t)}}}function V(c){let t,s;return t=new w({props:{nobranding:!0,$$slots:{default:[F]},$$scope:{ctx:c}}}),{c(){x(t.$$.fragment)},m(e,n){C(t,e,n),s=!0},p(e,[n]){const i={};n&67&&(i.$$scope={dirty:n,ctx:e}),t.$set(i)},i(e){s||(g(t.$$.fragment,e),s=!0)},o(e){$(t.$$.fragment,e),s=!1},d(e){L(t,e)}}}function q(c,t,s){let{params:e}=t,n=!1,i=!1;l();async function l(){s(1,i=!0);const d=new H("../");try{await d.users.confirmVerification(e==null?void 0:e.token),s(0,n=!0)}catch{s(0,n=!1)}s(1,i=!1)}const o=()=>window.close(),b=()=>window.close();return c.$$set=d=>{"params"in d&&s(2,e=d.params)},[n,i,e,o,b]}class I extends k{constructor(t){super(),v(this,t,q,V,y,{params:2})}}export{I as default};
+661
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-661
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -24,8 +24,8 @@
window.Prism = window.Prism || {};
window.Prism.manual = true;
</script>
<script type="module" crossorigin src="./assets/index.994990a4.js"></script>
<link rel="stylesheet" href="./assets/index.126056bc.css">
<script type="module" crossorigin src="./assets/index.21c960f7.js"></script>
<link rel="stylesheet" href="./assets/index.26987507.css">
</head>
<body>
<div id="app"></div>
+314 -287
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -189,7 +189,7 @@
submitShortcut,
...closeBracketsKeymap,
...defaultKeymap,
...searchKeymap,
searchKeymap.find((item) => item.key === "Mod-d"),
...historyKeymap,
...completionKeymap,
]),
@@ -270,7 +270,6 @@
}
const skipFields = [
"@request.user.profile.id",
"@request.user.profile.userId",
"@request.user.profile.created",
"@request.user.profile.updated",
@@ -379,7 +378,7 @@
submitShortcut,
...closeBracketsKeymap,
...defaultKeymap,
...searchKeymap,
searchKeymap.find((item) => item.key === "Mod-d"),
...historyKeymap,
...completionKeymap,
]),
+5 -6
View File
@@ -1,14 +1,13 @@
<script>
import CommonHelper from "@/utils/CommonHelper";
import tooltip from "@/actions/tooltip";
export let date = "";
// note: manual trim the ms without converting to DateTime
// to help improving the rendering performance in large data sets
$: shortDate = date.length > 19 ? date.substring(0, 19) : date;
</script>
{#if date}
<span class="txt" use:tooltip={CommonHelper.formatToLocalDate(date) + " Local"}>
{CommonHelper.formatToUTCDate(date)} UTC
</span>
<span class="txt">{shortDate} UTC</span>
{:else}
<span class="txt txt-hint">N/A</span>
{/if}
+3 -2
View File
@@ -25,8 +25,9 @@
let newSelected = [];
for (let item of items) {
if (CommonHelper.inArray(newKeyOfSelected, item[selectionKey])) {
for (let v of newKeyOfSelected) {
const item = CommonHelper.findByKey(items, selectionKey, v);
if (item) {
newSelected.push(item);
}
}
+1 -1
View File
@@ -36,7 +36,7 @@
$: filteredItems = filterItems(items, searchTerm);
$: isSelected = function (item) {
let normalized = CommonHelper.toArray(selected);
const normalized = CommonHelper.toArray(selected);
return CommonHelper.inArray(normalized, item);
};
@@ -20,7 +20,7 @@
<div class="content m-b-base">
<p>Subscribe to realtime changes via Server-Sent Events (SSE).</p>
<p>
Events are send for <strong>create</strong>, <strong>update</strong>
Events are sent for <strong>create</strong>, <strong>update</strong>
and <strong>delete</strong> record operations (see "Event data format" section below).
</p>
<div class="alert alert-info m-t-10">
@@ -14,7 +14,7 @@
};
}
// leave the validation to the api
// note: leave the validation to the api
// $: if (!CommonHelper.isEmpty(options.values) && options.maxSelect > options.values.length) {
// options.maxSelect = options.values.length;
// }
+2 -2
View File
@@ -148,6 +148,6 @@
<RecordUpsertPanel
bind:this={recordPanel}
collection={$activeCollection}
on:save={() => recordsList?.load()}
on:delete={() => recordsList?.load()}
on:save={() => recordsList?.reloadLoadedPages()}
on:delete={() => recordsList?.reloadLoadedPages()}
/>
@@ -7,6 +7,12 @@
export let record;
export let field;
// rough text cut to avoid rendering large chunk of texts
function cutText(text) {
text = text || "";
return text.length > 200 ? text.substring(0, 200) : text;
}
</script>
<td class="col-type-{field.type} col-field-{field.name}">
@@ -28,30 +34,32 @@
{:else if field.type === "date"}
<FormattedDate date={record[field.name]} />
{:else if field.type === "json"}
<span class="txt txt-ellipsis">{JSON.stringify(record[field.name])}</span>
<span class="txt txt-ellipsis">
{cutText(JSON.stringify(record[field.name]))}
</span>
{:else if field.type === "select"}
<div class="inline-flex">
{#each CommonHelper.toArray(record[field.name]) as item}
{#each CommonHelper.toArray(record[field.name]) as item, i (i + item)}
<span class="label">{item}</span>
{/each}
</div>
{:else if field.type === "relation" || field.type === "user"}
<div class="inline-flex">
{#each CommonHelper.toArray(record[field.name]) as item}
{#each CommonHelper.toArray(record[field.name]) as item, i (i + item)}
<IdLabel id={item} />
{/each}
</div>
{:else if field.type === "file"}
<div class="inline-flex">
{#each CommonHelper.toArray(record[field.name]) as filename}
{#each CommonHelper.toArray(record[field.name]) as filename, i (i + filename)}
<figure class="thumb thumb-sm">
<RecordFilePreview {record} {filename} />
</figure>
{/each}
</div>
{:else}
<span class="txt txt-ellipsis" title={record[field.name]}>
{record[field.name]}
<span class="txt txt-ellipsis" title={cutText(record[field.name])}>
{cutText(record[field.name])}
</span>
{/if}
</td>
+19 -9
View File
@@ -8,7 +8,7 @@
// original select props
export let multiple = false;
export let selected = multiple ? [] : undefined;
export let selected = [];
export let keyOfSelected = multiple ? [] : undefined;
export let selectPlaceholder = "- Select -";
export let optionComponent = RecordSelectOption; // custom component to use for each dropdown option item
@@ -23,15 +23,15 @@
let isLoadingSelected = false;
$: if (collectionId) {
loadList(true);
loadSelected().then(() => {
loadList(true);
});
}
$: isLoading = isLoadingList || isLoadingSelected;
$: canLoadMore = totalItems > list.length;
loadSelected();
async function loadSelected() {
const selectedIds = CommonHelper.toArray(keyOfSelected);
@@ -47,14 +47,22 @@
filters.push(`id="${id}"`);
}
selected = await ApiClient.records.getFullList(collectionId, 200, {
sort: "-created",
const result = await ApiClient.records.getFullList(collectionId, 200, {
filter: filters.join("||"),
$cancelKey: uniqueId + "loadSelected",
});
// preserve selected order
selected = [];
for (const id of selectedIds) {
const item = CommonHelper.findByKey(result, "id", id);
if (item) {
selected.push(item);
}
}
// add the selected models to the list (if not already)
list = CommonHelper.filterDuplicatesByKey(list.concat(selected));
list = CommonHelper.filterDuplicatesByKey(selected.concat(list));
} catch (err) {
ApiClient.errorResponseHandler(err);
}
@@ -78,10 +86,12 @@
});
if (reset) {
list = [];
list = CommonHelper.toArray(selected).slice();
}
list = CommonHelper.filterDuplicatesByKey(list.concat(result.items));
list = CommonHelper.filterDuplicatesByKey(
list.concat(result.items, CommonHelper.toArray(selected))
);
currentPage = result.page;
totalItems = result.totalItems;
} catch (err) {
+33 -16
View File
@@ -23,8 +23,11 @@
let isLoading = true;
let isDeleting = false;
$: if (collection && collection.id && sort !== -1 && filter !== -1) {
$: if (collection?.id) {
clearList();
}
$: if (collection?.id && sort !== -1 && filter !== -1) {
load(1);
}
@@ -36,6 +39,16 @@
$: areAllRecordsSelected = records.length && totalBulkSelected === records.length;
export async function reloadLoadedPages() {
const loadedPages = currentPage;
for (let i = 1; i <= loadedPages; i++) {
if (i === 1 || canLoadMore) {
await load(i);
}
}
}
export async function load(page = 1) {
if (!collection?.id) {
return;
@@ -43,16 +56,16 @@
isLoading = true;
if (page <= 1) {
clearList();
}
return ApiClient.records
.getList(collection.id, page, 50, {
sort: sort,
filter: filter,
})
.then((result) => {
if (page <= 1) {
clearList();
}
isLoading = false;
records = records.concat(result.items);
currentPage = result.page;
@@ -140,7 +153,7 @@
isDeleting = false;
// always reload because some of the records may not be deletable
return load();
return reloadLoadedPages();
});
}
</script>
@@ -150,16 +163,20 @@
<thead>
<tr>
<th class="bulk-select-col min-width">
<div class="form-field">
<input
type="checkbox"
id="checkbox_0"
disabled={!records.length}
checked={areAllRecordsSelected}
on:change={() => toggleSelectAllRecords()}
/>
<label for="checkbox_0" />
</div>
{#if isLoading}
<span class="loader loader-sm" />
{:else}
<div class="form-field">
<input
type="checkbox"
id="checkbox_0"
disabled={!records.length}
checked={areAllRecordsSelected}
on:change={() => toggleSelectAllRecords()}
/>
<label for="checkbox_0" />
</div>
{/if}
</th>
<SortHeader class="col-type-text col-field-id" name="id" bind:sort>
<div class="col-header-content">
+18 -8
View File
@@ -23,9 +23,9 @@
$: canLoadMore = totalItems > list.length;
loadList(true);
loadSelected();
loadList(true).then(() => {
loadSelected();
});
async function loadSelected() {
const selectedIds = CommonHelper.toArray(keyOfSelected);
@@ -41,14 +41,22 @@
filters.push(`id="${id}"`);
}
selected = await ApiClient.users.getFullList(100, {
sort: "-created",
const result = await ApiClient.users.getFullList(100, {
filter: filters.join("||"),
$cancelKey: uniqueId + "loadSelected",
});
// preserve selected order
selected = [];
for (const id of selectedIds) {
const item = CommonHelper.findByKey(result, "id", id);
if (item) {
selected.push(item);
}
}
// add the selected models to the list (if not already)
list = CommonHelper.filterDuplicatesByKey(list.concat(selected));
list = CommonHelper.filterDuplicatesByKey(selected.concat(list));
} catch (err) {
ApiClient.errorResponseHandler(err);
}
@@ -68,10 +76,12 @@
});
if (reset) {
list = [];
list = CommonHelper.toArray(selected).slice();
}
list = CommonHelper.filterDuplicatesByKey(list.concat(result.items));
list = CommonHelper.filterDuplicatesByKey(
list.concat(result.items, CommonHelper.toArray(selected))
);
currentPage = result.page;
totalItems = result.totalItems;
} catch (err) {
+2
View File
@@ -207,6 +207,7 @@ table {
// states
&.table-loading {
pointer-events: none;
opacity: 0.7;
}
}
@@ -218,6 +219,7 @@ table {
margin-left: calc(var(--baseSpacing) * -1);
margin-right: calc(var(--baseSpacing) * -1);
.bulk-select-col {
min-width: 70px;
input[type="checkbox"] ~ label {
opacity: 0.7;
}