mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-10-05 05:51:44 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
465cfb5293 | ||
|
|
f58a65e4be | ||
|
|
aa7be1edd1 | ||
|
|
f868756721 | ||
|
|
b66a4e32cc | ||
|
|
72df31e22d | ||
|
|
77639f94ce | ||
|
|
bff6b4c3ab | ||
|
|
aee115a99f | ||
|
|
b81d92dbd2 | ||
|
|
98a0f5eb05 | ||
|
|
3350ae651d | ||
|
|
394a46f3a2 | ||
|
|
4624e84b50 | ||
|
|
21dd105d9a | ||
|
|
8d60b928fc | ||
|
|
e7ae0bd716 | ||
|
|
4752ed84c7 | ||
|
|
5631d9b1c2 | ||
|
|
b36b8e35af | ||
|
|
9ff3e5a895 | ||
|
|
e5964e739e | ||
|
|
40d2849aa6 | ||
|
|
f3ae7731d4 | ||
|
|
1c3a912532 | ||
|
|
2b61b2babf | ||
|
|
d4b73af2e9 | ||
|
|
b11731222f | ||
|
|
f1873ad26d | ||
|
|
aeb78e51a1 | ||
|
|
541aa6b1e1 | ||
|
|
a9f259a1c7 | ||
|
|
a2d4f12071 | ||
|
|
846432f4c8 | ||
|
|
167e23e9ba | ||
|
|
ea2ef87d08 | ||
|
|
e067bf1517 | ||
|
|
97b85334a9 | ||
|
|
d4026ce60f | ||
|
|
40c631db32 | ||
|
|
095b0aca51 | ||
|
|
a7dfbbb8ec | ||
|
|
b65b7c5c56 | ||
|
|
4ebdfe4a1e | ||
|
|
19db35c48c | ||
|
|
1034dc9842 | ||
|
|
5bea84b94b | ||
|
|
7984e586a8 | ||
|
|
b022e138d4 | ||
|
|
db853850ab | ||
|
|
3d3d3b7355 | ||
|
|
3616b9d667 | ||
|
|
efd9604aa7 | ||
|
|
1ab6bba61b | ||
|
|
9e6b4eeda8 | ||
|
|
ea1a537a60 | ||
|
|
23a989118c | ||
|
|
d0b2551e78 | ||
|
|
b9b0e5ae80 | ||
|
|
f7fbc6c2c3 |
No files matched your search
+22
-7
@@ -2,19 +2,26 @@
|
||||
|
||||
**Keep in mind that PocketBase is a non-commercial open source project, maintained entirely on volunteer basis (there is no company or dedicated team behind it), and there are no bounties!**
|
||||
|
||||
If you discover a security vulnerability within PocketBase, please send an e-mail to **support at pocketbase.io** or submit a private [GitHub Security advisory](https://github.com/pocketbase/pocketbase/security/advisories).
|
||||
If you want to responsibly report a security issue you'll have to reach out as a human to **support at pocketbase.io**.
|
||||
|
||||
I try to be as responsive as possible and usually address security reports within a day or two, but if you didn't receive a reply from me for more than 5 days it is very likely that your email was flagged and in that case please open a GitHub issue or discussion just mentioning that you found a vulnerability and want to report it so that I can see the notification and will try to contact you for more details.
|
||||
This means:
|
||||
- no overconfident and arrogant tone
|
||||
- no threatening deadlines
|
||||
- no requirement for me to login in your security platform just to read the report
|
||||
- no inflated severity (we can discuss the CVSS score after confirming the issue)
|
||||
- no LLMs usage as part of your report description or followup communication
|
||||
|
||||
In case the vulnerability is confirmed, within another couple days I'll try to submit a fix, GitHub security advisory and CVE with remediation steps and **minimal details** regarding the found exploit to minimize giving too much hints to malicious actors (you'll be credited both in the fix release notes and in the public report).
|
||||
Reports that don't follow the above will NOT be reviewed no matter of their validity _(you are of course free to publish whatever you want; see also [#7718](https://github.com/pocketbase/pocketbase/discussions/7718))_.
|
||||
|
||||
### Please:
|
||||
**Or in other words - a simple _"Hey I think I found a security issue when I do X"_ is enough.**
|
||||
|
||||
- DO NOT use LLM as part of your report or email communication - it is extremely frustrating to spend an hour or more reading a wall of generated text, writing an elaborate reply and then to receive another generic LLM prompt response in return.
|
||||
I try to be as responsive as possible and usually address security issues within couple days but if you didn't receive a reply from me for more than a week it is very likely that your email was flagged and in that case please open a GitHub issue or discussion just mentioning that you found a vulnerability and want to report it so that I can see the notification and will try to contact you for more details.
|
||||
|
||||
- DO NOT reserve and publish MITRE CVE number on your own _(I prefer to do it through the GitHub Security advisory)_ and try to communicate first privately the details to better understand how the code is being used and whether the supposed vulnerability can be actually exploited in any real practical scenarios. Otherwise you are risking needlessly causing scaremongering and annoyance for users that rely on security scanners as part of their CI/CD pipeline.
|
||||
In case the vulnerability is confirmed:
|
||||
|
||||
- Wait before publicly disclosing and sharing details about the found vulnerability, **ideally at least 5 days after the fix**, to make it harder to exploit and give enough time for users to patch their instances _(you are free to provide a PoC and as much details as you want in your own blog/gist/etc.)_.
|
||||
- I'll start working on a local fix.
|
||||
- Once the fix is implemented locally, I'll publish a pre-announcement with a scheduled release date _(and when possible an approximate release time)_.
|
||||
- After the release, I'll publish a GitHub security advisory and CVE with remediation steps and **minimal** details regarding the found exploit _(you are free to publish PoC and more details in your own blog, gist, etc. but it is advised to wait at least a week after the release to allow enough time for people to patch their instances before making it more publicly known)_.
|
||||
|
||||
### Below is a short list of previous reports that are NOT considered security issues:
|
||||
|
||||
@@ -84,6 +91,14 @@ In many places where applicable we've tried to minimize the impact by using cons
|
||||
If you think that there is a place where we can improve the handling without hurting too much the user experience, feel free to open a regular public issue and it will be considered.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Attack-vectors relying on social engineering</strong></summary>
|
||||
|
||||
Reports for attacks relying on various social engineering tactics _(e.g. tricking someone to click on a link)_ are valid concerns but usually out of the security scope of the project as there are a lot of cases where the APIs are deliberately designed for minimal friction.
|
||||
|
||||
If you have concerns for such attack, feel free to open a regular public issue and we can eventually try to reconsider adding extra guards when feasible _(or at least properly document the existing behavior)_.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong><code>disintegration/imaging</code> CVE-2023-36308</strong></summary>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '>=1.26.3'
|
||||
go-version: '>=1.26.4'
|
||||
|
||||
# This step usually is not needed because the /ui/dist is pregenerated locally
|
||||
# but its here to ensure that each release embeds the latest admin ui artifacts.
|
||||
|
||||
@@ -1,3 +1,68 @@
|
||||
## v0.39.3
|
||||
|
||||
- Fixed JS error on `file` settings `maxSelect` change ([#7731](https://github.com/pocketbase/pocketbase/issues/7731)).
|
||||
|
||||
- Apply the `Ctrl+S` record panel save shortcut only if it is the current top open modal.
|
||||
|
||||
- Fixed `number` settings validator to not ignore 0 `max` value.
|
||||
|
||||
- Normalized field settings validation error messages and tooltips.
|
||||
|
||||
|
||||
## v0.39.2
|
||||
|
||||
- Fixed records list UI sorting ([#7724](https://github.com/pocketbase/pocketbase/issues/7724)).
|
||||
|
||||
- Don't clear the date input on invalid value while still typing ([#7726](https://github.com/pocketbase/pocketbase/issues/7726)).
|
||||
|
||||
- Return `filepath.SkipDir` in the `pb_hooks` dirs watcher to avoid unnecessary iterating over `node_modules` and `.*` prefixed hidden dirs (`.DS_Store`, `.git`, etc.).
|
||||
|
||||
- Show the "Affected rows" SQL console message only if non-empty to avoid ambiguity with drivers that don't support returning the affected rows count.
|
||||
|
||||
- Updated `modernc.org/sqlite` to v1.52.0 ([SQLite 3.53.2](https://sqlite.org/src/timeline?from=version-3.53.0&to=version-3.53.2&to2=branch-3.53&y=ci)).
|
||||
|
||||
|
||||
## v0.39.1
|
||||
|
||||
- Fixed multiple select options wrapping ([#7720](https://github.com/pocketbase/pocketbase/issues/7720)).
|
||||
|
||||
- Return the hidden record data fields for superusers realtime subscribers ([#7721](https://github.com/pocketbase/pocketbase/issues/7721)).
|
||||
|
||||
- Added default panic-recover handling for the cron jobs to avoid terminating the server on panic.
|
||||
|
||||
- Bumped the min Go GitHub action version to 1.26.4 as it includes some [minor security fixes](https://github.com/golang/go/issues?q=milestone%3AGo1.26.4).
|
||||
|
||||
|
||||
## v0.39.0
|
||||
|
||||
- Added new "SQL console" section under _Settings > Debug_ allowing executing any raw SQL query from the UI ([#2236](https://github.com/pocketbase/pocketbase/issues/2236); [#7638](https://github.com/pocketbase/pocketbase/discussions/7638)).
|
||||
_Note that this is intended for one-off analytic queries, the occasional `VACUUM`/`PRAGMA optimize` or debug purposes and not as the primary interface for interacting with your PocketBase data because it can break your application if not used with proper care!_
|
||||
|
||||
- Send system email alerts to superusers in case of an error with the automated backups ([#7698](https://github.com/pocketbase/pocketbase/issues/7698)).
|
||||
|
||||
- Various minor improvements and fixes:
|
||||
- fixed logs bulk selection export error
|
||||
- optimized logs and records list rendering
|
||||
- allowed word breaking in labels
|
||||
- text contrast improvements
|
||||
- registered missing `oidc2` and `oidc3` option fields
|
||||
- updated default email template texts for consistency
|
||||
- updated `modernc.org/sqlite` to v1.51.0
|
||||
- etc.
|
||||
|
||||
|
||||
## v0.38.2
|
||||
|
||||
- Added `RealtimeConnectRequestEvent.MaxTimeout` field to specify the absolute max duration a realtime connection can remain open (default to 30mins).
|
||||
_This is in addition to the `IdeTimeout` of 5mins in order to prevent misuse and to allow the GC to run more regularly._
|
||||
|
||||
- Added extra checks for the connected user IP in the realtime APIs to prevent bruteforce guest subscription update attempts and to serve as an extra protection for the "all-in-one" OAuth2 realtime handler.
|
||||
|
||||
- Don't reset the records list pagination on record update ([#7694](https://github.com/pocketbase/pocketbase/issues/7694)).
|
||||
|
||||
- Updated all `golang.org/x/` packages to cover the recent [security fixes](https://groups.google.com/g/golang-announce/c/PdiGK3xulk4) _(none of them should be a critical issue in PocketBase but nonetheless it is advised to update)_.
|
||||
|
||||
|
||||
## v0.38.1
|
||||
|
||||
- Silenced the superuser IPs confirmation if there is no change.
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
> For the most recent versions, please refer to [CHANGELOG.md](./CHANGELOG.md)
|
||||
---
|
||||
|
||||
## v0.22.46
|
||||
|
||||
- (_Backported from v0.39.1_) Bumped the min Go GitHub action version to 1.26.4 as it includes some [minor security fixes](https://github.com/golang/go/issues?q=milestone%3AGo1.26.4).
|
||||
|
||||
|
||||
## v0.22.45
|
||||
|
||||
- (_Backported from v0.38.2_) Updated all `golang.org/x/` packages to cover the recent [security fixes](https://groups.google.com/g/golang-announce/c/PdiGK3xulk4) _(none of them should be a critical issue in PocketBase but nonetheless it is advised to update)_.
|
||||
|
||||
|
||||
## v0.22.44
|
||||
|
||||
- (_Backported from v0.38.1_) Force unset the auth state of existing realtime connections on user tokenKey change.
|
||||
|
||||
@@ -48,6 +48,7 @@ func NewRouter(app core.App) (*router.Router[*core.RequestEvent], error) {
|
||||
bindBatchApi(app, apiGroup)
|
||||
bindRealtimeApi(app, apiGroup)
|
||||
bindHealthApi(app, apiGroup)
|
||||
bindSQLApi(app, apiGroup)
|
||||
|
||||
// UI routes
|
||||
bindUIExtensions(app)
|
||||
|
||||
+46
-3
@@ -28,6 +28,9 @@ const clientsChunkSize = 150
|
||||
// RealtimeClientAuthKey is the name of the realtime client store key that holds its auth state.
|
||||
const RealtimeClientAuthKey = "auth"
|
||||
|
||||
// RealtimeClientIPKey is the name of the realtime client store key that holds the IP of the connected client.
|
||||
const RealtimeClientIPKey = "pbRealtimeClientIP"
|
||||
|
||||
// bindRealtimeApi registers the realtime api endpoints.
|
||||
func bindRealtimeApi(app core.App, rg *router.RouterGroup[*core.RequestEvent]) {
|
||||
sub := rg.Group("/realtime")
|
||||
@@ -63,8 +66,12 @@ func realtimeConnect(e *core.RequestEvent) error {
|
||||
|
||||
connectEvent := new(core.RealtimeConnectRequestEvent)
|
||||
connectEvent.RequestEvent = e
|
||||
connectEvent.Client = subscriptions.NewDefaultClient()
|
||||
connectEvent.IdleTimeout = 5 * time.Minute
|
||||
connectEvent.MaxTimeout = 30 * time.Minute
|
||||
connectEvent.Client = subscriptions.NewDefaultClient()
|
||||
|
||||
// could be used as an optional cross-reference check in other API endpoints
|
||||
connectEvent.Client.Set(RealtimeClientIPKey, e.RealIP())
|
||||
|
||||
return e.App.OnRealtimeConnectRequest().Trigger(connectEvent, func(ce *core.RealtimeConnectRequestEvent) error {
|
||||
// register new subscription client
|
||||
@@ -73,7 +80,7 @@ func realtimeConnect(e *core.RequestEvent) error {
|
||||
e.App.SubscriptionsBroker().Unregister(ce.Client.Id())
|
||||
}()
|
||||
|
||||
ce.App.Logger().Debug("Realtime connection established.", slog.String("clientId", ce.Client.Id()))
|
||||
ce.App.Logger().Debug("Realtime connection established", slog.String("clientId", ce.Client.Id()))
|
||||
|
||||
// signalize established connection (aka. fire "connect" message)
|
||||
connectMsgEvent := new(core.RealtimeMessageEvent)
|
||||
@@ -99,12 +106,19 @@ func realtimeConnect(e *core.RequestEvent) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// start a max lifetime timer to prevent accumulating too much
|
||||
// connection resources and to allow the GC to run more regularly
|
||||
maxTimer := time.NewTimer(ce.MaxTimeout)
|
||||
defer maxTimer.Stop()
|
||||
|
||||
// start an idle timer to keep track of inactive/forgotten connections
|
||||
idleTimer := time.NewTimer(ce.IdleTimeout)
|
||||
defer idleTimer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-maxTimer.C:
|
||||
cancelRequest()
|
||||
case <-idleTimer.C:
|
||||
cancelRequest()
|
||||
case msg, ok := <-ce.Client.Channel():
|
||||
@@ -186,6 +200,21 @@ func realtimeSetSubscriptions(e *core.RequestEvent) error {
|
||||
return e.NotFoundError("Missing or invalid client id.", err)
|
||||
}
|
||||
|
||||
// for just in case to prevent someone changing a guest subscription
|
||||
//
|
||||
// note1: this is an extra precaution against clientId bruteforce attempts
|
||||
// for installations allowing longer realtime connections duration
|
||||
//
|
||||
// note2: custom registered clients (aka. those without IP in the store)
|
||||
// are excluded from the check for backward compatibility
|
||||
clientIP, _ := client.Get(RealtimeClientIPKey).(string)
|
||||
if clientIP != "" && clientIP != e.RealIP() {
|
||||
return e.BadRequestError(
|
||||
"Invalid realtime client.",
|
||||
errors.New("the subscription request IP doesn't match with the realtime client IP"),
|
||||
)
|
||||
}
|
||||
|
||||
// for now allow only guest->auth upgrades and any other auth change is forbidden
|
||||
clientAuth, _ := client.Get(RealtimeClientAuthKey).(*core.Record)
|
||||
if clientAuth != nil && !isSameAuth(clientAuth, e.Auth) {
|
||||
@@ -208,7 +237,7 @@ func realtimeSetSubscriptions(e *core.RequestEvent) error {
|
||||
e.Client.Subscribe(e.Subscriptions...)
|
||||
|
||||
e.App.Logger().Debug(
|
||||
"Realtime subscriptions updated.",
|
||||
"Realtime subscriptions updated",
|
||||
slog.String("clientId", e.Client.Id()),
|
||||
slog.Any("subscriptions", e.Subscriptions),
|
||||
)
|
||||
@@ -626,6 +655,20 @@ func realtimeBroadcastRecord(app core.App, action string, record *core.Record, d
|
||||
// which exact fields the client subscription requested or has permissions to access
|
||||
cleanRecord := record.Fresh()
|
||||
|
||||
// -------------------------------------------
|
||||
// @todo consider with the refactoring whether
|
||||
// the default enriching used by the regular APIs
|
||||
// can be reused here too to avoid eventual future
|
||||
// discrepencies in the record event data
|
||||
//
|
||||
// https://github.com/pocketbase/pocketbase/issues/7721
|
||||
// -------------------------------------------
|
||||
|
||||
// enable hidden fields for superuser subscribers
|
||||
if requestInfo.HasSuperuserAuth() {
|
||||
cleanRecord.Unhide(collection.Fields.FieldNames()...)
|
||||
}
|
||||
|
||||
// trigger the enrich hooks
|
||||
enrichErr := triggerRecordEnrichHooks(app, requestInfo, []*core.Record{cleanRecord}, func() error {
|
||||
// apply expand
|
||||
|
||||
+177
-8
@@ -26,6 +26,7 @@ func TestRealtimeConnect(t *testing.T) {
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/realtime",
|
||||
Timeout: 100 * time.Millisecond,
|
||||
Headers: map[string]string{"x-test-ip": "127.0.0.2"},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`id:`,
|
||||
@@ -37,6 +38,17 @@ func TestRealtimeConnect(t *testing.T) {
|
||||
"OnRealtimeConnectRequest": 1,
|
||||
"OnRealtimeMessageSend": 1,
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.Settings().TrustedProxy.Headers = []string{"x-test-ip"}
|
||||
|
||||
app.OnRealtimeConnectRequest().BindFunc(func(e *core.RealtimeConnectRequestEvent) error {
|
||||
if ip, _ := e.Client.Get(apis.RealtimeClientIPKey).(string); ip != "127.0.0.2" {
|
||||
t.Fatalf("Expected IP %q, got %q", "127.0.0.2", ip)
|
||||
}
|
||||
|
||||
return e.Next()
|
||||
})
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if len(app.SubscriptionsBroker().Clients()) != 0 {
|
||||
t.Errorf("Expected the subscribers to be removed after connection close, found %d", len(app.SubscriptionsBroker().Clients()))
|
||||
@@ -102,7 +114,8 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
|
||||
resetClient := func() {
|
||||
client.Unsubscribe()
|
||||
client.Set(apis.RealtimeClientAuthKey, nil)
|
||||
client.Unset(apis.RealtimeClientAuthKey)
|
||||
client.Unset(apis.RealtimeClientIPKey)
|
||||
}
|
||||
|
||||
validSubscriptionsLimit := make([]string, 1000)
|
||||
@@ -208,6 +221,26 @@ func TestRealtimeSubscribe(t *testing.T) {
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "existing client with different IP",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/realtime",
|
||||
Body: strings.NewReader(`{"clientId":"` + client.Id() + `","subscriptions":["test"]}`),
|
||||
Headers: map[string]string{"x-test-ip": "127.0.0.2"},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.Settings().TrustedProxy.Headers = []string{"x-test-ip"}
|
||||
|
||||
client.Set(apis.RealtimeClientIPKey, "127.0.0.1")
|
||||
|
||||
app.SubscriptionsBroker().Register(client)
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
resetClient()
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "existing client with valid topic length",
|
||||
Method: http.MethodPost,
|
||||
@@ -429,7 +462,10 @@ func TestRealtimeAuthRecordDeleteEvent(t *testing.T) {
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
apis.NewRouter(testApp)
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authRecord1, err := testApp.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
@@ -487,7 +523,10 @@ func TestRealtimeAuthRecordUpdateEvent(t *testing.T) {
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
apis.NewRouter(testApp)
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authRecord1, err := testApp.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
@@ -518,12 +557,130 @@ func TestRealtimeAuthRecordUpdateEvent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRealtimeRecordHiddenFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testApp, _ := tests.NewTestApp()
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// create temp collection with hidden fields
|
||||
testCollection := core.NewBaseCollection("test_realtime")
|
||||
testCollection.ListRule = types.Pointer("@request.auth.id != ''")
|
||||
testCollection.Fields.Add(
|
||||
&core.TextField{Name: "public"},
|
||||
&core.TextField{Name: "hidden", Hidden: true},
|
||||
)
|
||||
if err := testApp.Save(testCollection); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testSubscription := testCollection.Name + "/*"
|
||||
|
||||
// register guest subscriber
|
||||
guestClient := subscriptions.NewDefaultClient()
|
||||
guestClient.Subscribe(testSubscription)
|
||||
testApp.SubscriptionsBroker().Register(guestClient)
|
||||
|
||||
// register regular user subscriber
|
||||
regular, err := testApp.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
regularClient := subscriptions.NewDefaultClient()
|
||||
regularClient.Set(apis.RealtimeClientAuthKey, regular)
|
||||
regularClient.Subscribe(testSubscription)
|
||||
testApp.SubscriptionsBroker().Register(regularClient)
|
||||
|
||||
// register superuser subscriber
|
||||
superuser, err := testApp.FindAuthRecordByEmail(core.CollectionNameSuperusers, "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
superuserClient := subscriptions.NewDefaultClient()
|
||||
superuserClient.Set(apis.RealtimeClientAuthKey, superuser)
|
||||
superuserClient.Subscribe(testSubscription)
|
||||
testApp.SubscriptionsBroker().Register(superuserClient)
|
||||
|
||||
enrichCalls := map[string]int{}
|
||||
testApp.OnRecordEnrich(testCollection.Name).BindFunc(func(e *core.RecordEnrichEvent) error {
|
||||
var id string
|
||||
if e.RequestInfo.Auth != nil {
|
||||
id = e.RequestInfo.Auth.Id
|
||||
}
|
||||
enrichCalls[id]++
|
||||
return e.Next()
|
||||
})
|
||||
|
||||
timeout := time.After(3 * time.Second)
|
||||
done := make(chan struct{})
|
||||
|
||||
// collect first received messages
|
||||
var regularMessageData, superuserMessageData string
|
||||
go func() {
|
||||
regularMessageData = string((<-regularClient.Channel()).Data)
|
||||
superuserMessageData = string((<-superuserClient.Channel()).Data)
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
// broadcast create message
|
||||
testRecord := core.NewRecord(testCollection)
|
||||
testRecord.Set("public", "test1")
|
||||
testRecord.Set("hidden", "test2")
|
||||
if err := testApp.Save(testRecord); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for the events
|
||||
select {
|
||||
case <-timeout:
|
||||
t.Fatal("realtime test messages timeout")
|
||||
case <-done:
|
||||
// ready
|
||||
}
|
||||
|
||||
if total := len(enrichCalls); total != 2 {
|
||||
t.Fatalf("Expected %d enrich hook calls, got %d", 2, total)
|
||||
}
|
||||
|
||||
if total := enrichCalls[regular.Id]; total != 1 {
|
||||
t.Fatalf("Expected exactly 1 regular user enrich hook call, got %d", total)
|
||||
}
|
||||
|
||||
if total := enrichCalls[superuser.Id]; total != 1 {
|
||||
t.Fatalf("Expected exactly 1 superuser enrich hook call, got %d", total)
|
||||
}
|
||||
|
||||
// validate messages content
|
||||
scenarios := map[string]bool{
|
||||
"regular message public field should exist": strings.Contains(regularMessageData, `"public":`),
|
||||
"regular message hidden field should NOT exist": !strings.Contains(regularMessageData, `"hidden":`),
|
||||
"superuser message public field should exist": strings.Contains(superuserMessageData, `"public":`),
|
||||
"superuser message hidden field should exist": strings.Contains(superuserMessageData, `"hidden":`),
|
||||
}
|
||||
for name, valid := range scenarios {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if !valid {
|
||||
t.Fatal("Invalid realtime message expectation")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRealtimeAuthRecordUnsetOnTokenKeyRefresh(t *testing.T) {
|
||||
testApp, _ := tests.NewTestApp()
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
apis.NewRouter(testApp)
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authRecord1, err := testApp.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
@@ -557,7 +714,10 @@ func TestRealtimeAuthRecordUnsetOnCollectionSecretChange(t *testing.T) {
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
apis.NewRouter(testApp)
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
usersCollection, err := testApp.FindCollectionByNameOrId("users")
|
||||
if err != nil {
|
||||
@@ -633,7 +793,10 @@ func TestRealtimeAuthRecordUnsetOnCollectionDelete(t *testing.T) {
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
apis.NewRouter(testApp)
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
usersCollection, err := testApp.FindCollectionByNameOrId("users")
|
||||
if err != nil {
|
||||
@@ -736,7 +899,10 @@ func TestRealtimeCustomAuthModelDeleteEvent(t *testing.T) {
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
apis.NewRouter(testApp)
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authRecord1, err := testApp.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
@@ -793,7 +959,10 @@ func TestRealtimeCustomAuthModelUpdateEvent(t *testing.T) {
|
||||
defer testApp.Cleanup()
|
||||
|
||||
// init realtime handlers
|
||||
apis.NewRouter(testApp)
|
||||
_, err := apis.NewRouter(testApp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
authRecord, err := testApp.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
|
||||
@@ -45,6 +45,13 @@ func recordConfirmVerification(e *core.RequestEvent) error {
|
||||
if !wasVerified {
|
||||
e.Record.SetVerified(true)
|
||||
|
||||
// similar to the OTP auth, we enforce an extra password reset
|
||||
// guard as this way is less prone to pre-hijacking attacks
|
||||
// in case the password auth is eventually enabled later
|
||||
if !e.Record.Collection().PasswordAuth.Enabled {
|
||||
e.Record.SetRandomPassword()
|
||||
}
|
||||
|
||||
if err := e.App.Save(e.Record); err != nil {
|
||||
return firstApiError(err, e.BadRequestError("An error occurred while saving the verified state.", err))
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func TestRecordConfirmVerification(t *testing.T) {
|
||||
}
|
||||
|
||||
if user.Verified() {
|
||||
t.Fatalf("Expected the user to be unverified before the confirmation")
|
||||
t.Fatal("Expected the user to be unverified before the confirmation")
|
||||
}
|
||||
|
||||
// ensure that there is at least one pre-existing OAuth2 link
|
||||
@@ -152,6 +152,85 @@ func TestRecordConfirmVerification(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "valid token (disabled password auth)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/collections/users/confirm-verification",
|
||||
Body: strings.NewReader(`{
|
||||
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjRxMXhsY2xtZmxva3UzMyIsImV4cCI6MjUyNDYwNDQ2MSwidHlwZSI6InZlcmlmaWNhdGlvbiIsImNvbGxlY3Rpb25JZCI6Il9wYl91c2Vyc19hdXRoXyIsImVtYWlsIjoidGVzdEBleGFtcGxlLmNvbSJ9.SetHpu2H-x-q4TIUz-xiQjwi7MNwLCLvSs4O0hUSp0E"
|
||||
}`),
|
||||
ExpectedStatus: 204,
|
||||
ExpectedEvents: map[string]int{
|
||||
"*": 0,
|
||||
"OnRecordConfirmVerificationRequest": 1,
|
||||
"OnModelUpdate": 1,
|
||||
"OnModelValidate": 1,
|
||||
"OnModelUpdateExecute": 1,
|
||||
"OnModelAfterUpdateSuccess": 1,
|
||||
"OnRecordUpdate": 1,
|
||||
"OnRecordValidate": 1,
|
||||
"OnRecordUpdateExecute": 1,
|
||||
"OnRecordAfterUpdateSuccess": 1,
|
||||
// unverified->verified external auths removal
|
||||
"OnModelDelete": 2,
|
||||
"OnModelDeleteExecute": 2,
|
||||
"OnModelAfterDeleteSuccess": 2,
|
||||
"OnRecordDelete": 2,
|
||||
"OnRecordDeleteExecute": 2,
|
||||
"OnRecordAfterDeleteSuccess": 2,
|
||||
},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
user.Collection().PasswordAuth.Enabled = false
|
||||
if err = app.Save(user.Collection()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if user.Verified() {
|
||||
t.Fatal("Expected the user to be unverified before the confirmation")
|
||||
}
|
||||
|
||||
if !user.ValidatePassword("1234567890") {
|
||||
t.Fatal("Expected password to be valid")
|
||||
}
|
||||
|
||||
// ensure that there is at least one pre-existing OAuth2 link
|
||||
externalAuths, err := app.FindAllExternalAuthsByRecord(user)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(externalAuths) == 0 {
|
||||
t.Fatal("Expected at least one external auths")
|
||||
}
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
user, err := app.FindAuthRecordByEmail("users", "test@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !user.Verified() {
|
||||
t.Fatalf("Expected the user to be verified after the confirmation")
|
||||
}
|
||||
|
||||
if user.ValidatePassword("1234567890") {
|
||||
t.Fatal("Expected the user password to be reset")
|
||||
}
|
||||
|
||||
// ensure that all pre-existing OAuth2 links are cleared
|
||||
externalAuths, err := app.FindAllExternalAuthsByRecord(user)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(externalAuths) > 0 {
|
||||
t.Fatalf("Expected all external auths to be cleared, found %d", len(externalAuths))
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "valid token (already verified)",
|
||||
Method: http.MethodPost,
|
||||
|
||||
@@ -55,6 +55,16 @@ func oauth2SubscriptionRedirect(e *core.RequestEvent) error {
|
||||
}
|
||||
defer client.Unsubscribe(oauth2SubscriptionTopic)
|
||||
|
||||
// additional check to minimize the risk of XSRF attack vectors
|
||||
//
|
||||
// note: custom registered clients (aka. those without IP in the store)
|
||||
// are excluded from the check for backward compatibility
|
||||
clientIP, _ := client.Get(RealtimeClientIPKey).(string)
|
||||
if clientIP != "" && clientIP != e.RealIP() {
|
||||
e.App.Logger().Debug("The client IP that completed the authentication is different from the one that initialized the OAuth2 realtime connection")
|
||||
return failureRedirect(e)
|
||||
}
|
||||
|
||||
// temporary store the Apple user's name so that it can be later retrieved with the authWithOAuth2 call
|
||||
// (see https://github.com/pocketbase/pocketbase/issues/7090)
|
||||
if data.AppleUser != "" && data.Error == "" && data.Code != "" {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"github.com/pocketbase/pocketbase/tools/subscriptions"
|
||||
@@ -16,9 +17,9 @@ import (
|
||||
func TestRecordAuthWithOAuth2Redirect(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
clientStubs := make([]map[string]subscriptions.Client, 0, 10)
|
||||
clientStubs := make([]map[string]subscriptions.Client, 0, 11)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
for i := 0; i < 11; i++ {
|
||||
c1 := subscriptions.NewDefaultClient()
|
||||
|
||||
c2 := subscriptions.NewDefaultClient()
|
||||
@@ -335,6 +336,28 @@ func TestRecordAuthWithOAuth2Redirect(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "client with different IP",
|
||||
Method: http.MethodGet,
|
||||
URL: "/api/oauth2-redirect?code=123&state=" + clientStubs[10]["c3"].Id(),
|
||||
Headers: map[string]string{"x-test-ip": "127.0.0.2"},
|
||||
BeforeTestFunc: func(t testing.TB, app *tests.TestApp, e *core.ServeEvent) {
|
||||
app.Settings().TrustedProxy.Headers = []string{"x-test-ip"}
|
||||
|
||||
clientStubs[10]["c3"].Set(apis.RealtimeClientIPKey, "127.0.0.1")
|
||||
|
||||
beforeTestFunc(clientStubs[10], map[string][]string{
|
||||
"c3": {`"state":"` + clientStubs[10]["c3"].Id(), `"code":"123"`},
|
||||
})(t, app, e)
|
||||
},
|
||||
ExpectedStatus: http.StatusTemporaryRedirect,
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
app.Store().Get("cancelFunc").(context.CancelFunc)()
|
||||
|
||||
checkFailureRedirect(t, app, res)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
|
||||
@@ -68,7 +68,7 @@ func recordAuthWithOTP(e *core.RequestEvent) error {
|
||||
otpId := e.OTP.Id
|
||||
otpSentTo := e.OTP.SentTo()
|
||||
|
||||
// eagerly delete the OTP to avoid unnecessery double delete model hook calls
|
||||
// eagerly delete the OTP to avoid unnecessary double delete model hook calls
|
||||
// triggered by the password change below
|
||||
err := e.App.Delete(e.OTP)
|
||||
if err != nil {
|
||||
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
package apis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/router"
|
||||
)
|
||||
|
||||
const (
|
||||
runSQLMaxRows = 1000
|
||||
runSQLMaxTimeout = 3 * time.Minute
|
||||
)
|
||||
|
||||
// bindSQLApi registers the SQL api endpoints.
|
||||
func bindSQLApi(app core.App, rg *router.RouterGroup[*core.RequestEvent]) {
|
||||
subGroup := rg.Group("/sql").Bind(RequireSuperuserAuth())
|
||||
subGroup.POST("", runSQL)
|
||||
}
|
||||
|
||||
func runSQL(e *core.RequestEvent) error {
|
||||
// extra precaution in case manually invoked from somewhere else
|
||||
if !e.HasSuperuserAuth() {
|
||||
return e.ForbiddenError("", nil)
|
||||
}
|
||||
|
||||
form := runSQLForm{}
|
||||
|
||||
err := e.BindBody(&form)
|
||||
if err != nil {
|
||||
return firstApiError(err, e.BadRequestError("An error occurred while loading the submitted data.", err))
|
||||
}
|
||||
|
||||
err = form.validate()
|
||||
if err != nil {
|
||||
return firstApiError(err, e.BadRequestError("An error occurred while validating the submitted data.", err))
|
||||
}
|
||||
|
||||
result, err := executeQuery(e.App, form.Query, runSQLMaxRows)
|
||||
if err != nil {
|
||||
return firstApiError(err, e.BadRequestError("Failed to execute query. Raw error:\n"+err.Error(), nil))
|
||||
}
|
||||
|
||||
return e.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
type runSQLForm struct {
|
||||
Query string `form:"query" json:"query"`
|
||||
}
|
||||
|
||||
func (form *runSQLForm) validate() error {
|
||||
return validation.ValidateStruct(form,
|
||||
validation.Field(&form.Query, validation.Required, validation.Length(0, 5000)),
|
||||
)
|
||||
}
|
||||
|
||||
type runSQLResultColumn struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Nullable bool `json:"nullable"`
|
||||
}
|
||||
|
||||
type runSQLResult struct {
|
||||
ExecTime int64 `json:"execTime"`
|
||||
AffectedRows int64 `json:"affectedRows"`
|
||||
Columns []runSQLResultColumn `json:"columns"`
|
||||
Rows [][]any `json:"rows"`
|
||||
}
|
||||
|
||||
var knownWriteQueryPrefixes = []string{
|
||||
"INSERT", "CREATE", "UPDATE", "DELETE",
|
||||
"DROP", "DETACH", "ALTER", "REPLACE",
|
||||
}
|
||||
|
||||
func executeQuery(app core.App, query string, maxRows int) (*runSQLResult, error) {
|
||||
query = strings.TrimSpace(query)
|
||||
if query == "" {
|
||||
// see https://github.com/mattn/go-sqlite3/issues/950
|
||||
return nil, errors.New("empty query")
|
||||
}
|
||||
|
||||
var isPossibleWriteQuery bool
|
||||
|
||||
// loosely check the query type
|
||||
ucQuery := strings.ToUpper(query)
|
||||
if !strings.HasPrefix(ucQuery, "SELECT") {
|
||||
for _, prefix := range knownWriteQueryPrefixes {
|
||||
if strings.HasPrefix(ucQuery, prefix) {
|
||||
isPossibleWriteQuery = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note: don't extend the request context to minimize the risk of
|
||||
// causing integrity issues with custom non-transaction mutations
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), runSQLMaxTimeout)
|
||||
defer cancelFunc()
|
||||
|
||||
result := &runSQLResult{
|
||||
// init empty slices to ensure "[]" serialization
|
||||
Columns: []runSQLResultColumn{},
|
||||
Rows: [][]any{},
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
defer func() {
|
||||
result.ExecTime = time.Since(now).Milliseconds()
|
||||
}()
|
||||
|
||||
// assume write/mutation query
|
||||
// ---------------------------------------------------------------
|
||||
if isPossibleWriteQuery {
|
||||
// auto wrap in transaction in case there are multiple inline queries
|
||||
txErr := app.RunInTransaction(func(txApp core.App) error {
|
||||
execResult, err := txApp.NonconcurrentDB().NewQuery(query).WithContext(ctx).Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result.AffectedRows, err = execResult.RowsAffected()
|
||||
if err != nil {
|
||||
// non-critical error (e.g. not supported by the driver)
|
||||
txApp.Logger().Debug("Unable to fetch affected rows", slog.String("error", err.Error()))
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if txErr != nil {
|
||||
return nil, txErr
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// assume query returning rows
|
||||
// ---------------------------------------------------------------
|
||||
rows, err := app.ConcurrentDB().NewQuery(query).WithContext(ctx).Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// populate columns info
|
||||
// ---
|
||||
colTypes, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, colType := range colTypes {
|
||||
col := runSQLResultColumn{
|
||||
Name: colType.Name(),
|
||||
Type: colType.DatabaseTypeName(),
|
||||
}
|
||||
col.Nullable, _ = colType.Nullable()
|
||||
|
||||
result.Columns = append(result.Columns, col)
|
||||
}
|
||||
|
||||
// populate rows
|
||||
// ---
|
||||
for rows.Next() {
|
||||
if len(result.Rows) >= maxRows {
|
||||
break
|
||||
}
|
||||
|
||||
rowData := make([]any, len(colTypes))
|
||||
for i := 0; i < len(colTypes); i++ {
|
||||
var v *string
|
||||
rowData[i] = &v
|
||||
}
|
||||
|
||||
err := rows.Scan(rowData...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result.Rows = append(result.Rows, rowData)
|
||||
}
|
||||
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package apis_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
)
|
||||
|
||||
func TestSQLRun(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
scenarios := []tests.ApiScenario{
|
||||
{
|
||||
Name: "guest",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"select 1"}`),
|
||||
ExpectedStatus: 401,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "regular user",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"select 1"}`),
|
||||
Headers: map[string]string{
|
||||
// users, test2@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6Im9hcDY0MGNvdDR5cnUycyIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoiX3BiX3VzZXJzX2F1dGhfIiwiZXhwIjoyNTI0NjA0NDYxLCJyZWZyZXNoYWJsZSI6dHJ1ZX0.GfJo6EHIobgas_AXt-M-tj5IoQendPnrkMSe9ExuSEY",
|
||||
},
|
||||
ExpectedStatus: 403,
|
||||
ExpectedContent: []string{`"data":{}`},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "superuser",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"select 1"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"execTime":`,
|
||||
`"affectedRows":0`,
|
||||
`"columns":[{"name":"1","type":"","nullable":true}]`,
|
||||
`"rows":[["1"]]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "empty query",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":""}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
`"data":{`,
|
||||
`"query":{`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "invalid query",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"invalid"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
`"data":{}`,
|
||||
`Raw error:`,
|
||||
`SQL logic error`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "query with length above the limit",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"` + strings.Repeat("a", 5001) + `"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
`"data":{`,
|
||||
`"query":{`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "query with length equal to the limit",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"select '` + strings.Repeat("a", 4985) + `' as id"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"execTime":`,
|
||||
`"affectedRows":0`,
|
||||
`"columns":[{"name":"id","type":"","nullable":true}]`,
|
||||
`"rows":[["aaa`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "single write query",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"create table test_sql_table(id int primary key)"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if !app.HasTable("test_sql_table") {
|
||||
t.Fatalf("Missing expected new %q table", "test_sql_table")
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"execTime":`,
|
||||
`"affectedRows":0`,
|
||||
`"columns":[]`,
|
||||
`"rows":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "multiple write queries",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"create table test_sql_table(id int primary key);insert into test_sql_table(id)VALUES(1)"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
var total int
|
||||
err := app.DB().NewQuery("select count(*) from test_sql_table").Row(&total)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if total != 1 {
|
||||
t.Fatalf("Expected exactly 1 row, found: %d", total)
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"execTime":`,
|
||||
`"affectedRows":1`,
|
||||
`"columns":[]`,
|
||||
`"rows":[]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "multiple write queries (transaction rollback)",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"create table test_sql_table(id int primary key);insert into test_sql_table(id)VALUES(1);invalid"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
AfterTestFunc: func(t testing.TB, app *tests.TestApp, res *http.Response) {
|
||||
if app.HasTable("test_sql_table") {
|
||||
t.Fatalf("Expected table %q to not be created", "test_sql_table")
|
||||
}
|
||||
},
|
||||
ExpectedStatus: 400,
|
||||
ExpectedContent: []string{
|
||||
`"data":{}`,
|
||||
`Raw error:`,
|
||||
`SQL logic error`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
{
|
||||
Name: "multiple read queries",
|
||||
Method: http.MethodPost,
|
||||
URL: "/api/sql",
|
||||
Body: strings.NewReader(`{"query":"select 1;select 2"}`),
|
||||
Headers: map[string]string{
|
||||
// superusers, test@example.com
|
||||
"Authorization": "eyJhbGciOiJIUzI1NiJ9.eyJpZCI6InN5d2JoZWNuaDQ2cmhtMCIsInR5cGUiOiJhdXRoIiwiY29sbGVjdGlvbklkIjoicGJjXzMxNDI2MzU4MjMiLCJleHAiOjI1MjQ2MDQ0NjEsInJlZnJlc2hhYmxlIjp0cnVlfQ.UXgO3j-0BumcugrFjbd7j0M4MQvbrLggLlcu_YNGjoY",
|
||||
},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContent: []string{
|
||||
`"execTime":`,
|
||||
`"affectedRows":0`,
|
||||
// only the result of the last query should be returned
|
||||
`"columns":[{"name":"2","type":"","nullable":true}]`,
|
||||
`"rows":[["2"]]`,
|
||||
},
|
||||
ExpectedEvents: map[string]int{"*": 0},
|
||||
},
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
scenario.Test(t)
|
||||
}
|
||||
}
|
||||
@@ -322,6 +322,19 @@ func (app *BaseApp) registerAutobackupHooks() {
|
||||
slog.String("name", name),
|
||||
slog.String("error", err.Error()),
|
||||
)
|
||||
|
||||
alertError := sendSystemAlertToAllSuperusers(
|
||||
app,
|
||||
"Autobackup failure",
|
||||
"Failed to create/upload automated backup. Raw error:\n"+err.Error(),
|
||||
)
|
||||
if alertError != nil {
|
||||
app.Logger().Warn(
|
||||
"[Backup cron] Failed to send backup error alerts",
|
||||
slog.String("name", name),
|
||||
slog.String("error", alertError.Error()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
maxKeep := app.Settings().Backups.CronMaxKeep
|
||||
|
||||
@@ -70,7 +70,7 @@ func (m *Collection) setDefaultAuthOptions() {
|
||||
},
|
||||
AuthToken: TokenConfig{
|
||||
Secret: security.RandomString(50),
|
||||
Duration: 604800, // 7 days
|
||||
Duration: 432000, // 5days
|
||||
},
|
||||
PasswordResetToken: TokenConfig{
|
||||
Secret: security.RandomString(50),
|
||||
@@ -82,7 +82,7 @@ func (m *Collection) setDefaultAuthOptions() {
|
||||
},
|
||||
VerificationToken: TokenConfig{
|
||||
Secret: security.RandomString(50),
|
||||
Duration: 259200, // 3days
|
||||
Duration: 86400, // 1day
|
||||
},
|
||||
FileToken: TokenConfig{
|
||||
Secret: security.RandomString(50),
|
||||
|
||||
@@ -18,6 +18,7 @@ var defaultVerificationTemplate = EmailTemplate{
|
||||
<p>
|
||||
<a class="btn" href="` + EmailPlaceholderAppURL + "/_/#/auth/confirm-verification/" + EmailPlaceholderToken + `" target="_blank" rel="noopener">Verify</a>
|
||||
</p>
|
||||
<p><i>If you didn't recently register, please ignore this email.</i></p>
|
||||
<p>
|
||||
Thanks,<br/>
|
||||
` + EmailPlaceholderAppName + ` team
|
||||
@@ -31,7 +32,7 @@ var defaultResetPasswordTemplate = EmailTemplate{
|
||||
<p>
|
||||
<a class="btn" href="` + EmailPlaceholderAppURL + "/_/#/auth/confirm-password-reset/" + EmailPlaceholderToken + `" target="_blank" rel="noopener">Reset password</a>
|
||||
</p>
|
||||
<p><i>If you didn't ask to reset your password, you can ignore this email.</i></p>
|
||||
<p><i>If you didn't ask to reset your password, please ignore this email.</i></p>
|
||||
<p>
|
||||
Thanks,<br/>
|
||||
` + EmailPlaceholderAppName + ` team
|
||||
@@ -45,7 +46,7 @@ var defaultConfirmEmailChangeTemplate = EmailTemplate{
|
||||
<p>
|
||||
<a class="btn" href="` + EmailPlaceholderAppURL + "/_/#/auth/confirm-email-change/" + EmailPlaceholderToken + `" target="_blank" rel="noopener">Confirm new email</a>
|
||||
</p>
|
||||
<p><i>If you didn't ask to change your email address, you can ignore this email.</i></p>
|
||||
<p><i>If you didn't ask to change your email address, please ignore this email.</i></p>
|
||||
<p>
|
||||
Thanks,<br/>
|
||||
` + EmailPlaceholderAppName + ` team
|
||||
|
||||
+17
-1
@@ -448,8 +448,24 @@ type RealtimeConnectRequestEvent struct {
|
||||
|
||||
Client subscriptions.Client
|
||||
|
||||
// note: modifying it after the connect has no effect
|
||||
// IdleTimeout specifies the max duration to wait for a new message
|
||||
// before closing the connection.
|
||||
//
|
||||
// Modifying the value after the connection has been established has no effect.
|
||||
//
|
||||
// Defaults to 5 minutes.
|
||||
IdleTimeout time.Duration
|
||||
|
||||
// MaxTimeout specifies the maximum duration a realtime connection
|
||||
// can remain open (including even if there are ongoing messages).
|
||||
//
|
||||
// Once the specified duration expires, the current connection will
|
||||
// be terminated, until a client reconnect is issued (if the client is still active).
|
||||
//
|
||||
// Modifying the value after the connection has been established has no effect.
|
||||
//
|
||||
// Defaults to 30 minutes.
|
||||
MaxTimeout time.Duration
|
||||
}
|
||||
|
||||
type RealtimeMessageEvent struct {
|
||||
|
||||
+18
-6
@@ -2,7 +2,6 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
@@ -23,6 +22,12 @@ var (
|
||||
_ SetterFinder = (*NumberField)(nil)
|
||||
)
|
||||
|
||||
var (
|
||||
onlyIntValidationError = validation.NewError("validation_only_int_constraint", "Decimal numbers are not allowed")
|
||||
minNumberValidationError = validation.NewError("validation_min_number_constraint", "Must be greater or equal than {{.min}}")
|
||||
maxNumberValidationError = validation.NewError("validation_max_number_constraint", "Must be less or equal than {{.max}}")
|
||||
)
|
||||
|
||||
// NumberField defines "number" type field for storing numeric (float64) value.
|
||||
//
|
||||
// The respective zero record field value is 0.
|
||||
@@ -151,15 +156,15 @@ func (f *NumberField) ValidateValue(ctx context.Context, app App, record *Record
|
||||
}
|
||||
|
||||
if f.OnlyInt && val != float64(int64(val)) {
|
||||
return validation.NewError("validation_only_int_constraint", "Decimal numbers are not allowed")
|
||||
return onlyIntValidationError
|
||||
}
|
||||
|
||||
if f.Min != nil && val < *f.Min {
|
||||
return validation.NewError("validation_min_number_constraint", fmt.Sprintf("Must be larger than %f", *f.Min))
|
||||
return minNumberValidationError.SetParams(map[string]any{"min": *f.Min})
|
||||
}
|
||||
|
||||
if f.Max != nil && val > *f.Max {
|
||||
return validation.NewError("validation_max_number_constraint", fmt.Sprintf("Must be less than %f", *f.Max))
|
||||
return maxNumberValidationError.SetParams(map[string]any{"max": *f.Max})
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -171,7 +176,14 @@ func (f *NumberField) ValidateSettings(ctx context.Context, app App, collection
|
||||
validation.By(f.checkOnlyInt),
|
||||
}
|
||||
if f.Min != nil && f.Max != nil {
|
||||
maxRules = append(maxRules, validation.Min(*f.Min))
|
||||
maxRules = append(maxRules, validation.By(func(value interface{}) error {
|
||||
// similar to validation.Min but doesn't ignore zero values
|
||||
v, _ := value.(*float64)
|
||||
if v == nil || f.Min == nil || *v >= *f.Min {
|
||||
return nil
|
||||
}
|
||||
return minNumberValidationError.SetParams(map[string]any{"min": *f.Min})
|
||||
}))
|
||||
}
|
||||
|
||||
return validation.ValidateStruct(f,
|
||||
@@ -190,7 +202,7 @@ func (f *NumberField) checkOnlyInt(value any) error {
|
||||
}
|
||||
|
||||
if *v != float64(int64(*v)) {
|
||||
return validation.NewError("validation_only_int_constraint", "Decimal numbers are not allowed.")
|
||||
return onlyIntValidationError
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -237,7 +237,7 @@ func TestNumberFieldValidateSettings(t *testing.T) {
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"decumal min",
|
||||
"decimal min",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
@@ -248,7 +248,7 @@ func TestNumberFieldValidateSettings(t *testing.T) {
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"decumal min (onlyInt)",
|
||||
"decimal min (onlyInt)",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
@@ -272,7 +272,7 @@ func TestNumberFieldValidateSettings(t *testing.T) {
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"decumal max",
|
||||
"decimal max",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
@@ -283,7 +283,7 @@ func TestNumberFieldValidateSettings(t *testing.T) {
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"decumal max (onlyInt)",
|
||||
"decimal max (onlyInt)",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
@@ -307,19 +307,31 @@ func TestNumberFieldValidateSettings(t *testing.T) {
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"min > max",
|
||||
"min > max (0)",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
Name: "test",
|
||||
Min: types.Pointer(2.0),
|
||||
Max: types.Pointer(1.0),
|
||||
Max: types.Pointer(0.0),
|
||||
}
|
||||
},
|
||||
[]string{"max"},
|
||||
},
|
||||
{
|
||||
"min <= max",
|
||||
"min (0) > max",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
Name: "test",
|
||||
Min: types.Pointer(0.0),
|
||||
Max: types.Pointer(-1.0),
|
||||
}
|
||||
},
|
||||
[]string{"max"},
|
||||
},
|
||||
{
|
||||
"min == max",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
@@ -330,6 +342,18 @@ func TestNumberFieldValidateSettings(t *testing.T) {
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"min < max",
|
||||
func() *core.NumberField {
|
||||
return &core.NumberField{
|
||||
Id: "test",
|
||||
Name: "test",
|
||||
Min: types.Pointer(2.0),
|
||||
Max: types.Pointer(3.0),
|
||||
}
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ func (app *BaseApp) registerMFAHooks() {
|
||||
err = e.App.DeleteAllMFAsByRecord(e.Record)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"[%s] failed to delete all previos MFAs for record %q: %w",
|
||||
"[%s] failed to delete all previous MFAs for record %q: %w",
|
||||
e.Record.Collection().Name,
|
||||
e.Record.Id,
|
||||
err,
|
||||
|
||||
@@ -38,11 +38,8 @@ func TestNotifyWatcher_SettingsUpdate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ctx, cancelCtx := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancelCtx()
|
||||
|
||||
sem := semaphore.NewWeighted(1)
|
||||
sem.Acquire(ctx, 1)
|
||||
timeout := time.After(3 * time.Second)
|
||||
done := make(chan struct{})
|
||||
|
||||
app1.OnSettingsReload().BindFunc(func(e *core.SettingsReloadEvent) error {
|
||||
testEvents.SetFunc(app1, func(old int) int {
|
||||
@@ -53,7 +50,9 @@ func TestNotifyWatcher_SettingsUpdate(t *testing.T) {
|
||||
|
||||
app2.OnSettingsReload().BindFunc(func(e *core.SettingsReloadEvent) error {
|
||||
testEvents.SetFunc(app2, func(old int) int {
|
||||
sem.Release(1)
|
||||
defer func() {
|
||||
done <- struct{}{}
|
||||
}()
|
||||
|
||||
return old + 1
|
||||
})
|
||||
@@ -66,8 +65,13 @@ func TestNotifyWatcher_SettingsUpdate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// block until released or timeouted
|
||||
sem.Acquire(ctx, 1)
|
||||
// wait for the event
|
||||
select {
|
||||
case <-timeout:
|
||||
t.Fatal("app2 reload event timeout")
|
||||
case <-done:
|
||||
// ready
|
||||
}
|
||||
|
||||
if app1Total := testEvents.Get(app1); app1Total != 1 {
|
||||
t.Fatalf("Expected 1 app1 event, got %d", app1Total)
|
||||
@@ -161,6 +165,7 @@ func TestNotifyWatcher_CollectionsUpdate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := app1.Delete(dummyCollection); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// block until released or timeouted
|
||||
|
||||
+1
-1
@@ -139,7 +139,7 @@ func (app *BaseApp) registerOTPHooks() {
|
||||
err := e.App.DeleteAllOTPsByRecord(e.Record)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
"[%s] failed to delete all previos OTPs for record %q: %w",
|
||||
"[%s] failed to delete all previous OTPs for record %q: %w",
|
||||
e.Record.Collection().Name,
|
||||
e.Record.Id,
|
||||
err,
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"html"
|
||||
"html/template"
|
||||
"net/mail"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/mailer"
|
||||
)
|
||||
|
||||
const systemAlertHTML = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<style>
|
||||
body, html {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
color: #16161a;
|
||||
background: #fff;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: normal;
|
||||
font-family: Source Sans Pro, sans-serif, emoji;
|
||||
}
|
||||
body {
|
||||
padding: 20px 30px;
|
||||
}
|
||||
p {
|
||||
display: block;
|
||||
margin: 10px 0;
|
||||
font-family: inherit;
|
||||
}
|
||||
small {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
em, i {
|
||||
font-style: italic;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
.alert {
|
||||
padding: 15px;
|
||||
background: #e4e8ec;
|
||||
border-radius: 5px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>{{.AppName}} system alert occurred:</p>
|
||||
<p class="alert"><strong>{{.AlertDetails}}</strong></p>
|
||||
<p>For more information you could explore the logs in the dashboard of your application.</p>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
// sendSystemAlertToAllSuperusers sends a system error alert to all superusers.
|
||||
//
|
||||
// note: unexported for now until there is clarity around the planned log level alerts.
|
||||
func sendSystemAlertToAllSuperusers(app App, subject string, details string) error {
|
||||
superusers, err := app.FindAllRecords(CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var alertErrors []error
|
||||
for _, superuser := range superusers {
|
||||
err := sendSystemAlert(app, superuser, subject, details)
|
||||
if err != nil {
|
||||
alertErrors = append(alertErrors, err)
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Join(alertErrors...)
|
||||
}
|
||||
|
||||
// sendSystemAlert sends a system error alert to a single superuser.
|
||||
//
|
||||
// note: unexported for now until there is clarity around the planned log level alerts.
|
||||
func sendSystemAlert(app App, superuser *Record, subject string, details string) error {
|
||||
if !superuser.IsSuperuser() {
|
||||
return errors.New("system alerts can be sent only to superusers")
|
||||
}
|
||||
|
||||
if subject == "" || details == "" {
|
||||
return errors.New("system alerts subject and details are required")
|
||||
}
|
||||
|
||||
data := struct {
|
||||
AppName string
|
||||
AlertDetails string
|
||||
}{
|
||||
AppName: app.Settings().Meta.AppName,
|
||||
AlertDetails: details,
|
||||
}
|
||||
|
||||
tpl := template.New("system_alert")
|
||||
|
||||
var parseErr error
|
||||
tpl, parseErr = tpl.Parse(systemAlertHTML)
|
||||
if parseErr != nil {
|
||||
return parseErr
|
||||
}
|
||||
|
||||
var buff bytes.Buffer
|
||||
executeErr := tpl.Execute(&buff, data)
|
||||
if executeErr != nil {
|
||||
return executeErr
|
||||
}
|
||||
|
||||
message := &mailer.Message{
|
||||
From: mail.Address{
|
||||
Name: app.Settings().Meta.SenderName,
|
||||
Address: app.Settings().Meta.SenderAddress,
|
||||
},
|
||||
To: []mail.Address{{Address: superuser.Email()}},
|
||||
Subject: "[" + app.Settings().Meta.AppName + " system alert] " + html.EscapeString(subject),
|
||||
HTML: buff.String(),
|
||||
}
|
||||
|
||||
return app.NewMailClient().Send(message)
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSendSystemAlert(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testDataDir, err := os.MkdirTemp("", "sendSystemAlert_pb_data")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(testDataDir)
|
||||
|
||||
testApp := NewBaseApp(BaseAppConfig{
|
||||
DataDir: testDataDir,
|
||||
})
|
||||
defer testApp.ResetBootstrapState()
|
||||
|
||||
if err := testApp.Bootstrap(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := createTestSuperusers(testApp, 3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
superuser, err := testApp.FindAuthRecordByEmail(CollectionNameSuperusers, "test1@example.com")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var sendCalls int
|
||||
testApp.OnMailerSend().BindFunc(func(e *MailerEvent) error {
|
||||
sendCalls++
|
||||
|
||||
if !strings.Contains(e.Message.Subject, "test_subject") {
|
||||
t.Fatalf("Missing %q in Message.Subject:\n%s", "test_subject", e.Message.Subject)
|
||||
}
|
||||
|
||||
if !strings.Contains(e.Message.HTML, "test_details") {
|
||||
t.Fatalf("Missing %q in Message.HTML:\n%s", "test_details", e.Message.HTML)
|
||||
}
|
||||
|
||||
if len(e.Message.To) != 1 || e.Message.To[0].Address != "test1@example.com" {
|
||||
t.Fatalf("Expected To address %q, got %v", "test1@example.com", e.Message.To)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
sendSystemAlert(testApp, superuser, "test_subject", "test_details")
|
||||
|
||||
if sendCalls != 1 {
|
||||
t.Fatalf("Expected 1 mail send call, got %d", sendCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendSystemAlertToAllSuperusers(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testDataDir, err := os.MkdirTemp("", "sendSystemAlertToAllSuperusers_pb_data")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(testDataDir)
|
||||
|
||||
testApp := NewBaseApp(BaseAppConfig{
|
||||
DataDir: testDataDir,
|
||||
})
|
||||
defer testApp.ResetBootstrapState()
|
||||
|
||||
if err := testApp.Bootstrap(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := createTestSuperusers(testApp, 3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var sendCalls int
|
||||
testApp.OnMailerSend().BindFunc(func(e *MailerEvent) error {
|
||||
sendCalls++
|
||||
|
||||
if !strings.Contains(e.Message.Subject, "test_subject") {
|
||||
t.Fatalf("Missing %q in Message.Subject:\n%s", "test_subject", e.Message.Subject)
|
||||
}
|
||||
|
||||
if !strings.Contains(e.Message.HTML, "test_details") {
|
||||
t.Fatalf("Missing %q in Message.HTML:\n%s", "test_details", e.Message.HTML)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
sendSystemAlertToAllSuperusers(testApp, "test_subject", "test_details")
|
||||
|
||||
if sendCalls != 3 {
|
||||
t.Fatalf("Expected 3 mail send calls, got %d", sendCalls)
|
||||
}
|
||||
}
|
||||
|
||||
func createTestSuperusers(app App, total int) error {
|
||||
superusersCollection, err := app.FindCollectionByNameOrId(CollectionNameSuperusers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range total {
|
||||
superuser := NewRecord(superusersCollection)
|
||||
superuser.SetEmail("test" + strconv.Itoa(i+1) + "@example.com")
|
||||
superuser.SetRandomPassword()
|
||||
|
||||
if err := app.Save(superuser); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -17,12 +17,12 @@ require (
|
||||
github.com/pocketbase/tygoja v0.0.0-20250812183945-97ffe055281f
|
||||
github.com/spf13/cast v1.10.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
golang.org/x/crypto v0.51.0
|
||||
golang.org/x/image v0.40.0
|
||||
golang.org/x/net v0.54.0
|
||||
golang.org/x/crypto v0.52.0
|
||||
golang.org/x/image v0.41.0
|
||||
golang.org/x/net v0.55.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sync v0.20.0
|
||||
modernc.org/sqlite v1.50.1
|
||||
modernc.org/sqlite v1.52.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -40,7 +40,7 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/sys v0.44.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
modernc.org/libc v1.72.3 // indirect
|
||||
|
||||
@@ -82,23 +82,23 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.40.0 h1:Tw4GyDXMo+daZN1znreBRC3VayR1aLFUyUEOLUdW1a8=
|
||||
golang.org/x/image v0.40.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=
|
||||
golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo=
|
||||
golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
@@ -134,8 +134,8 @@ modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.50.1 h1:l+cQvn0sd0zJJtfygGHuQJ5AjlrwXmWPw4KP3ZMwr9w=
|
||||
modernc.org/sqlite v1.50.1/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
|
||||
modernc.org/sqlite v1.52.0 h1:p4dhYh2tXZCiyaqHwRVJDjIGKWyXayiQpThxgDzJaxo=
|
||||
modernc.org/sqlite v1.52.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
expectedDriverVersion = "v1.50.1"
|
||||
expectedDriverVersion = "v1.52.0"
|
||||
expectedLibcVersion = "v1.72.3"
|
||||
|
||||
// ModerncDepsCheckHookId is the id of the hook that performs the modernc.org/* deps checks.
|
||||
|
||||
+4195
-4164
File diff suppressed because it is too large.
Load diff
@@ -443,11 +443,16 @@ func (p *plugin) watchHooks() error {
|
||||
//
|
||||
// @todo replace once recursive watcher is added (https://github.com/fsnotify/fsnotify/issues/18)
|
||||
dirsErr := filepath.WalkDir(watchDir, func(path string, entry fs.DirEntry, err error) error {
|
||||
// skip access failures, hidden directories, node_modules, etc.
|
||||
if err != nil || !entry.IsDir() || entry.Name() == "node_modules" || strings.HasPrefix(entry.Name(), ".") {
|
||||
// ignore access failures and non-dir entries
|
||||
if err != nil || !entry.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// skip traversing hidden directories and node_modules
|
||||
if strings.HasPrefix(entry.Name(), ".") || entry.Name() == "node_modules" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
return watcher.Add(path)
|
||||
})
|
||||
if dirsErr != nil {
|
||||
|
||||
@@ -36,10 +36,10 @@ migrate((app) => {
|
||||
},
|
||||
"authRule": "",
|
||||
"authToken": {
|
||||
"duration": 604800
|
||||
"duration": 432000
|
||||
},
|
||||
"confirmEmailChangeTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Confirm your {APP_NAME} new email address"
|
||||
},
|
||||
"createRule": null,
|
||||
@@ -170,18 +170,18 @@ migrate((app) => {
|
||||
"duration": 1800
|
||||
},
|
||||
"resetPasswordTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Reset your {APP_NAME} password"
|
||||
},
|
||||
"system": true,
|
||||
"type": "auth",
|
||||
"updateRule": null,
|
||||
"verificationTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p><i>If you didn't recently register, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Verify your {APP_NAME} email"
|
||||
},
|
||||
"verificationToken": {
|
||||
"duration": 259200
|
||||
"duration": 86400
|
||||
},
|
||||
"viewRule": "id = \"1\""
|
||||
});
|
||||
@@ -218,10 +218,10 @@ func init() {
|
||||
},
|
||||
"authRule": "",
|
||||
"authToken": {
|
||||
"duration": 604800
|
||||
"duration": 432000
|
||||
},
|
||||
"confirmEmailChangeTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Confirm your {APP_NAME} new email address"
|
||||
},
|
||||
"createRule": null,
|
||||
@@ -352,18 +352,18 @@ func init() {
|
||||
"duration": 1800
|
||||
},
|
||||
"resetPasswordTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Reset your {APP_NAME} password"
|
||||
},
|
||||
"system": true,
|
||||
"type": "auth",
|
||||
"updateRule": null,
|
||||
"verificationTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p><i>If you didn't recently register, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Verify your {APP_NAME} email"
|
||||
},
|
||||
"verificationToken": {
|
||||
"duration": 259200
|
||||
"duration": 86400
|
||||
},
|
||||
"viewRule": "id = \"1\""
|
||||
}` + "`" + `
|
||||
@@ -489,10 +489,10 @@ migrate((app) => {
|
||||
},
|
||||
"authRule": "",
|
||||
"authToken": {
|
||||
"duration": 604800
|
||||
"duration": 432000
|
||||
},
|
||||
"confirmEmailChangeTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Confirm your {APP_NAME} new email address"
|
||||
},
|
||||
"createRule": null,
|
||||
@@ -623,18 +623,18 @@ migrate((app) => {
|
||||
"duration": 1800
|
||||
},
|
||||
"resetPasswordTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Reset your {APP_NAME} password"
|
||||
},
|
||||
"system": false,
|
||||
"type": "auth",
|
||||
"updateRule": null,
|
||||
"verificationTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p><i>If you didn't recently register, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Verify your {APP_NAME} email"
|
||||
},
|
||||
"verificationToken": {
|
||||
"duration": 259200
|
||||
"duration": 86400
|
||||
},
|
||||
"viewRule": "id = \"1\""
|
||||
});
|
||||
@@ -674,10 +674,10 @@ func init() {
|
||||
},
|
||||
"authRule": "",
|
||||
"authToken": {
|
||||
"duration": 604800
|
||||
"duration": 432000
|
||||
},
|
||||
"confirmEmailChangeTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to confirm your new email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-email-change/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Confirm new email</a>\n</p>\n<p><i>If you didn't ask to change your email address, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Confirm your {APP_NAME} new email address"
|
||||
},
|
||||
"createRule": null,
|
||||
@@ -808,18 +808,18 @@ func init() {
|
||||
"duration": 1800
|
||||
},
|
||||
"resetPasswordTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, you can ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Click on the button below to reset your password.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-password-reset/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Reset password</a>\n</p>\n<p><i>If you didn't ask to reset your password, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Reset your {APP_NAME} password"
|
||||
},
|
||||
"system": false,
|
||||
"type": "auth",
|
||||
"updateRule": null,
|
||||
"verificationTemplate": {
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"body": "<p>Hello,</p>\n<p>Thank you for joining us at {APP_NAME}.</p>\n<p>Click on the button below to verify your email address.</p>\n<p>\n <a class=\"btn\" href=\"{APP_URL}/_/#/auth/confirm-verification/{TOKEN}\" target=\"_blank\" rel=\"noopener\">Verify</a>\n</p>\n<p><i>If you didn't recently register, please ignore this email.</i></p>\n<p>\n Thanks,<br/>\n {APP_NAME} team\n</p>",
|
||||
"subject": "Verify your {APP_NAME} email"
|
||||
},
|
||||
"verificationToken": {
|
||||
"duration": 259200
|
||||
"duration": 86400
|
||||
},
|
||||
"viewRule": "id = \"1\""
|
||||
}` + "`" + `
|
||||
|
||||
+3
-1
@@ -14,6 +14,8 @@ import (
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/routine"
|
||||
)
|
||||
|
||||
// Cron is a crontab-like struct for tasks/jobs scheduling.
|
||||
@@ -222,7 +224,7 @@ func (c *Cron) runDue(t time.Time) {
|
||||
|
||||
for _, j := range c.jobs {
|
||||
if j.schedule.IsDue(moment) {
|
||||
go j.Run()
|
||||
routine.FireAndForget(j.Run)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ func FireAndForget(f func(), wg ...*sync.WaitGroup) {
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Println("RECOVERED FROM PANIC (safe to ignore):", err)
|
||||
log.Println("[FireAndForget] RECOVERED FROM PANIC:", err)
|
||||
|
||||
stack := make([]byte, 2<<10) // 2 KB
|
||||
length := runtime.Stack(stack, false)
|
||||
|
||||
@@ -12,4 +12,4 @@ PB_DOCS_URL = "https://pocketbase.io/docs"
|
||||
PB_JS_SDK_URL = "https://github.com/pocketbase/js-sdk"
|
||||
PB_DART_SDK_URL = "https://github.com/pocketbase/dart-sdk"
|
||||
PB_RELEASES = "https://github.com/pocketbase/pocketbase/releases"
|
||||
PB_VERSION = "v0.38.1"
|
||||
PB_VERSION = "v0.39.3"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";function r(r){let i=app.utils.getApiExampleURL(),a={collectionId:r.id,collectionName:r.name},o=[{title:200,value:JSON.stringify({token:`...JWT...`,record:Object.assign(a,app.utils.getDummyFieldsData(r))},null,2)},{title:401,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";function r(r){let i=app.utils.getApiExampleURL(),a={collectionId:r.id,collectionName:r.name},o=[{title:200,value:JSON.stringify({token:`...JWT...`,record:Object.assign(a,app.utils.getDummyFieldsData(r))},null,2)},{title:401,value:`
|
||||
{
|
||||
"status": 401,
|
||||
"message": "The request requires valid record authorization token to be set.",
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";function r(r){let i=app.utils.getApiExampleURL(),a={collectionId:r.id,collectionName:r.name},o=[{title:200,value:JSON.stringify({token:`...JWT...`,record:Object.assign(a,app.utils.getDummyFieldsData(r)),meta:{id:`abc123`,name:`John Doe`,username:`john.doe`,email:`test@example.com`,avatarURL:`https://example.com/avatar.png`,accessToken:`...`,refreshToken:`...`,expiry:`2022-01-01 10:00:00.123Z`,isNew:!1,rawUser:{}}},null,2)},{title:400,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";function r(r){let i=app.utils.getApiExampleURL(),a={collectionId:r.id,collectionName:r.name},o=[{title:200,value:JSON.stringify({token:`...JWT...`,record:Object.assign(a,app.utils.getDummyFieldsData(r)),meta:{id:`abc123`,name:`John Doe`,username:`john.doe`,email:`test@example.com`,avatarURL:`https://example.com/avatar.png`,accessToken:`...`,refreshToken:`...`,expiry:`2022-01-01 10:00:00.123Z`,isNew:!1,rawUser:{}}},null,2)},{title:400,value:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "An error occurred while submitting the form.",
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";function r(e){let n=app.utils.getApiExampleURL(),r=[{title:`OTP request`,content:i},{title:`OTP auth`,content:a}],o=store({activeActionIndex:0});return t.div({pbEvent:`apiPreviewAuthWithOTP`,className:`content`},t.p(null,`Authenticate with an one-time/short-lived password (OTP).`),t.p(null,`On successful authentication the user will be also marked as verified (if the OTP source is email and the user is not verified already).`),t.p(null,`Note that when requesting an OTP we return an `,t.code(null,`otpId`),` even if a user with the provided email doesn't exist as a very basic enumeration protection.`),app.components.codeBlockTabs({className:`sdk-examples m-t-sm`,historyKey:`pbLastSDK`,tabs:[{title:`JS SDK`,language:`js`,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";function r(e){let n=app.utils.getApiExampleURL(),r=[{title:`OTP request`,content:i},{title:`OTP auth`,content:a}],o=store({activeActionIndex:0});return t.div({pbEvent:`apiPreviewAuthWithOTP`,className:`content`},t.p(null,`Authenticate with an one-time/short-lived password (OTP).`),t.p(null,`On successful authentication the user will be also marked as verified (if the OTP source is email and the user is not verified already).`),t.p(null,`Note that when requesting an OTP we return an `,t.code(null,`otpId`),` even if a user with the provided email doesn't exist as a very basic enumeration protection.`),app.components.codeBlockTabs({className:`sdk-examples m-t-sm`,historyKey:`pbLastSDK`,tabs:[{title:`JS SDK`,language:`js`,value:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${n}');
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";function r(r){let i=app.utils.getApiExampleURL(),a=r.passwordAuth?.identityFields||[],o=a.length==0?`NONE`:`YOUR_`+a.join(`_OR_`).toUpperCase(),s={collectionId:r.id,collectionName:r.name},c=[{title:200,value:JSON.stringify({token:`...JWT...`,record:Object.assign(s,app.utils.getDummyFieldsData(r))},null,2)},{title:400,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";function r(r){let i=app.utils.getApiExampleURL(),a=r.passwordAuth?.identityFields||[],o=a.length==0?`NONE`:`YOUR_`+a.join(`_OR_`).toUpperCase(),s={collectionId:r.id,collectionName:r.name},c=[{title:200,value:JSON.stringify({token:`...JWT...`,record:Object.assign(s,app.utils.getDummyFieldsData(r))},null,2)},{title:400,value:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Failed to authenticate.",
|
||||
File renamed without changes.
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";function r(r){let s=app.utils.getApiExampleURL(),c=r.createRule===null,l=r.type===`auth`,u=l?[`password`,`verified`,`email`,`emailVisibility`]:[],d=r.fields?.filter(e=>!e.hidden&&e.type!=`autodate`&&!u.includes(e.name))||[],f={collectionId:r.id,collectionName:r.name},p=[{title:200,value:JSON.stringify(Object.assign(f,app.utils.getDummyFieldsData(r)),null,2)},{title:400,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";function r(r){let s=app.utils.getApiExampleURL(),c=r.createRule===null,l=r.type===`auth`,u=l?[`password`,`verified`,`email`,`emailVisibility`]:[],d=r.fields?.filter(e=>!e.hidden&&e.type!=`autodate`&&!u.includes(e.name))||[],f={collectionId:r.id,collectionName:r.name},p=[{title:200,value:JSON.stringify(Object.assign(f,app.utils.getDummyFieldsData(r)),null,2)},{title:400,value:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Failed to create record.",
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";import{t as r}from"./filterSyntax-Df6AnI5x.js";function i(i){let a=app.utils.getApiExampleURL(),o=i.listRule===null,s={collectionId:i.id,collectionName:i.name},c=[{title:200,value:JSON.stringify({page:1,perPage:30,totalPages:1,totalItems:2,items:[Object.assign(s,app.utils.getDummyFieldsData(i)),Object.assign(s,app.utils.getDummyFieldsData(i))]},null,2)},{title:400,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";import{t as r}from"./filterSyntax-CEI8Ui7Q.js";function i(i){let a=app.utils.getApiExampleURL(),o=i.listRule===null,s={collectionId:i.id,collectionName:i.name},c=[{title:200,value:JSON.stringify({page:1,perPage:30,totalPages:1,totalItems:2,items:[Object.assign(s,app.utils.getDummyFieldsData(i)),Object.assign(s,app.utils.getDummyFieldsData(i))]},null,2)},{title:400,value:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Something went wrong while processing your request.",
|
||||
Vendored
+2
-2
@@ -1,10 +1,10 @@
|
||||
import{t as e}from"./fieldsInfo-Bz62125-.js";function n(n){let r=app.utils.getApiExampleURL(),i=store({isLoading:!1,authMethods:[],get responses(){return[{title:200,value:i.isLoading?`...`:JSON.stringify(i.authMethods,null,2)},{title:404,value:`
|
||||
import{t as e}from"./fieldsInfo-CiLcXgNq.js";function n(n){let r=app.utils.getApiExampleURL(),i=store({isLoading:!1,authMethods:[],get responses(){return[{title:200,value:i.isLoading?`...`:JSON.stringify(i.authMethods,null,2)},{title:404,value:`
|
||||
{
|
||||
"status": 404,
|
||||
"message": "Missing collection context.",
|
||||
"data": {}
|
||||
}
|
||||
`}]}});async function a(){i.isLoading=!0;try{i.authMethods=await app.pb.collection(n.name).listAuthMethods()}catch(e){e.isAbort&&app.pb.checkApiError(e)}i.isLoading=!1}return t.div({pbEvent:`apiPreviewListAuthMethods`,className:`content`,onmount:()=>{a()}},t.p(null,`Returns a public list with all allowed ${n.name} authentication methods.`),app.components.codeBlockTabs({className:`sdk-examples m-t-sm`,historyKey:`pbLastSDK`,tabs:[{title:`JS SDK`,language:`js`,value:`
|
||||
`}]}});async function a(){i.isLoading=!0;try{i.authMethods=await app.pb.collection(n.name).listAuthMethods(),i.isLoading=!1}catch(e){e?.isAbort||(app.checkApiError(e),i.isLoading=!1)}}return t.div({pbEvent:`apiPreviewListAuthMethods`,className:`content`,onmount:()=>{a()}},t.p(null,`Returns a public list with all allowed ${n.name} authentication methods.`),app.components.codeBlockTabs({className:`sdk-examples m-t-sm`,historyKey:`pbLastSDK`,tabs:[{title:`JS SDK`,language:`js`,value:`
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${r}');
|
||||
Vendored
File renamed without changes.
File renamed without changes.
+1
-1
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";import{fullDummyPayload as r,primitivesDummyPayload as i,replaceDummyPayloadPlaceholder as a}from"./docsCreate-Be3S3y5K.js";function o(o){let s=app.utils.getApiExampleURL(),c=o.updateRule===null,l=o.type===`auth`?[`id`,`password`,`verified`,`email`,`emailVisibility`]:[`id`],u=o.fields?.filter(e=>!e.hidden&&e.type!=`autodate`&&!l.includes(e.name))||[],d={collectionId:o.id,collectionName:o.name},f=[{title:200,value:JSON.stringify(Object.assign(d,app.utils.getDummyFieldsData(o)),null,2)},{title:400,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";import{fullDummyPayload as r,primitivesDummyPayload as i,replaceDummyPayloadPlaceholder as a}from"./docsCreate-DDIyFVY7.js";function o(o){let s=app.utils.getApiExampleURL(),c=o.updateRule===null,l=o.type===`auth`?[`id`,`password`,`verified`,`email`,`emailVisibility`]:[`id`],u=o.fields?.filter(e=>!e.hidden&&e.type!=`autodate`&&!l.includes(e.name))||[],d={collectionId:o.id,collectionName:o.name},f=[{title:200,value:JSON.stringify(Object.assign(d,app.utils.getDummyFieldsData(o)),null,2)},{title:400,value:`
|
||||
{
|
||||
"status": 400,
|
||||
"message": "Failed to create record.",
|
||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
import{t as e}from"./expandInfo-DGS0CLSa.js";import{t as n}from"./fieldsInfo-Bz62125-.js";function r(r){let i=app.utils.getApiExampleURL(),a=r.viewRule===null,o={collectionId:r.id,collectionName:r.name},s=[{title:200,value:JSON.stringify(Object.assign(o,app.utils.getDummyFieldsData(r)),null,2)}];return a&&s.push({title:403,value:`
|
||||
import{t as e}from"./expandInfo-CJ9aeAaN.js";import{t as n}from"./fieldsInfo-CiLcXgNq.js";function r(r){let i=app.utils.getApiExampleURL(),a=r.viewRule===null,o={collectionId:r.id,collectionName:r.name},s=[{title:200,value:JSON.stringify(Object.assign(o,app.utils.getDummyFieldsData(r)),null,2)}];return a&&s.push({title:403,value:`
|
||||
{
|
||||
"status": 403,
|
||||
"message": "Only superusers can access this action.",
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1 +1 @@
|
||||
@font-face{font-display:block;font-family:remixicon;src:url(../fonts/remixicon/remixicon.woff2?t=1734404658139)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:normal;font-weight:400;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-regular.woff2)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:italic;font-weight:400;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-italic.woff2)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:normal;font-weight:600;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-600.woff2)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:italic;font-weight:600;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-600italic.woff2)format("woff2")}@font-face{font-display:swap;font-family:IBM Plex Mono;font-style:normal;font-weight:400;src:url(../fonts/ibm-plex-mono/ibm-plex-mono-v20-latin-regular.woff2)format("woff2")}@font-face{font-display:swap;font-family:IBM Plex Mono;font-style:normal;font-weight:600;src:url(../fonts/ibm-plex-mono/ibm-plex-mono-v20-latin-600.woff2)format("woff2")}.ri-24-hours-fill:before{content:""}.ri-24-hours-line:before{content:""}.ri-4k-fill:before{content:""}.ri-4k-line:before{content:""}.ri-a-b:before{content:""}.ri-account-box-fill:before{content:""}.ri-account-box-line:before{content:""}.ri-account-circle-fill:before{content:""}.ri-account-circle-line:before{content:""}.ri-account-pin-box-fill:before{content:""}.ri-account-pin-box-line:before{content:""}.ri-account-pin-circle-fill:before{content:""}.ri-account-pin-circle-line:before{content:""}.ri-add-box-fill:before{content:""}.ri-add-box-line:before{content:""}.ri-add-circle-fill:before{content:""}.ri-add-circle-line:before{content:""}.ri-add-fill:before{content:""}.ri-add-line:before{content:""}.ri-admin-fill:before{content:""}.ri-admin-line:before{content:""}.ri-advertisement-fill:before{content:""}.ri-advertisement-line:before{content:""}.ri-airplay-fill:before{content:""}.ri-airplay-line:before{content:""}.ri-alarm-fill:before{content:""}.ri-alarm-line:before{content:""}.ri-alarm-warning-fill:before{content:""}.ri-alarm-warning-line:before{content:""}.ri-album-fill:before{content:""}.ri-album-line:before{content:""}.ri-alert-fill:before{content:""}.ri-alert-line:before{content:""}.ri-aliens-fill:before{content:""}.ri-aliens-line:before{content:""}.ri-align-bottom:before{content:""}.ri-align-center:before{content:""}.ri-align-justify:before{content:""}.ri-align-left:before{content:""}.ri-align-right:before{content:""}.ri-align-top:before{content:""}.ri-align-vertically:before{content:""}.ri-alipay-fill:before{content:""}.ri-alipay-line:before{content:""}.ri-amazon-fill:before{content:""}.ri-amazon-line:before{content:""}.ri-anchor-fill:before{content:""}.ri-anchor-line:before{content:""}.ri-ancient-gate-fill:before{content:""}.ri-ancient-gate-line:before{content:""}.ri-ancient-pavilion-fill:before{content:""}.ri-ancient-pavilion-line:before{content:""}.ri-android-fill:before{content:""}.ri-android-line:before{content:""}.ri-angularjs-fill:before{content:""}.ri-angularjs-line:before{content:""}.ri-anticlockwise-2-fill:before{content:""}.ri-anticlockwise-2-line:before{content:""}.ri-anticlockwise-fill:before{content:""}.ri-anticlockwise-line:before{content:""}.ri-app-store-fill:before{content:""}.ri-app-store-line:before{content:""}.ri-apple-fill:before{content:""}.ri-apple-line:before{content:""}.ri-apps-2-fill:before{content:""}.ri-apps-2-line:before{content:""}.ri-apps-fill:before{content:""}.ri-apps-line:before{content:""}.ri-archive-drawer-fill:before{content:""}.ri-archive-drawer-line:before{content:""}.ri-archive-fill:before{content:""}.ri-archive-line:before{content:""}.ri-arrow-down-circle-fill:before{content:""}.ri-arrow-down-circle-line:before{content:""}.ri-arrow-down-fill:before{content:""}.ri-arrow-down-line:before{content:""}.ri-arrow-down-s-fill:before{content:""}.ri-arrow-down-s-line:before{content:""}.ri-arrow-drop-down-fill:before{content:""}.ri-arrow-drop-down-line:before{content:""}.ri-arrow-drop-left-fill:before{content:""}.ri-arrow-drop-left-line:before{content:""}.ri-arrow-drop-right-fill:before{content:""}.ri-arrow-drop-right-line:before{content:""}.ri-arrow-drop-up-fill:before{content:""}.ri-arrow-drop-up-line:before{content:""}.ri-arrow-go-back-fill:before{content:""}.ri-arrow-go-back-line:before{content:""}.ri-arrow-go-forward-fill:before{content:""}.ri-arrow-go-forward-line:before{content:""}.ri-arrow-left-circle-fill:before{content:""}.ri-arrow-left-circle-line:before{content:""}.ri-aLine truncated
|
||||
@font-face{font-display:block;font-family:remixicon;src:url(../fonts/remixicon/remixicon.woff2?t=1734404658139)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:normal;font-weight:400;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-regular.woff2)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:italic;font-weight:400;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-italic.woff2)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:normal;font-weight:600;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-600.woff2)format("woff2")}@font-face{font-display:block;font-family:IBM Plex Sans;font-style:italic;font-weight:600;src:url(../fonts/ibm-plex-sans/ibm-plex-sans-v23-cyrillic_latin_latin-ext-600italic.woff2)format("woff2")}@font-face{font-display:swap;font-family:IBM Plex Mono;font-style:normal;font-weight:400;src:url(../fonts/ibm-plex-mono/ibm-plex-mono-v20-latin-regular.woff2)format("woff2")}@font-face{font-display:swap;font-family:IBM Plex Mono;font-style:normal;font-weight:600;src:url(../fonts/ibm-plex-mono/ibm-plex-mono-v20-latin-600.woff2)format("woff2")}.ri-24-hours-fill:before{content:""}.ri-24-hours-line:before{content:""}.ri-4k-fill:before{content:""}.ri-4k-line:before{content:""}.ri-a-b:before{content:""}.ri-account-box-fill:before{content:""}.ri-account-box-line:before{content:""}.ri-account-circle-fill:before{content:""}.ri-account-circle-line:before{content:""}.ri-account-pin-box-fill:before{content:""}.ri-account-pin-box-line:before{content:""}.ri-account-pin-circle-fill:before{content:""}.ri-account-pin-circle-line:before{content:""}.ri-add-box-fill:before{content:""}.ri-add-box-line:before{content:""}.ri-add-circle-fill:before{content:""}.ri-add-circle-line:before{content:""}.ri-add-fill:before{content:""}.ri-add-line:before{content:""}.ri-admin-fill:before{content:""}.ri-admin-line:before{content:""}.ri-advertisement-fill:before{content:""}.ri-advertisement-line:before{content:""}.ri-airplay-fill:before{content:""}.ri-airplay-line:before{content:""}.ri-alarm-fill:before{content:""}.ri-alarm-line:before{content:""}.ri-alarm-warning-fill:before{content:""}.ri-alarm-warning-line:before{content:""}.ri-album-fill:before{content:""}.ri-album-line:before{content:""}.ri-alert-fill:before{content:""}.ri-alert-line:before{content:""}.ri-aliens-fill:before{content:""}.ri-aliens-line:before{content:""}.ri-align-bottom:before{content:""}.ri-align-center:before{content:""}.ri-align-justify:before{content:""}.ri-align-left:before{content:""}.ri-align-right:before{content:""}.ri-align-top:before{content:""}.ri-align-vertically:before{content:""}.ri-alipay-fill:before{content:""}.ri-alipay-line:before{content:""}.ri-amazon-fill:before{content:""}.ri-amazon-line:before{content:""}.ri-anchor-fill:before{content:""}.ri-anchor-line:before{content:""}.ri-ancient-gate-fill:before{content:""}.ri-ancient-gate-line:before{content:""}.ri-ancient-pavilion-fill:before{content:""}.ri-ancient-pavilion-line:before{content:""}.ri-android-fill:before{content:""}.ri-android-line:before{content:""}.ri-angularjs-fill:before{content:""}.ri-angularjs-line:before{content:""}.ri-anticlockwise-2-fill:before{content:""}.ri-anticlockwise-2-line:before{content:""}.ri-anticlockwise-fill:before{content:""}.ri-anticlockwise-line:before{content:""}.ri-app-store-fill:before{content:""}.ri-app-store-line:before{content:""}.ri-apple-fill:before{content:""}.ri-apple-line:before{content:""}.ri-apps-2-fill:before{content:""}.ri-apps-2-line:before{content:""}.ri-apps-fill:before{content:""}.ri-apps-line:before{content:""}.ri-archive-drawer-fill:before{content:""}.ri-archive-drawer-line:before{content:""}.ri-archive-fill:before{content:""}.ri-archive-line:before{content:""}.ri-arrow-down-circle-fill:before{content:""}.ri-arrow-down-circle-line:before{content:""}.ri-arrow-down-fill:before{content:""}.ri-arrow-down-line:before{content:""}.ri-arrow-down-s-fill:before{content:""}.ri-arrow-down-s-line:before{content:""}.ri-arrow-drop-down-fill:before{content:""}.ri-arrow-drop-down-line:before{content:""}.ri-arrow-drop-left-fill:before{content:""}.ri-arrow-drop-left-line:before{content:""}.ri-arrow-drop-right-fill:before{content:""}.ri-arrow-drop-right-line:before{content:""}.ri-arrow-drop-up-fill:before{content:""}.ri-arrow-drop-up-line:before{content:""}.ri-arrow-go-back-fill:before{content:""}.ri-arrow-go-back-line:before{content:""}.ri-arrow-go-forward-fill:before{content:""}.ri-arrow-go-forward-line:before{content:""}.ri-arrow-left-circle-fill:before{content:""}.ri-arrow-left-circle-line:before{content:""}.ri-aLine truncated
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./docsList-u2AryqHB.js","./expandInfo-CJ9aeAaN.js","./fieldsInfo-CiLcXgNq.js","./filterSyntax-CEI8Ui7Q.js","./docsView-oyuvqZP8.js","./docsCreate-DDIyFVY7.js","./docsUpdate-BzDOztgb.js","./docsListAuthMethods-CYETXzX7.js","./docsAuthWithPassword-NXiq7pZI.js","./docsAuthWithOAuth2-BnaAmEeK.js","./docsAuthWithOTP-75iz1Xmg.js","./docsAuthRefresh-Cq3xTY6M.js"])))=>i.map(i=>d[i]);
|
||||
var e=Object.create,n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getPrototypeOf,o=Object.prototype.hasOwnProperty,s=(e,n)=>()=>(n||(e((n={exports:{}}).exports,n),e=null),n.exports),c=(e,a,s,c)=>{if(a&&typeof a==`object`||typeof a==`function`)for(var l=i(a),u=0,d=l.length,f;u<d;u++)f=l[u],!o.call(e,f)&&f!==s&&n(e,f,{get:(e=>a[e]).bind(null,f),enumerable:!(c=r(a,f))||c.enumerable});return e},l=(r,i,o)=>(o=r==null?{}:e(a(r)),c(i||!r||!r.__esModule?n(o,`default`,{value:r,enumerable:!0}):o,r));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))r(e);new MutationObserver(e=>{for(let n of e)if(n.type===`childList`)for(let e of n.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&r(e)}).observe(document,{childList:!0,subtree:!0});function n(e){let n={};return e.integrity&&(n.integrity=e.integrity),e.referrerPolicy&&(n.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?n.credentials=`include`:e.crossOrigin===`anonymous`?n.credentials=`omit`:n.credentials=`same-origin`,n}function r(e){if(e.ep)return;e.ep=!0;let r=n(e);fetch(e.href,r)}})();var u=`abcdefghijklmnopqrstuvwxyz0123456789`,d=store({hash:window.location.hash});window.addEventListener(`hashchange`,()=>{d.hash=window.location.hash}),Prism.languages.pbrule={string:Prism.languages.js.string,number:Prism.languages.js.number,function:Prism.languages.js.function,boolean:/\b(?:true|false)\b/i,constant:/\b(?:null)\b/i,comment:{pattern:/\/\/.*/,greedy:!0},italic:/_via_|\:\w+/,keyword:/&&|\|\||\??(?:!~|!=|>=|<=|=|~|>|>|<)(?=[@\w\s]|$)/};var f={isObject(e){return typeof e==`object`&&!!e&&e.constructor===Object},isEmpty(e){return e==null||e===``||Array.isArray(e)&&e.length===0||typeof e==`object`&&app.utils.isEmptyObject(e)},isEmptyObject(e){for(let n in e)return!1;return!0},toArray(e,n=!1){return Array.isArray(e)?e.slice():(n||!f.isEmpty(e))&&e!==void 0?[e]:[]},removeByValue(e,n){if(!Array.isArray(e)){console.warn(`[removeByValue] not an array:`,e);return}for(let r=e.length-1;r>=0;r--)if(e[r]==n){e.splice(r,1);break}},removeByKey(e,n,r){if(!Array.isArray(e)){console.warn(`[removeByKey] not an array:`,e);return}for(let i in e)if(e[i][n]==r){e.splice(i,1);break}},pushUnique(e,n){if(!Array.isArray(e)){console.warn(`[pushUnique] not an array:`,e);return}e.includes(n)||e.push(n)},mergeUnique(e,n){for(let r of n)app.utils.pushUnique(e,r);return e},pushOrReplaceObject(e,n,r=`id`){for(let i=e.length-1;i>=0;i--)if(e[i][r]==n[r]){e[i]=n;return}e.push(n)},filterDuplicatesByKey(e,n=`id`){e=Array.isArray(e)?e:[];let r={};for(let i of e)r[i[n]]=i;return Object.values(r)},filterRedactedProps(e,n=`******`){let r=JSON.parse(JSON.stringify(e||{}));for(let e in r)typeof r[e]==`object`&&r[e]!==null?r[e]=f.filterRedactedProps(r[e],n):r[e]===n&&delete r[e];return r},getByPath(e,n,r=null,i=`.`){let a=e||{},o=(n||``).split(i);for(let e of o){if(!f.isObject(a)&&!Array.isArray(a)||a[e]===void 0)return r;a=a[e]}return a},setByPath(e,n,r,i=`.`){if(typeof e!=`object`||!e){console.warn(`setByPath: data not an object or array.`);return}let a=e,o=n.split(i),s=o.pop();for(let e of o)(!f.isObject(a)&&!Array.isArray(a)||!f.isObject(a[e])&&!Array.isArray(a[e]))&&(a[e]={}),a=a[e];a[s]=r},deleteByPath(e,n,r=`.`){let i=e||{},a=(n||``).split(r),o=a.pop();for(let e of a)(!f.isObject(i)&&!Array.isArray(i)||!f.isObject(i[e])&&!Array.isArray(i[e]))&&(i[e]={}),i=i[e];Array.isArray(i)?i.splice(o,1):f.isObject(i)&&delete i[o],a.length>0&&(Array.isArray(i)&&!i.length||f.isObject(i)&&!Object.keys(i).length)&&(Array.isArray(e)&&e.length>0||f.isObject(e)&&Object.keys(e).length>0)&&f.deleteByPath(e,a.join(r),r)},emptyClone(e,n=[]){let r=JSON.parse(JSON.stringify(e));for(let e in r)n.includes(e)||(typeof r[e]==`string`?r[e]=``:typeof r[e]==`number`?r[e]=0:typeof r[e]==`boolean`?r[e]=!1:Array.isArray(r[e])?r[e]=[]:app.utils.isObject(r[e])&&(r[e]={}));return r},randomString(e=8,n=u){let r=``;for(let i=0;i<e;i++)r+=n.charAt(Math.floor(Math.random()*n.length));return r},randomSecret(e=30){if(typeof crypto>`u`)return app.utils.randomString(e);let n=new Uint8Array(e);crypto.getRandomValues(n);let r=``;for(let i=0;i<e;i++)r+=`-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.charAt(n[i]%64);return r},sentenize(e,n=!0){if(typeof e!=`string`)return``;if(e=e.trim().split(`_`).join(` `),e===``)return e;if(e=e[0].toUpperCase()+e.substring(1),n){let n=e[e.length-1];n!==`.`&&n!==`?`&&n!==`!`&&(e+=`.`)}return e},trimQuotedValue(e){return typeof e==`string`&&(e[0]==`"`||e[0]==`'`||e[0]=="`")&&e[0]==e[e.length-1]?e.slice(1,-1):e},slugify(e,n=`_`,r=[]){if(e===``)return``;let i={a:/а|à|á|å|â/gi,b:/б/gi,c:/ц|ç/gi,d:/д/gi,e:/е|è|é|ê|ẽ|ë/gi,f:/ф/gi,g:/г/gi,h:/х/gi,i:/й|и|ì|í|î/gi,j:/ж/gi,k:/к/gi,l:/л/gi,m:/м/gi,n:/н|ñ/gi,o:/о|ò|ó|ô|ø/gi,p:/п/gi,q:/я/gi,r:/р/gi,s:/с/gi,t:/т/gi,u:/ю|ù|ú|ů|ûLine truncated
|
||||
`),i=new Blob([r],{type:`text/csv;charset=utf-8;`}),a=window.URL.createObjectURL(i);f.download(a,n)},getApiExampleURL(){let e;if(app.pb.baseURL.startsWith(`http://`)||app.pb.baseURL.startsWith(`https://`))e=app.pb.baseURL;else{e=window.location.href;let n=e.indexOf(`/_/`);e=n>=0?e.substring(0,n):window.location.origin}return e.replace(`//localhost`,`//127.0.0.1`)},isActivePath(e,n=!0,r=``){r||=d.hash;let i;return i=RegExp(n?`^`+RegExp.escape(e)+`\\/?.*$`:`^`+RegExp.escape(e)+`\\/?(?:\\?.+)?$`),i.test(r)},getHashQueryParams(e=``){e||=d.hash;let n=``,r=e.indexOf(`?`);return r>-1&&(n=window.location.hash.substring(r+1)),Object.fromEntries(new URLSearchParams(n))},replaceHashQueryParams(e,n=null){e||={};let r=``,i=window.location.hash,a=i.indexOf(`?`);a>-1&&(r=i.substring(a+1),i=i.substring(0,a));let o=new URLSearchParams(r);for(let n in e){let r=e[n];f.isEmpty(r)?o.delete(n):o.set(n,r)}r=o.toString(),r!=``&&(i+=`?`+r);let s=window.location.href,c=s.indexOf(`#`);c>-1&&(s=s.substring(0,c));let l=s+i;return n===!1||(n===!0?window.history.pushState(null,``,l):window.history.replaceState(null,``,l)),l},getLocalHistory(e,n=null){try{let r=window.localStorage.getItem(e);if(r)return JSON.parse(r)||n}catch(n){console.log(`failed to load local history:`,e,n)}return n},saveLocalHistory(e,n){try{app.utils.isEmpty(n)?window.localStorage.removeItem(e):typeof n==`string`?window.localStorage.setItem(e,n):window.localStorage.setItem(e,JSON.stringify(n))}catch(n){console.log(`failed to save local history:`,e,n)}},generateThumb(e,n=100,r=100){return new Promise(i=>{let a=new FileReader;a.onload=function(a){let o=new Image;o.onload=function(){let a=document.createElement(`canvas`),s=a.getContext(`2d`),c=o.width,l=o.height;return a.width=n,a.height=r,s.drawImage(o,c>l?(c-l)/2:0,0,c>l?l:c,c>l?l:c,0,0,n,r),i(a.toDataURL(e.type))},o.src=a.target.result},a.readAsDataURL(e)})},normalizeSearchFilter(e,n=[]){if(e=(e||``).trim(),!e||!n.length)return e;for(let n of[`=`,`!=`,`~`,`!~`,`>`,`>=`,`<`,`<=`])if(e.includes(n))return e;return e=isNaN(e)&&e!=`true`&&e!=`false`?`"${e.replace(/^[\"\'\`]|[\"\'\`]$/gm,``)}"`:e,n.map(n=>`${n}~${e}`).join(`||`)},logLevels:{[-4]:{label:`DEBUG`,class:``},0:{label:`INFO`,class:`success`},4:{label:`WARN`,class:`warning`},8:{label:`ERROR`,class:`danger`}},logDataFormatters:{execTime:function(e){return e?.data?.execTime===void 0?`N/A`:e.data.execTime+`ms`}},extendStore(e,n={},...r){let i=[];for(let a in n){let o=n[a];typeof e.__raw?.[a]==`function`||typeof o!=`function`||a.length>2&&a.startsWith(`on`)||r.includes(a)?e[a]=o:i.push(watch(o,n=>{e[a]=n}))}return i},cssTimeToMs(e){return e?(e=e.toLowerCase(),e.endsWith(`ms`)?Number(e.substring(0,e.length-2)):e.endsWith(`s`)?Number(e.substring(0,e.length-1)):Number(e)||0):0},isDarkEnoughForWhiteText(e){if(e=e?.startsWith(`#`)?e.substring(1):e,e?.length!=6)return!1;let n=parseInt(e.substring(0,2),16),r=parseInt(e.substring(2,4),16),i=parseInt(e.substring(4,6),16);return(n*299+r*587+i*114)/1e3<128},imageExtensions:[`.jpg`,`.jpeg`,`.png`,`.svg`,`.gif`,`.jfif`,`.webp`,`.avif`],videoExtensions:[`.mp4`,`.avi`,`.mov`,`.3gp`,`.wmv`],audioExtensions:[`.aa`,`.aac`,`.m4v`,`.mp3`,`.ogg`,`.oga`,`.mogg`,`.amr`],documentExtensions:[`.pdf`,`.doc`,`.docx`,`.xls`,`.xlsx`,`.ppt`,`.pptx`,`.odp`,`.odt`,`.ods`,`.txt`],hasImageExtension(e){return e=(e||``).toLowerCase(),!!app.utils.imageExtensions.find(n=>e.endsWith(n))},hasVideoExtension(e){return e=(e||``).toLowerCase(),!!app.utils.videoExtensions.find(n=>e.endsWith(n))},hasAudioExtension(e){return e=(e||``).toLowerCase(),!!app.utils.audioExtensions.find(n=>e.endsWith(n))},hasDocumentExtension(e){return e=(e||``).toLowerCase(),!!app.utils.documentExtensions.find(n=>e.endsWith(n))},getFileType(e){return app.utils.hasImageExtension(e)?`image`:app.utils.hasVideoExtension(e)?`video`:app.utils.hasAudioExtension(e)?`audio`:app.utils.hasDocumentExtension(e)?`document`:`file`},fileTypeIcons:{image:`ri-image-line`,video:`ri-movie-line`,audio:`ri-music-2-line`,document:`ri-file-line`,file:`ri-file-line`},fallbackFieldIcon:`ri-puzzle-line`,fallbackCollectionIcon:`ri-puzzle-line`,fallbackProviderIcon:`ri-puzzle-line`,fallbackPresentableProps:[`title`,`name`,`slug`,`email`,`username`,`nickname`,`displayName`,`label`,`subject`,`topic`,`message`,`heading`,`headline`,`header`,`caption`,`key`,`identifier`,`id`],sortedCollections(e=[]){let n,r;function i(e,i){return n=e.name.startsWith(`_`),r=i.name.startsWith(`_`),n&&!r?1:!n&&r?-1:e.name>i.name?1:e.name<i.name?-1:0}return e.slice().sort(i)},sortedCollectionsByType(e=[]){let n=[],r=[],i=[];for(let a of e)a.type===`auth`?n.push(a):a.type===`base`?r.push(a):i.push(a);return[].concat(app.utils.sortedCollections(n),app.utils.sortedCollections(r),app.utils.sortedCollections(i))},hasCollectionChanges(e,n,r=!1){if(e||={},n||={},e.id!=n.id)return!0;for(let r in e)if(r!==`fields`&&JSON.stringify(e[r])!==JSON.stringify(n[r]))return!0;let i=Array.isArray(e.fields)?e.fields:[],a=Array.isArray(n.fields)?n.fields:[],o
Line truncated
|
||||
`),n+=r.map(e=>{let n=``;return e.name.includes(`(`)||e.name.includes(` `)?n+=e.name:n+="`"+e.name+"`",e.collate&&(n+=` COLLATE `+e.collate),e.sort&&(n+=` `+e.sort.toUpperCase()),n}).join(`,
|
||||
`),r.length>1&&(n+=`
|
||||
`),n+=`)`,e.where&&(n+=` WHERE ${e.where}`),n},replaceIndexFields(e,n){let r=app.utils.parseIndex(e);return typeof n==`function`?Object.assign(r,n(r)||{}):Object.assign(r,n||{}),app.utils.buildIndex(r)},replaceIndexColumn(e,n,r){if(n===r)return e;let i=app.utils.parseIndex(e),a=!1;for(let e of i.columns)e.name===n&&(e.name=r,a=!0);return a?app.utils.buildIndex(i):e}};window.app=window.app||{},window.app.utils=f,window.app=window.app||{},window.app.utils=window.app.utils||{},window.app.utils.mimeTypes=[{ext:``,mimeType:`application/octet-stream`},{ext:`.xpm`,mimeType:`image/x-xpixmap`},{ext:`.7z`,mimeType:`application/x-7z-compressed`},{ext:`.zip`,mimeType:`application/zip`},{ext:`.xlsx`,mimeType:`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`},{ext:`.docx`,mimeType:`application/vnd.openxmlformats-officedocument.wordprocessingml.document`},{ext:`.pptx`,mimeType:`application/vnd.openxmlformats-officedocument.presentationml.presentation`},{ext:`.epub`,mimeType:`application/epub+zip`},{ext:`.apk`,mimeType:`application/vnd.android.package-archive`},{ext:`.jar`,mimeType:`application/jar`},{ext:`.odt`,mimeType:`application/vnd.oasis.opendocument.text`},{ext:`.ott`,mimeType:`application/vnd.oasis.opendocument.text-template`},{ext:`.ods`,mimeType:`application/vnd.oasis.opendocument.spreadsheet`},{ext:`.ots`,mimeType:`application/vnd.oasis.opendocument.spreadsheet-template`},{ext:`.odp`,mimeType:`application/vnd.oasis.opendocument.presentation`},{ext:`.otp`,mimeType:`application/vnd.oasis.opendocument.presentation-template`},{ext:`.odg`,mimeType:`application/vnd.oasis.opendocument.graphics`},{ext:`.otg`,mimeType:`application/vnd.oasis.opendocument.graphics-template`},{ext:`.odf`,mimeType:`application/vnd.oasis.opendocument.formula`},{ext:`.odc`,mimeType:`application/vnd.oasis.opendocument.chart`},{ext:`.sxc`,mimeType:`application/vnd.sun.xml.calc`},{ext:`.pdf`,mimeType:`application/pdf`},{ext:`.fdf`,mimeType:`application/vnd.fdf`},{ext:``,mimeType:`application/x-ole-storage`},{ext:`.msi`,mimeType:`application/x-ms-installer`},{ext:`.aaf`,mimeType:`application/octet-stream`},{ext:`.msg`,mimeType:`application/vnd.ms-outlook`},{ext:`.xls`,mimeType:`application/vnd.ms-excel`},{ext:`.pub`,mimeType:`application/vnd.ms-publisher`},{ext:`.ppt`,mimeType:`application/vnd.ms-powerpoint`},{ext:`.doc`,mimeType:`application/msword`},{ext:`.ps`,mimeType:`application/postscript`},{ext:`.psd`,mimeType:`image/vnd.adobe.photoshop`},{ext:`.p7s`,mimeType:`application/pkcs7-signature`},{ext:`.ogg`,mimeType:`application/ogg`},{ext:`.oga`,mimeType:`audio/ogg`},{ext:`.ogv`,mimeType:`video/ogg`},{ext:`.png`,mimeType:`image/png`},{ext:`.png`,mimeType:`image/vnd.mozilla.apng`},{ext:`.jpg`,mimeType:`image/jpeg`},{ext:`.jxl`,mimeType:`image/jxl`},{ext:`.jp2`,mimeType:`image/jp2`},{ext:`.jpf`,mimeType:`image/jpx`},{ext:`.jpm`,mimeType:`image/jpm`},{ext:`.jxs`,mimeType:`image/jxs`},{ext:`.gif`,mimeType:`image/gif`},{ext:`.webp`,mimeType:`image/webp`},{ext:`.exe`,mimeType:`application/vnd.microsoft.portable-executable`},{ext:``,mimeType:`application/x-elf`},{ext:``,mimeType:`application/x-object`},{ext:``,mimeType:`application/x-executable`},{ext:`.so`,mimeType:`application/x-sharedlib`},{ext:``,mimeType:`application/x-coredump`},{ext:`.a`,mimeType:`application/x-archive`},{ext:`.deb`,mimeType:`application/vnd.debian.binary-package`},{ext:`.tar`,mimeType:`application/x-tar`},{ext:`.xar`,mimeType:`application/x-xar`},{ext:`.bz2`,mimeType:`application/x-bzip2`},{ext:`.fits`,mimeType:`application/fits`},{ext:`.tiff`,mimeType:`image/tiff`},{ext:`.bmp`,mimeType:`image/bmp`},{ext:`.ico`,mimeType:`image/x-icon`},{ext:`.mp3`,mimeType:`audio/mpeg`},{ext:`.flac`,mimeType:`audio/flac`},{ext:`.midi`,mimeType:`audio/midi`},{ext:`.ape`,mimeType:`audio/ape`},{ext:`.mpc`,mimeType:`audio/musepack`},{ext:`.amr`,mimeType:`audio/amr`},{ext:`.wav`,mimeType:`audio/wav`},{ext:`.aiff`,mimeType:`audio/aiff`},{ext:`.au`,mimeType:`audio/basic`},{ext:`.mpeg`,mimeType:`video/mpeg`},{ext:`.mov`,mimeType:`video/quicktime`},{ext:`.mp4`,mimeType:`video/mp4`},{ext:`.avif`,mimeType:`image/avif`},{ext:`.3gp`,mimeType:`video/3gpp`},{ext:`.3g2`,mimeType:`video/3gpp2`},{ext:`.mp4`,mimeType:`audio/mp4`},{ext:`.mqv`,mimeType:`video/quicktime`},{ext:`.m4a`,mimeType:`audio/x-m4a`},{ext:`.m4v`,mimeType:`video/x-m4v`},{ext:`.heic`,mimeType:`image/heic`},{ext:`.heic`,mimeType:`image/heic-sequence`},{ext:`.heif`,mimeType:`image/heif`},{ext:`.heif`,mimeType:`image/heif-sequence`},{ext:`.mj2`,mimeType:`video/mj2`},{ext:`.dvb`,mimeType:`video/vnd.dvb.file`},{ext:`.webm`,mimeType:`video/webm`},{ext:`.avi`,mimeType:`video/x-msvideo`},{ext:`.flv`,mimeType:`video/x-flv`},{ext:`.mkv`,mimeType:`video/x-matroska`},{ext:`.asf`,mimeType:`video/x-ms-asf`},{ext:`.asf`,mimeType:`video/x-ms-wmv`},{ext:`.asf`,mimeType:`video/asf`},{ext:`.aac`,mimeType:`audio/aac`},{ext:`.voc`,mimeType:`audio/x-unknown`},{ext:`.m3u`,mimeType:`application/vnd.apple.mpegurl`},{ext:`.rmvb`,mimeType:`application/Line truncated
|
||||
`)}o&&(n.classList.add(`error`),r.__errListener=function(){te(r,n),app.utils.deleteByPath(app.store.errors,i)},r.addEventListener(`input`,r.__errListener),r.addEventListener(`change`,r.__errListener),r.setAttribute(`data-error`,!0),r.setCustomValidity&&r.reportValidity&&r.classList.contains(`inline-error`)?(r.setCustomValidity(o),r.reportValidity(),r._oldTitle=r.title,r.title=o):n.after(t.div({"html-data-input-name":i,className:`field-help error generated-error`,textContent:o})))}}),p.onmessage=e=>{e.data?.collections&&JSON.stringify(app.store.collections)!=JSON.stringify(e.data.collections)&&(app.store.collections=e.data.collections),e.data?.settings&&JSON.stringify(app.store.settings)!=JSON.stringify(e.data.settings)&&(app.store.settings=e.data.settings),e.data?.colorScheme&&(app.store.userColorScheme=e.data.colorScheme)},watch(()=>JSON.stringify(app.store.collections),(e,n)=>{e&&e!=`[]`&&n&&n!=`[]`&&e!=n&&p?.postMessage({collections:JSON.parse(e)})}),watch(()=>JSON.stringify(app.store.settings),(e,n)=>{e&&e!=`{}`&&n&&n!=`{}`&&e!=n&&p?.postMessage({settings:JSON.parse(e)}),window.localStorage.setItem(m,e)});var y=class e extends Error{constructor(n){super(`ClientResponseError`),this.url=``,this.status=0,this.response={},this.isAbort=!1,this.originalError=null,Object.setPrototypeOf(this,e.prototype),typeof n==`object`&&n&&(this.originalError=n.originalError,this.url=typeof n.url==`string`?n.url:``,this.status=typeof n.status==`number`?n.status:0,this.isAbort=!!n.isAbort||n.name===`AbortError`||n.message===`Aborted`,n.response!==null&&typeof n.response==`object`?this.response=n.response:n.data!==null&&typeof n.data==`object`?this.response=n.data:this.response={}),this.originalError||n instanceof e||(this.originalError=n),this.name=`ClientResponseError `+this.status,this.message=this.response?.message,this.message||(this.isAbort?this.message=`The request was aborted (most likely autocancelled; you can find more info in https://github.com/pocketbase/js-sdk#auto-cancellation).`:this.originalError?.cause?.message?.includes(`ECONNREFUSED ::1`)?this.message=`Failed to connect to the PocketBase server. Try changing the SDK URL from localhost to 127.0.0.1 (https://github.com/pocketbase/js-sdk/issues/21).`:this.message=`Something went wrong.`),this.cause=this.originalError}get data(){return this.response}toJSON(){return{...this}}},ne=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;function re(e,n){let r={};if(typeof e!=`string`)return r;let i=Object.assign({},n||{}).decode||ae,a=0;for(;a<e.length;){let n=e.indexOf(`=`,a);if(n===-1)break;let o=e.indexOf(`;`,a);if(o===-1)o=e.length;else if(o<n){a=e.lastIndexOf(`;`,n-1)+1;continue}let s=e.slice(a,n).trim();if(r[s]===void 0){let a=e.slice(n+1,o).trim();a.charCodeAt(0)===34&&(a=a.slice(1,-1));try{r[s]=i(a)}catch{r[s]=a}}a=o+1}return r}function ie(e,n,r){let i=Object.assign({},r||{}),a=i.encode||b;if(!ne.test(e))throw TypeError(`argument name is invalid`);let o=a(n);if(o&&!ne.test(o))throw TypeError(`argument val is invalid`);let s=e+`=`+o;if(i.maxAge!=null){let e=i.maxAge-0;if(isNaN(e)||!isFinite(e))throw TypeError(`option maxAge is invalid`);s+=`; Max-Age=`+Math.floor(e)}if(i.domain){if(!ne.test(i.domain))throw TypeError(`option domain is invalid`);s+=`; Domain=`+i.domain}if(i.path){if(!ne.test(i.path))throw TypeError(`option path is invalid`);s+=`; Path=`+i.path}if(i.expires){if(!function(e){return Object.prototype.toString.call(e)===`[object Date]`||e instanceof Date}(i.expires)||isNaN(i.expires.valueOf()))throw TypeError(`option expires is invalid`);s+=`; Expires=`+i.expires.toUTCString()}if(i.httpOnly&&(s+=`; HttpOnly`),i.secure&&(s+=`; Secure`),i.priority)switch(typeof i.priority==`string`?i.priority.toLowerCase():i.priority){case`low`:s+=`; Priority=Low`;break;case`medium`:s+=`; Priority=Medium`;break;case`high`:s+=`; Priority=High`;break;default:throw TypeError(`option priority is invalid`)}if(i.sameSite)switch(typeof i.sameSite==`string`?i.sameSite.toLowerCase():i.sameSite){case!0:s+=`; SameSite=Strict`;break;case`lax`:s+=`; SameSite=Lax`;break;case`strict`:s+=`; SameSite=Strict`;break;case`none`:s+=`; SameSite=None`;break;default:throw TypeError(`option sameSite is invalid`)}return s}function ae(e){return e.indexOf(`%`)===-1?e:decodeURIComponent(e)}function b(e){return encodeURIComponent(e)}var x=typeof navigator<`u`&&navigator.product===`ReactNative`||typeof global<`u`&&global.HermesInternal,oe;function S(e){if(e)try{let n=decodeURIComponent(oe(e.split(`.`)[1]).split(``).map((function(e){return`%`+(`00`+e.charCodeAt(0).toString(16)).slice(-2)})).join(``));return JSON.parse(n)||{}}catch{}return{}}function se(e,n=0){let r=S(e);return!(Object.keys(r).length>0&&(!r.exp||r.exp-n>Date.now()/1e3))}oe=typeof atob!=`function`||x?e=>{let n=String(e).replace(/=+$/,``);if(n.length%4==1)throw Error(`'atob' failed: The string to be decoded is not correctly encoded.`);for(var r,i,a=0,o=0,s=``;i=n.charAt(o++);~i&&(r=a%4?64*r+i:i,a++%4)&&(s+=String.fromCharCode(255&r>>(-2*a&6))))i=`A
Line truncated
|
||||
`+ft:null),"html-class":`formatted-date`,className:()=>`formatted-date ${n.short?`short`:`full`}`,onunmount:()=>{r.forEach(e=>e?.unwatch())}},()=>{if(!n.value)return t.span({className:`missing-value`});if(n.short){let e=n.value.split(` `);return[t.span({className:`primary-date`},e[0]),t.span({className:`secondary-date`},e[1])]}return[t.span({className:`primary-date`},app.utils.toLocalDatetime(n.value)),t.span({className:`secondary-date`},n.value)]})},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.refreshButton=function(e={}){let n=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,tooltip:`Refresh`,className:`btn transparent secondary circle rotate-btn`,disabled:!1,onclick:function(e){}}),r=app.utils.extendStore(n,e),i,a=t.button({rid:n.rid,id:()=>n.id,hidden:()=>n.hidden,inert:()=>n.inert,type:`button`,ariaLabel:app.attrs.tooltip(()=>n.tooltip),disabled:()=>n.disabled,className:()=>n.className,onunmount:()=>{clearTimeout(i),r.forEach(e=>e?.unwatch())},onclick:e=>{e.preventDefault(),n.onclick&&n.onclick(e),a.dataset.rotate=!0,a.addEventListener(`animationend`,()=>{a.dataset.rotate=!1}),clearTimeout(i),i=setTimeout(()=>{clearTimeout(i),a.dataset.rotate=!1},500)}},t.i({className:`ri-refresh-line`,ariaHidden:!0}));return a},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.searchHistoryButton=function(e={}){let n=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,value:void 0,historyKey:`default`,max:15,openInNewTabParam:`filter`,btnClassName:`btn sm pill secondary transparent p-r-5`,onselect:function(e){}}),r=app.utils.extendStore(n,e),i=store({items:app.utils.getLocalHistory(n.historyKey,[])});function a(e){o(e),i.items.unshift(e)}function o(e){app.utils.removeByValue(i.items,e)}let s=`history_dropdown_`+app.utils.randomString();r.push(watch(()=>n.value,e=>{e&&a(e)})),r.push(watch(()=>{i.items.length>n.max&&(i.items=i.items.slice(0,n.max)),app.utils.saveLocalHistory(n.historyKey,i.items)}));let c=t.div({id:s,className:`dropdown sm left nowrap history-searchbar-dropdown`,popover:`hint`,onclick:e=>(e.stopPropagation(),!1)},t.div({className:`block p-5`},t.small({className:`txt-hint`},`Search history`)),()=>i.items?.length?i.items.slice(0,n.max).map(e=>t.button({type:`button`,className:`dropdown-item txt-code`,onclick:()=>{c.hidePopover(),n.onselect?.(e),a(e)},onauxclick:()=>{if(n.openInNewTabParam){a(e),c.hidePopover();let r=app.utils.replaceHashQueryParams({[n.openInNewTabParam]:e},!1);window.open(r,`_blank`)}}},t.span({className:`txt-ellipsis`,title:e,textContent:e}),t.small({role:`button`,className:`remove-btn link-hint m-l-auto p-l-5 p-r-5`,title:`Clear`,onauxclick:e=>(e.stopPropagation(),!1),onclick:n=>(n.stopPropagation(),o(e),!1)},t.i({className:`ri-close-line`,ariaHidden:!0})))):t.div({rid:`no-history`,className:`block p-5`},t.span(null,`Your recent searches will show up here.`)));return t.button({rid:n.rid,id:()=>n.id,hidden:()=>n.hidden,inert:()=>n.inert,type:`button`,title:`Search history`,className:()=>n.btnClassName,"html-popovertarget":s,onunmount:()=>{r?.forEach(e=>e?.unwatch())}},t.i({className:`ri-search-line`,ariaHidden:!0}),t.i({className:`ri-arrow-drop-down-line`,ariaHidden:!0}),c)},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.s3Test=function(e={}){let n=`s3_test_request`,r=store({rid:void 0,config:null,label:`Use S3 storage`,testFilesystem:`storage`}),i=app.utils.extendStore(r,e),a=store({isTesting:!1,testError:null,get hasError(){return!app.utils.isEmpty(a.testError)}}),o,s;function c(e=150){if(!r.config.enabled){clearTimeout(o);return}a.isTesting=!0,clearTimeout(o),o=setTimeout(()=>{l()},e)}async function l(){if(a.isTesting=!0,!r.config.enabled||!r.testFilesystem){a.testError=null,a.isTesting=!1;return}app.pb.cancelRequest(n),clearTimeout(s),s=setTimeout(()=>{app.pb.cancelRequest(n),a.testError=Error(`S3 test connection timeout.`),a.isTesting=!1},3e4);try{await app.pb.props.testS3(r.testFilesystem,{requestKey:n}),a.testError=null,a.isTesting=!1}catch(e){e?.isAbort||(a.testError=e,a.isTesting=!1,clearTimeout(s))}}return i.push(watch(()=>r.testFilesystem&&r.config,()=>c())),t.div({pbEvent:`s3Test`,rid:r.rid,hidden:()=>!r.testFilesystem,className:()=>`label s3-test-label txt-nowrap ${a.hasError?`warning`:`success`}`,ariaDescription:app.attrs.tooltip(()=>a.testError?.data?.message),onunmount:()=>{clearTimeout(s),clearTimeout(o),i.forEach(e=>e?.unwatch())}},()=>a.isTesting?t.span({className:`loader sm`}):a.hasError?[t.i({className:`ri-error-warning-line txt-warning`,ariaHidden:!0}),t.span({className:`txt`},`Failed to establish S3 connection`)]:[t.i({className:`ri-checkbox-circle-line txt-success`,ariaHidden:!0}),t.span({className:`txt`},`S3 connected successfully`)])},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.s3ConfigFields=function(e={}){let n=store({rid:void 0,id:void 0,hidden:void 0,in
Line truncated
|
||||
<video controls width="300">
|
||||
<source src="${a}" />
|
||||
<p>Download: <a href="${a}" download="${i}">${i}</a>.</p>
|
||||
</video>
|
||||
`)}})}},{type:`menuitem`,text:`Manual embed`,onAction:()=>{tinymce.activeEditor.execCommand(`mceMedia`)}}])}})}var Ct=`DIV.P.A.EM.B.STRONG.H1.H2.H3.H4.H5.H6.TABLE.TR.TD.TH.TBODY.THEAD.TFOOT.BR.HR.Q.SUP.SUB.DEL.IMG.OL.UL.LI.CODE`.split(`.`);function z(e){if(e){for(let n of e.children)z(n);Ct.includes(e.tagName)?(e.removeAttribute(`style`),e.removeAttribute(`class`)):B(e)}}function B(e){let n=e.parentNode;for(;e.firstChild;)n.insertBefore(e.firstChild,e);n.removeChild(e)}async function wt(){if(window.tinymce!==void 0)return;let e=`lazy-tinymce-js`;return document.getElementById(e)?new Promise((e,n)=>{function r(){document.removeEventListener(`tinymceLoadSuccess`,i),document.removeEventListener(`tinymceLoadError`,a)}let i=function(){r(),e()},a=function(e){r(),n(e?.details)};document.addEventListener(`tinymceLoadSuccess`,i),document.addEventListener(`tinymceLoadError`,a)}):new Promise((n,r)=>{document.head.querySelector(`#shablon-script`).after(t.script({id:e,src:`./libs/tinymce/tinymce.min.js`,onload:()=>{n()},onerror:e=>{console.warn(`failed to load tinymce.min.js:`,e),r(e)}}))}).then(()=>{document.dispatchEvent(new CustomEvent(`tinymceLoadSuccess`))}).catch(e=>{document.dispatchEvent(new CustomEvent(`tinymceLoadError`,{detail:e}))})}window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.colorPicker=function(e={}){let n=`picker_`+app.utils.randomString(),r=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,className:``,name:``,required:!1,disabled:!1,value:``,predefinedColors:[],onchange:e=>{},onmount:e=>{},onunmount:e=>{}}),i=app.utils.extendStore(r,e),a=store({inputValue:`#ffffff`});function o(e){e=e?.toLowerCase()||``,r.onchange?.(e)}let s,c=t.input({type:`color`,className:`color-picker-input`,id:()=>r.id,name:()=>r.name,required:()=>r.required,disabled:()=>r.disabled,value:()=>(a.inputValue=r.value||`#ffffff`,r.value||void 0),oninput:e=>{a.inputValue=e.target.value,clearTimeout(s),s=setTimeout(()=>{o(e.target.value)},50)}});return t.div({rid:r.rid,hidden:()=>r.hidden,inert:()=>r.inert,className:()=>`color-picker ${r.className}`,onmount:e=>{r.onmount?.(e)},onunmount:e=>{clearTimeout(s),r.onunmount?.(e),i.forEach(e=>e?.unwatch())}},t.div({className:`color-picker-input-wrapper`},c,t.output({className:`result`,style:()=>`color: lch(from ${a.inputValue||`#ffffff`} calc((49 - l) * infinity) 0 0);`,textContent:()=>a.inputValue})),t.button({hidden:()=>!r.predefinedColors.length,type:`button`,title:`Predefined colors`,className:`link-hint predefined-colors-btn`,"html-popovertarget":n+`predefined-colors-dropdown`},t.i({className:`ri-arrow-down-s-line`,ariaHidden:!0})),t.div({pbEvent:`predefinedColorsDropdown`,id:n+`predefined-colors-dropdown`,className:`dropdown predefined-colors-dropdown`,popover:`auto`},t.div({className:`predefined-colors-list`},()=>r.predefinedColors?.map(e=>t.button({type:`button`,className:()=>`color ${r.value==e?`active`:``}`,style:`background:${e}`,onclick:n=>{c&&(n.target.closest(`.dropdown`)?.hidePopover(),c.value=e||void 0,c.dispatchEvent(new Event(`input`,{bubbles:!0})))}})))))},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.ruleField=function(e={}){let n=`rule_`+app.utils.randomString(),r=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,className:``,required:!1,disabled:!1,name:void 0,label:void 0,help:void 0,value:null,nullable:!0,placeholder:`Leave empty to grant everyone access...`,autocomplete:e=>[],oninput:e=>{},onmount:e=>{},onunmount:e=>{},get isLocked(){return r.value==null}}),i=app.utils.extendStore(r,e,`isLocked`),a,o=``;function s(e){r.value=e,r.oninput?.(e),a?.dispatchEvent(new CustomEvent(`change`,{detail:e}))}function c(){r.value!==null&&(o=r.value,s(null))}function l(){s(o??``),setTimeout(()=>{document.getElementById(n)?.focus()},0)}return a=t.div({rid:r.rid,id:()=>r.id,hidden:()=>r.hidden,inert:()=>r.inert,"html-name":()=>r.name,className:()=>[`field`,`rule-field`,r.required?`required`:null,r.value===null?`locked`:null,r.disabled?`disabled`:null].filter(Boolean).join(` `),onmount:e=>{r.onmount?.(e)},onunmount:e=>{r.onunmount?.(e),i.forEach(e=>e?.unwatch())}},t.label({htmlFor:n},e=>r.label?typeof r.label==`function`?r.label(e):typeof r.label==`string`?t.span({className:`txt`},r.label):r.label:t.span({className:`txt`},`Rule`),t.span({hidden:()=>!r.isLocked,className:`txt superusers-label`},`(Superusers only)`)),e=>r.isLocked?t.button({type:`button`,className:`unlock-overlay`,disabled:()=>r.disabled,onclick:l},t.span({className:`txt`},`Unlock and set custom rule`),t.i({className:`ri-lock-unlock-line`,ariaHidden:!0})):[app.components.codeEditor({id:n,language:`pbrule`,required:()=>r.required,disabled:()=>r.disabled,value:()=>r.value,oninput:s,placeholder:()=>r.placeholder,autocomplete:r.autocomplete,autocompleteContainer:e}),t.button({hidden:()=>!r.nullable,type:`button`,className:`superuser-toggle`,disabled:()=>r.disabled,onclick:c},t.i({c
Line truncated
|
||||
<defs>
|
||||
<marker id="${s}_arrow1" class="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="context-stroke">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
</marker>
|
||||
</defs>
|
||||
<g class="paths-group" marker-end="url(#${s}_arrow1)"></g>
|
||||
`;let h=m.querySelector(`.paths-group`),g=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);g.classList.add(`erd-paths`,`front`),g.innerHTML=`
|
||||
<defs>
|
||||
<marker id="${s}_arrow2" class="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="context-stroke">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
</marker>
|
||||
</defs>
|
||||
<g class="paths-group" marker-end="url(#${s}_arrow2)"></g>
|
||||
`;let _=g.querySelector(`.paths-group`);return a=t.div({tabIndex:-1,rid:n.rid,id:()=>n.id,hidden:()=>n.hidden,inert:()=>n.inert,className:()=>`erd ${n.className} ${i.isUpdating?`updating`:``} ${i.isPanning?`panning`:``} ${i.activeCollection?`active`:``}`,onkeydown:e=>{(e.ctrlKey||e.metaKey)&&e.key==`0`&&f()},onmount:async e=>{e.addEventListener(`wheel`,r=>{r.preventDefault();let a=e.getBoundingClientRect(),o=r.clientX-a.left,s=r.clientY-a.top,c=(o-i.viewX)/n.scale,l=(s-i.viewY)/n.scale,u=Math.min(Math.max(-r.deltaY*.001+n.scale,Dt),Et);i.viewX=o-c*u,i.viewY=s-l*u,n.scale=u}),e.addEventListener(`pointerdown`,e=>{e.buttons==1&&(i.isPanning=!0,i.panStartX=e.clientX-i.viewX,i.panStartY=e.clientY-i.viewY)}),e._ondragging=function(e){i.isPanning&&(i.viewX=e.clientX-i.panStartX,i.viewY=e.clientY-i.panStartY)},e._ondragstop=function(){i.isPanning=!1},window.addEventListener(`pointermove`,e._ondragging),window.addEventListener(`pointerup`,e._ondragstop),c()},onunmount:e=>{window.removeEventListener(`pointermove`,e._ondragging),window.removeEventListener(`pointerup`,e._ondragstop),r.forEach(e=>e?.unwatch())}},t.div({className:`erd-area`,style:()=>`transform: translate(${i.viewX}px, ${i.viewY}px) scale(${n.scale});`},m,t.div({className:`erd-tables`},()=>n.collections.map(e=>t.div({style:()=>`left:${i.positions[e.id]?.x||0}px;top:${i.positions[e.id]?.y||0}px`,className:()=>`erd-table type-${e.type} ${p(e)?`active`:``} ${e.system?`system`:``}`,"html-data-collection-id":()=>e.id,"html-data-collection-name":()=>e.name,onmouseenter:()=>{h.querySelectorAll(`[data-to="${e.id}"]`)?.forEach(e=>{e.classList.add(`active-to`),_.append(e)}),h.querySelectorAll(`[data-from="${e.id}"]`)?.forEach(e=>{e.classList.add(`active-from`),_.append(e)}),i.activeCollection=e},onmouseleave:()=>{for(let e of _.children)e.classList.remove(`active-from`,`active-to`);h.append(..._.children),i.activeCollection=null}},t.div({className:`erd-table-row header`},()=>e.name),()=>e.fields?.map(e=>t.div({className:`erd-table-row type-${e.type} ${e.primaryKey?`primary-key`:``}`,"html-data-field-id":()=>e.id,"html-data-field-name":()=>e.name},t.i({ariaHidden:!0,title:()=>e.type,className:()=>`field-icon ${app.fieldTypes[e.type].icon||app.utils.fallbackFieldIcon}`}),t.span({className:`field-name`},()=>e.name),()=>{if(e.hidden)return t.span({className:`label danger field-hidden-label`},`Hidden`)},()=>{if(e.maxSelect!==void 0)return t.span({className:`meta`},e.maxSelect>1?`multiple`:`single`)}))))),g),t.nav({className:`erd-nav`,onmousedown:e=>{e.stopImmediatePropagation()}},t.button({type:`button`,className:`btn sm circle secondary`,title:`Zoom in`,onclick:()=>{n.scale+=.05}},t.i({className:`ri-add-line`,ariaHidden:!0})),t.button({type:`button`,className:`btn sm circle secondary`,title:`Zoom out`,onclick:()=>{n.scale-=.05}},t.i({className:`ri-subtract-line`,ariaHidden:!0})))),a};function H(e,n,r=1,i=``,a=``,o=2){if(!e||!n)return;let s=e.closest(`.erd-area`).getBoundingClientRect(),c=e.getBoundingClientRect(),l=n.getBoundingClientRect();o*=r;let u=c.top-s.top+c.height/2,d=l.top-s.top+l.height/2,f,p;c.left<l.left?(f=c.left-s.left+c.width+o,p=l.left-s.left-o):c.left>l.left?(f=c.left-s.left-o,p=l.left-s.left+l.width+o):(f=c.left-s.left-o,p=l.left-s.left-o),f/=r,p/=r,u/=r,d/=r;let m=f+(p-f)/2;f==p&&(m-=20);let h=`M ${f} ${u}
|
||||
L ${m} ${u}
|
||||
L ${m} ${d}
|
||||
L ${p} ${d}`,g=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return g.setAttribute(`class`,`relation-path`),g.setAttribute(`data-from`,i||``),g.setAttribute(`data-to`,a||``),g.setAttribute(`d`,h),g}window.app=window.app||{},window.app.components=window.app.components||{};var Ot=1e3;window.app.components.pageSidebar=function(e={},...n){let r,i=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,className:``,widthHistoryKey:`pbPageSidebarWidth`,onmount:void 0,onunmount:void 0}),a=app.utils.extendStore(i,e),o=store({responsiveShow:!1}),s;function c(){if(r){if(window.innerWidth>Ot){o.responsiveShow=!1,r.dataset.responsive=!1,s?.remove(),s=null;return}r.dataset.responsive=!0,s||(s=t.button({type:`button`,className:`btn transparent secondary responsive-sidebar-btn`,title:`Toggle sidebar`,onclick:e=>{e.stopPropagation(),o.responsiveShow=!o.responsiveShow}},t.i({className:`ri-menu-2-line`,ariaHidden:!0})),document.body.querySelector(`.page-header .breadcrumbs`).before(s))}}function l(e){if(e.target.closest(`.responsive-close`)){o.responsiveShow=!1;return}if(!(e.target.closest(`.page-sidebar`)||e.target.closest(`.app-header`)||e.target.closest(`.modal`)))return e.preventDefault(),e.stopImmediatePropagation(),o.responsiveShow=!1,!1}return a.push(watch(()=>o.responsiveShow,e=>{e?window.addEventListener(`click`,l,!0):window.removeEventListener(`click`,l,!0)})),r=t.aside({pbEvent:`pageSidebar`,rid:i.rid,id:()=>i.id,hidden:()=>i.hidden,inert:()=>i.inert,className:()=>`page-sidebar ${i.className} ${o.responsiveShow?`active`:``}`,onmount:e=>{c(e),window.addEventListener(`resize`,c),i.onmount?.(e)},onunmount:e=>{i.onunmount?.(e),window.removeEventListener(`click`,l,!0),window.removeEventListener(`resize`,c),s?.remove(),a.forEach(e=>e?.unwatch())}},e=>{let n;return i.widthHistoryKey&&(n=localStorage.getItem(i.widthHistoryKey),n&&(e.style.width=n)),app.components.dragline({ondragstart:n=>{e._startWidth=e.offsetWidth},ondragging:(r,a,o)=>{n=e._startWidth+a+`px`,e.style.width=n,i.widthHistoryKey&&localStorage.setItem(i.widthHistoryKey,n)}})},...n),r};var kt=`modulepreload`,U=function(e,n){return new URL(e,n).href},At={},W=function(e,n,r){let i=Promise.resolve();if(n&&n.length>0){let e=document.getElementsByTagName(`link`),a=document.querySelector(`meta[property=csp-nonce]`),o=a?.nonce||a?.getAttribute(`nonce`);function s(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}i=s(n.map(n=>{if(n=U(n,r),n in At)return;At[n]=!0;let i=n.endsWith(`.css`),a=i?`[rel="stylesheet"]`:``;if(r)for(let r=e.length-1;r>=0;r--){let a=e[r];if(a.href===n&&(!i||a.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${n}"]${a}`))return;let s=document.createElement(`link`);if(s.rel=i?`stylesheet`:kt,i||(s.as=`script`),s.crossOrigin=``,s.href=n,o&&s.setAttribute(`nonce`,o),document.head.appendChild(s),i)return new Promise((e,r)=>{s.addEventListener(`load`,e),s.addEventListener(`error`,()=>r(Error(`Unable to preload CSS for ${n}`)))})}))}function a(e){let n=new Event(`vite:preloadError`,{cancelable:!0});if(n.payload=e,window.dispatchEvent(n),!n.defaultPrevented)throw e}return i.then(n=>{for(let e of n||[])e.status===`rejected`&&a(e.reason);return e().catch(a)})};window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openApiPreview=function(e,n={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null}){let r=jt(e,n);r&&(document.body.appendChild(r),app.modals.open(r))};function jt(e,n){if(!e){console.warn(`[apiPreviewModal] missing required collection`);return}let r,i=store({activeTab:`List/Search`,tabEl:null,isLoading:!1}),a={"List/Search":async e=>{let{docsList:n}=await W(async()=>{let{docsList:e}=await import(`./docsList-u2AryqHB.js`);return{docsList:e}},__vite__mapDeps([0,1,2,3]),import.meta.url);return i.tabEl=n(e)},View:async e=>{let{docsView:n}=await W(async()=>{let{docsView:e}=await import(`./docsView-oyuvqZP8.js`);return{docsView:e}},__vite__mapDeps([4,1,2]),import.meta.url);return i.tabEl=n(e)}};e.type!=`view`&&(a.Create=async e=>{let{docsCreate:n}=await W(async()=>{let{docsCreate:e}=await import(`./docsCreate-DDIyFVY7.js`);return{docsCreate:e}},__vite__mapDeps([5,1,2]),import.meta.url);return i.tabEl=n(e)},a.Update=async e=>{let{docsUpdate:n}=await W(async()=>{let{docsUpdate:e}=await import(`./docsUpdate-BzDOztgb.js`);return{docsUpdate:e}},__vite__mapDeps([6,5,1,2]),import.meta.url);return i.tabEl=n(e)},a.Delete=async e=>{let{docsDelete:n}=await W(async()=>{let{docsDelete:e}=await import(`./docsDelete-CeK6pY9P.js`);return{docsDelete:e}},[],import.meta.url);return i.tabEl=n(e)},a.Realtime=async e=>{let{docsRealtime:n}=await W(async()=>{let{docsRealtime:e}=await import(`./docsRealtime-pRgUAX2q.js`);return{docsRealtime:e}},[],import.meta.url);return i.tabEl=n(e)},a.Batch=async e=>{let{docsBatch:n}=await W(async()=>{let{docsBatch:e}=await import(`./docsBatch-CQUyIsim.js`);return{docsBatc
Line truncated
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i}');
|
||||
|
||||
// load the token into the store
|
||||
const token = '...';
|
||||
pb.authStore.save(token, null);
|
||||
`,footnote:t.div({className:`txt-right`},t.a({href:`https://github.com/pocketbase/js-sdk`,target:`_blank`,rel:`noopener noreferrer`,textContent:`JS SDK docs`}))},{title:`Dart SDK`,language:`dart`,value:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${i}');
|
||||
|
||||
// load the token into the store
|
||||
final token = '...';
|
||||
pb.authStore.save(token, null);
|
||||
`,footnote:t.div({className:`txt-right`},t.a({href:`https://github.com/pocketbase/dart-sdk`,target:`_blank`,rel:`noopener noreferrer`,textContent:`Dart SDK docs`}))}]})),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,disabled:()=>r.isLoading,onclick:()=>app.modals.close()},t.span({className:`txt`},`Close`)),t.button({hidden:()=>r.token,type:`submit`,"html-form":n+`_form`,className:()=>`btn expanded-lg ${r.isLoading?`loading`:``}`,disabled:()=>r.isLoading},t.span({className:`txt`},`Generate token`)),t.button({hidden:()=>!r.token,type:`button`,className:()=>`btn secondary expanded-lg ${r.isLoading?`loading`:``}`,onclick:()=>o()},t.span({className:`txt`},`Generate new one`))))}window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openRecordUpsert=function(e,n=null,r={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null,onsave:null,ondelete:null,onduplicate:null,ontokensreset:null,onpasswordresetsend:null,onverificationsend:null}){app.store.errors=null;let i=X(e,n,r);i&&(document.body.appendChild(i),app.modals.open(i))};var Xt=`main`,Zt=`authProviders`;function X(e,n,r){if(!e?.id){console.warn(`[recordUpsertModal] missing required collection`);return}let i,a=`record_upsert_`+app.utils.randomString(),o=app.utils.getLocalHistory(app.consts.COLUMNS_STORAGE_PREFIX+e.id,{}),s=store({isLoading:!0,isSaving:!1,isLocked:!1,originalRecord:{},record:{},initialDraft:null,activeTab:Xt,get isNew(){return app.utils.isEmpty(s.originalRecord?.id)},get isAuthCollection(){return e.type==`auth`},get isSuperusersCollection(){return e.name==`_superusers`},get showTabs(){return!s.isNew&&s.isAuthCollection&&!s.isSuperusersCollection},get excludedFields(){let e=[`id`];return s.isAuthCollection&&e.push(`email`,`emailVisibility`,`verified`,`password`,`tokenKey`),e},get initialDraftHash(){return l(s.initialDraft)},get recordHash(){return l(s.record)},get originalRecordHash(){return l(s.originalRecord)},get hasChanges(){return s.originalRecordHash!=s.recordHash||s.isAuthCollection&&(!!s.record.password||!!s.record.passwordConfirm)},get isFormDisabled(){return s.isLoading||s.isSaving||!s.isNew&&!s.hasChanges}});function c(e,n){switch(e){case`expand`:return;case`password`:case`passwordConfirm`:case`tokenKey`:return s.isAuthCollection?void 0:n}return n}function l(e){return e?JSON.stringify(e,c):``}function u(){return`draft_`+e.id+`_`+(s.originalRecord?.id||``)}function d(){return window.localStorage.getItem(u())||``}function f(){try{let e=d();if(e)return JSON.parse(e)}catch(e){console.warn(`getDraft failure:`,e),m()}return null}function p(e){try{window.localStorage.setItem(u(),e)}catch(e){console.warn(`saveDraft failure:`,e),m()}}function m(){window.localStorage.removeItem(u()),s.initialDraft=null}function h(){if(!s.initialDraft)return;app.store.errors&&(app.store.errors=null);let e=JSON.parse(JSON.stringify(s.initialDraft));m(),s.record=e}let g;function _(){s.initialDraft=f(),g?.unwatch(),g=watch(()=>s.recordHash,(e,n)=>{n!==void 0&&(s.hasChanges?p(e):m())})}async function v(n){if(s.isLoading=!0,g?.unwatch(),n=app.utils.isObject(n)?n:{id:n||``},!n.id){s.originalRecord=JSON.parse(JSON.stringify(n)),s.record=JSON.parse(JSON.stringify(n)),s.isLoading=!1,s.isLocked=!1,_();return}try{s.isLocked=!!app.store.settings?.meta?.hideControls,s.originalRecord=JSON.parse(JSON.stringify(n)),s.record=JSON.parse(JSON.stringify(n));let r=await app.pb.collection(e.name).getOne(n.id,{requestKey:`upsert_load_`+n.id});n.expand&&(r.expand=JSON.parse(JSON.stringify(n.expand))),Object.assign(s.originalRecord,JSON.parse(JSON.stringify(r))),Object.assign(s.record,JSON.parse(JSON.stringify(r))),s.isLoading=!1,setTimeout(()=>{_()},0)}catch(e){e?.isAbort||(app.checkApiError(e),s.isLoading=!1,setTimeout(()=>app.modals.close(i,!0),0))}}function ee(e){for(let n in e)(n.startsWith(`@@`)||s.isAuthCollection&&(n==`password`||n==`passwordConfirm`))&&delete e[n]}async function te(){let n={};for(let e in s.record){if(e==`expand`||e.startsWith(`@@`))continue;let r=s.record[e]?.__raw||s.record[e];r===void 0&&(r=null),n[e]=r}for(let r of e.fields){let i=app.fieldTypes[r.type]?.onrecordsave;i&&await i({collection:e,originalRecord:s.originalRecord,record:s.record,field:r,payload:n})}return n}async function y(n=!0){if(!(s.isLocked||s.isSaving||!s.isNew&&!s.hasChanges)){s.isSaving=!0;try{let a=await te(),o=app.utils.isEmpty(s.originalRecord?.id),c;c=o?await app.pb.collection(e.name).create(a):await app.pb.collection(e.name).update(s.originalRecord.id,a),m(),o?(s.originalRecord=structuredClone(c),s.record=structuredClone(c)):(Object.assign(s.originalRecord,structuredClone(c)),Object.assign(s.record,structuredClone(c)),ee(s.originalRecord),ee(s.record)),r.onsave?.(c,o),app.store.errors=null;let l;l=o?`Successfully created ${e.name} "${c.id}".`:`Successfully updated ${e.name} "${c.id}".`,app.toasts.success(l,{key:`recordSave`}),s.isSaving=!1,n&&app.modals.close(i,!0)}
Line truncated
|
||||
The field cannot be presentable if hidden.`),n})})))},()=>{if(i.showHidden)return t.div({className:`field prop-hidden`},t.input({type:`checkbox`,id:r+`.hidden`,className:`sm`,name:()=>`fields.${e.fieldIndex}.hidden`,checked:()=>!!e.field.hidden,onchange:n=>e.field.hidden=n.target.checked}),t.label({htmlFor:r+`.hidden`},t.span({className:`txt`},`Hidden`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Hide from the JSON API response and filters.`)})))},t.button({hidden:()=>!i.showDuplicate&&(!i.showRemove||e.field.system),type:`button`,title:`More options`,className:`btn sm circle transparent secondary more-btn m-l-auto`,"html-popovertarget":r+`_options_dropdown`},t.i({className:`ri-more-line`,ariaHidden:!0})),t.div({id:r+`_options_dropdown`,className:`dropdown sm field-options-dropdown`,popover:`auto`},()=>{if(i.showDuplicate)return t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,textContent:`Duplicate`,onclick:e=>{o(),e.target.closest(`.dropdown`).hidePopover()}})},()=>{if(!(!i.showRemove||e.field.system))return t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,textContent:`Remove`,onclick:e=>{s(),e.target.closest(`.dropdown`).hidePopover(),e.target.closest(`details`).open=!1}})})))};function yn(e,n=`field`){let r=n,i=2,a=n.match(/\d+$/)?.[0]||``,o=a?n.substring(0,n.length-a.length):n;for(;e?.find(e=>e.name.toLowerCase()==r.toLowerCase());)r=o+((a<<0)+i),i++;return r}function bn(e,n,r){if(!r||n===void 0||n===r||!e?.passwordAuth?.identityFields?.length)return;let i=e.passwordAuth.identityFields;for(let e=0;e<i.length;e++)i[e]==n&&(i[e]=r)}function xn(e,n,r){!r||n===void 0||n===r||!e?.indexes?.length||!e?.fields?.length||e.fields.find(e=>!e[`@toDelete`]&&e.name==n)||(e.indexes=e.indexes.map(e=>app.utils.replaceIndexColumn(e,n,r)))}function Sn(e){let n=`text_`+app.utils.randomString(),r=store({get hasAutogenerate(){return!app.utils.isEmpty(e.field.autogeneratePattern)&&app.utils.isEmpty(e.originalRecord?.id)},get isDisabled(){return!app.utils.isEmpty(e.originalRecord?.id)&&e.field.primaryKey},get isRequired(){return e.field.required&&!r.hasAutogenerate&&!r.isDisabled}});return t.div({className:`record-field-input field-type-text`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:n},t.i({ariaHidden:!0,className:()=>e.field.primaryKey?`ri-key-line`:app.fieldTypes.text.icon}),t.span({className:`txt`},()=>e.field.name)),t.div({className:`autoexpand-wrapper`},t.textarea({id:n,rows:1,name:()=>e.field.name,required:()=>r.isRequired,disabled:()=>r.isDisabled,placeholder:()=>r.hasAutogenerate?`Leave empty to autogenerate...`:``,value:()=>e.record[e.field.name]||``,oninput:n=>e.record[e.field.name]=n.target.value||``}),t.div({className:`input`},()=>e.record[e.field.name],` `))),()=>{if(!e.field.primaryKey||!e.originalRecord?.id)return;let n=e.collection?.fields?.filter(e=>e.type==`autodate`)||[];if(!n.length)return;let r=[];for(let i of n)r.push(`${i.name}: ${app.utils.displayValue(e.record[i.name])}`);return t.div({className:`field addon`},t.i({className:`ri-information-line txt-hint link-faded`,ariaDescription:app.attrs.tooltip(r.join(`
|
||||
`),`left`)}))}),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function Cn(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.min`},t.span({className:`txt`},`Min length`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Clear the field or set it to 0 for no limit.`)})),t.input({type:`number`,id:n+`.min`,name:()=>`fields.${e.fieldIndex}.min`,step:1,min:0,max:2**53-1,placeholder:`No min limit`,value:()=>e.field.min||``,oninput:n=>{e.field.min=parseInt(n.target.value,10)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.max`},t.span({className:`txt`},`Max length`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Clear the field or set it to 0 to fallback to the default limit.`)})),t.input({type:`number`,id:n+`.max`,name:()=>`fields.${e.fieldIndex}.max`,step:1,min:()=>e.field.min||0,max:2**53-1,placeholder:`Default to max 5000 characters`,value:()=>e.field.max||``,oninput:n=>{e.field.max=parseInt(n.target.value,10)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.pattern`},t.span({className:`txt`},`Validation pattern`),()=>{if(e.field.primaryKey)return t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`All record ids have forbidden characters and unique case-insensitive (ASCII) validations in addition to the user defined regex pattern.`)})}),t.input({type:`text`,id:n+`.pattern`,name:()=>`fields.${e.fieldIndex}.pattern`,value:()=>e.field.pattern||``,oninput:n=>e.field.pattern=n.target.value})),t.div({className:`field-help`},`Ex. `,t.code(null,`^[a-z0-9]+$`))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.autogeneratePattern`},t.span({className:`txt`},`Autogenerate pattern`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Set and autogenerate text matching the pattern on missing record create value.`)})),t.input({type:`text`,id:n+`.autogeneratePattern`,name:()=>`fields.${e.fieldIndex}.autogeneratePattern`,value:()=>e.field.autogeneratePattern||``,oninput:n=>e.field.autogeneratePattern=n.target.value})),t.div({className:`field-help`},`Ex. `,t.code(null,`[a-z0-9]{30}`))),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string.`)})))]})}function wn(e){return t.div({className:`record-field-view field-type-text`},()=>{let n=e.record[e.field.name]||``;if(n==``)return t.span({className:`missing-value`});if(e.field.primaryKey){let r=null;return e.record?.collectionName==`_superusers`&&app.store.superuser?.id==e.record?.id&&(r=t.strong({className:`txt`,textContent:` (you)`})),t.span({className:`label`},app.components.copyButton(n),t.span({className:`txt-ellipsis`},app.utils.truncate(n),r))}return e.short?t.span({className:`txt txt-ellipsis`,textContent:app.utils.truncate(n)}):n})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.text={icon:`ri-text`,label:`Plain text`,settings:Cn,input:Sn,view:wn,filterModifiers:e=>[`lower`],dummyData:(e,n=!1)=>e.primaryKey?app.utils.randomString(15):`example text`};function Tn(e){let n=`editor_`+app.utils.randomString(),r=store({lazyEditor:``}),i;return t.div({className:`record-field-input field-type-editor large-modal`,onmount:()=>{i=setTimeout(()=>{r.lazyEditor=app.components.tinymce({id:n,name:()=>e.field.name,required:()=>e.field.required,convertURLs:()=>e.field.convertURLs,value:()=>e.record[e.field.name]||``,onchange:n=>{e.record[e.field.name]=n}})},0)},onunmount:()=>{clearTimeout(i)}},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.editor.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name)),()=>r.lazyEditor),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function En(e){let n=`f_`+app.utils.randomString();return store({showInfo:!1}),app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},t.span(null,`Max size `),t.small(null,`(bytes)`)),t.input({type:`number`,id:n+`.maxSize`,name:()=>`fields.${e.fieldIndex}.maxSize`,min:0,step:1,max:2**53-1,placeholder
Line truncated
|
||||
This field is disabled if "Only domains" is set.`)})),t.input({type:`text`,id:n+`.exceptDomains`,disabled:()=>!app.utils.isEmpty(e.field.onlyDomains),name:()=>`fields.${e.fieldIndex}.exceptDomains`,value:()=>app.utils.joinNonEmpty(e.field.exceptDomains),onchange:n=>e.field.exceptDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.onlyDomains`},t.span({className:`txt`},`Only domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are ONLY allowed.
|
||||
This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:n+`.onlyDomains`,disabled:()=>!app.utils.isEmpty(e.field.exceptDomains),name:()=>`fields.${e.fieldIndex}.onlyDomains`,value:()=>app.utils.joinNonEmpty(e.field.onlyDomains),onchange:n=>e.field.onlyDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string.`)})))]})}function $(e){return t.div({className:`record-field-view field-type-email`},()=>{let n=e.record[e.field.name]||``;return n?e.short?t.span({className:`txt txt-ellipsis`,textContent:app.utils.truncate(n)}):n:t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.email={icon:`ri-mail-line`,label:`Email`,settings:Fn,input:Pn,view:$,filterModifiers:e=>[`lower`],dummyData:(e,n=!1)=>`test_${app.utils.randomString(3,`123567890`)}@example.com`};function In(e){let n=`url_`+app.utils.randomString();return t.div({className:`record-field-input field-type-url`},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.url.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name)),t.input({type:`url`,id:n,spellcheck:!1,name:()=>e.field.name,required:()=>e.field.required,value:()=>e.record[e.field.name]||``,oninput:n=>e.record[e.field.name]=n.target.value})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function Ln(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.exceptDomains`},t.span({className:`txt`},`Except domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are NOT allowed.
|
||||
This field is disabled if "Only domains" is set.`)})),t.input({type:`text`,id:n+`.exceptDomains`,disabled:()=>!app.utils.isEmpty(e.field.onlyDomains),name:()=>`fields.${e.fieldIndex}.exceptDomains`,value:()=>app.utils.joinNonEmpty(e.field.exceptDomains),onchange:n=>e.field.exceptDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.onlyDomains`},t.span({className:`txt`},`Only domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are ONLY allowed.
|
||||
This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:n+`.onlyDomains`,disabled:()=>!app.utils.isEmpty(e.field.exceptDomains),name:()=>`fields.${e.fieldIndex}.onlyDomains`,value:()=>app.utils.joinNonEmpty(e.field.onlyDomains),onchange:n=>e.field.onlyDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string.`)})))]})}function Rn(e){return t.div({className:`record-field-view field-type-url`},()=>{let n=e.record[e.field.name]||``;return n?t.a({href:()=>n,className:`txt txt-ellipsis`,rel:`noopener noreferrer`,target:`_blank`,textContent:app.utils.truncate(n),ariaDescription:app.attrs.tooltip(`Open in new tab`),onclick:e=>{e.stopPropagation()}}):t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.url={icon:`ri-link`,label:`URL`,settings:Ln,input:In,view:Rn,dummyData:(e,n=!1)=>`https://example.com`};var zn=Intl.DateTimeFormat().resolvedOptions().timeZone;function Bn(e){let n=`date_`+app.utils.randomString();return t.div({className:`record-field-input field-type-date`},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.date.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name,` (`,zn,`)`)),t.input({id:n,step:1,type:`datetime-local`,name:()=>e.field.name,required:()=>e.field.required,value:()=>app.utils.toDatetimeLocalInputValue(e.record[e.field.name]),onchange:n=>{n.target.value&&(e.record[e.field.name]=app.utils.toRFC3339Datetime(n.target.value))},onblur:n=>{e.record[e.field.name]=null,e.record[e.field.name]=app.utils.toRFC3339Datetime(n.target.value)}})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function Vn(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.min`},t.span({className:`txt`},`Min date (Local)`)),t.input({type:`datetime-local`,id:n+`.min`,step:1,name:()=>`fields.${e.fieldIndex}.min`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.min),onchange:n=>{e.field.min=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.max`},t.span({className:`txt`},`Max date (Local)`)),t.input({type:`datetime-local`,id:n+`.max`,step:1,name:()=>`fields.${e.fieldIndex}.max`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.max),onchange:n=>{e.field.max=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string.`)})))]})}function Hn(e){return t.div({className:`record-field-view field-type-date`},app.components.formattedDate({value:()=>e.record[e.field.name],short:()=>e.short}))}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.date={icon:`ri-calendar-line`,label:`Datetime`,settings:Vn,input:Bn,view:Hn,dummyData:(e,n=!1)=>new Date().toISOString().replaceAll(`T`,` `)};function Un(e){delete e.clone[e.field.name]}function Wn(e){let n=[{label:`Create`,value:1},{label:`Update`,value:2},{label:`Create/Update`,value:3}];function r(e){return e.onCreate&&e.onUpdate?3:e.onUpdate?2:1}function i(n){switch(n){case 1:e.field.onCreate=!0,e.field.onUpdate=!1;break;case 2:e.field.onCreate=!1,e.field.onUpdate=!0;break;case 3:e.field.onCreate=!0,e.field.onUpdate=!0;break}}let a=store({isDropdownOpen:!1});return app.components.fieldSettings(e,{header:t.div({className:`field header-select autodate-select`,ariaDescription:app.attrs.tooltip(`Auto set on`,`left`),onmount:()=>{i(r(e.field))}},app.comp
Line truncated
|
||||
Leave empty for no restriction.`)})),app.components.select({max:99,placeholder:`No restriction`,options:app.utils.mimeTypes.map(e=>({value:e.mimeType,label:()=>t.div({className:`inline-flex gap-10`},t.span({className:`txt`},e.ext||`-`),t.small({className:`txt-hint`},e.mimeType))})),name:()=>`fields.${e.fieldIndex}.mimeTypes`,value:()=>app.utils.toArray(e.field.mimeTypes),onchange:n=>e.field.mimeTypes=n.map(e=>e.value)})),t.div({className:`field-help`},t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`mimeTypesDropdown`},t.span({className:`txt`},`Choose presets`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`mimeTypesDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`image/jpeg`,`image/png`,`image/svg+xml`,`image/gif`,`image/webp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Images (jpg, png, svg, gif, webp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/pdf`,`application/msword`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,`application/vnd.ms-excel`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Documents (pdf, doc/docx, xls/xlsx)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`video/mp4`,`video/mpeg`,`video/x-msvideo`,`video/quicktime`,`video/3gpp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Videos (mp4, mpeg, avi, mov, 3gp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/zip`,`application/x-7z-compressed`,`application/x-rar-compressed`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Archives (zip, 7zip, rar)`})))),t.div({className:()=>e.field.maxSelect>1?`col-sm-6`:`col-sm-9`},t.div({className:`field`},t.label({htmlFor:n+`.thumbs`},t.span({className:`txt`},`Thumb sizes`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of additional thumb sizes for image files, along with the default thumb size of 100x100. The thumbs are generated lazily on first access.`)})),t.input({type:`text`,id:n+`.thumbs`,placeholder:`e.g. 50x50, 480x720`,name:()=>`fields.${e.fieldIndex}.thumbs`,value:()=>app.utils.joinNonEmpty(e.field.thumbs),onchange:n=>e.field.thumbs=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},t.span({className:`txt m-r-5`},`Use comma as separator.`),t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`thumbFormatsDropdown`},t.span({className:`txt`},`Supported formats`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`thumbFormatsDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.ul({className:`m-0 p-l-sm`},t.li(null,t.strong(null,`WxH`),t.span(null,` (e.g. 100x50) - crop to WxH viewbox (from center)`)),t.li(null,t.strong(null,`WxHt`),t.span(null,` (e.g. 100x50t) - crop to WxH viewbox (from top)`)),t.li(null,t.strong(null,`WxHb`),t.span(null,` (e.g. 100x50b) - crop to WxH viewbox (from bottom)`)),t.li(null,t.strong(null,`WxHf`),t.span(null,` (e.g. 100x50f) - fit inside a WxH viewbox (without cropping)`)),t.li(null,t.strong(null,`0xH`),t.span(null,` (e.g. 0x50) - resize to H height preserving the aspect ratio`)),t.li(null,t.strong(null,`Wx0`),t.span(null,` (e.g. 100x0) - resize to W width preserving the aspect ratio`)))))),t.div({className:`col-sm-3`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},`Max size`),t.input({type:`number`,id:n+`.maxSize`,step:1,min:0,max:2**53-1,placeholder:`~5MB default`,name:()=>`fields.${e.fieldIndex}.maxSize`,value:()=>e.field.maxSize||``,oninput:n=>e.field.maxSize=parseInt(n.target.value,10)})),t.div({className:`field-help`},`In bytes.`)),t.div({className:`col-sm-3`,hidden:()=>e.field.maxSelect<<0<2},t.div({className:`field`},t.label({htmlFor:n+`.maxSelect`},`Max select`),t.input({type:`number`,id:n+`.maxSelect`,placeholder:`Default to single`,step:1,min:2,required:!0,max:2**53-1,name:()=>`fields.${e.fieldIndex}.maxSelect`,value:()=>e.field.maxSelect||``,onchange:n=>{let r=parseInt(n.target.value,10);r>1?e.field.maxSelect=r:e.field.maxSelect=1}}))),t.div({className:`col-sm-12`},t.div({className:`field m-t-5 m-b-5`},t.input({className:`switch`,type:`checkbox`,id:n+`.protected`,name:()=>`fields.${e.fieldIndex}.protected`,checked:()=>!!e.field.protected,onchange:n=>e.field.protected=n.target.checked}),t.label({htmlFor:n+`.protected`},t.span({className:`txt`},`Protected`),t.small({className:`txt-hint`},`File download requests will need to satisfy the View API rule (`,t.a({href:`https://pocketbase.io/docs/files-handling#protected-files`,target:`_blank`,rel:`noopener noreferrer`,textContent:`Learn more`}),`).`)))),t.div({className:`col-sm-12`},t.div({class
Line truncated
|
||||
`),oninput:n=>{let r=n.target.value.trimStart().replaceAll(`
|
||||
|
||||
`,`
|
||||
`).split(`
|
||||
`);e.field.values=r,app.utils.deleteByPath(app.store.errors,`fields.${e.fieldIndex}.values`)},onchange:n=>{let r=new Set,i=n.target.value.split(`
|
||||
`);for(let e of i)e!=``&&r.add(e);e.field.values=Array.from(r)},onblur:e=>{(!e.relatedTarget||!i.contains(e.relatedTarget))&&i.hidePopover()}}))),a=[watch(()=>{e.field.values?.length&&e.field.maxSelect>e.field.values.length&&(e.field.maxSelect=e.field.values.length)})];return app.components.fieldSettings(e,{header:[t.div({className:`field header-select field-select-choices-input`,onunmount:()=>{a.forEach(e=>e?.unwatch())}},t.input({type:`text`,placeholder:`Add choices*`,className:`txt-left inline-error`,value:()=>e.field.values?.join(` • `)||``,name:()=>`fields.${e.fieldIndex}.values`,onfocus:e=>(i?.showPopover({source:e.target}),i.querySelector(`textarea`)?.focus(),!1)}),i),t.div({className:`field header-select single-multiple-select`},app.components.select({required:!0,options:r,value:()=>e.field.maxSelect>1,onchange:n=>{n?.[0]?.value?e.field.maxSelect=e.field.values.length||2:e.field.maxSelect=1}}))],content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-12`,hidden:()=>e.field.maxSelect<<0<2},t.div({className:`field`},t.label({htmlFor:n+`.maxSelect`},`Max select`),t.input({type:`number`,id:n+`.maxSelect`,placeholder:`Default to single`,step:1,min:2,max:()=>e.field.values?.length||2,name:()=>`fields.${e.fieldIndex}.maxSelect`,value:()=>e.field.maxSelect||``,onchange:n=>{let r=parseInt(n.target.value,10);r>1?e.field.maxSelect=r:e.field.maxSelect=1}}))),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},()=>e.field.maxSelect>1?`(!=[])`:`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(()=>`Requires the field value to be nonempty ${e.field.maxSelect>1?`array`:`string`}.`)})))]})}function rr(e){return t.div({className:`record-field-view field-type-select`},()=>{let n=app.utils.toArray(e.record[e.field.name],!1);return n.length?n.map(e=>t.span({className:`label`,title:e,textContent:app.utils.truncate(e,100)})):t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.select={icon:`ri-list-check`,label:`Select`,settings:nr,input:tr,view:rr,filterModifiers:e=>e.maxSelect>1?[`each`,`length`]:[],dummyData:(e,n=!1)=>e.maxSelect>1?e.values?.slice(0,2)||[]:e.values?.[0]||``};function ir(e){let n=`json_`+app.utils.randomString(),r=store({value:``}),i=[watch(()=>e.record[e.field.name],(n,i)=>{if(!(n!==``&&n===r.value)){if(typeof n==`string`&&!n.startsWith(`"`)&&!n.endsWith(`"`)){r.value=JSON.stringify(n===void 0?null:n),e.record[e.field.name]=r.value;return}typeof n==`string`&&n.startsWith(`"`)&&n.endsWith(`"`)?r.value=n:n===null?r.value=`null`:r.value=JSON.stringify(n===void 0?null:n,null,2)}})];function a(){let n=r.value.trim();if(n===``){e.record[e.field.name]=null;return}try{let r=JSON.parse(n);typeof r==`string`?e.record[e.field.name]=JSON.stringify(r):e.record[e.field.name]=r}catch{e.record[e.field.name]=n}}return t.div({className:`record-field-input field-type-json`},t.div({className:`field`,onunmount:()=>{clearTimeout(void 0),i.forEach(e=>e?.unwatch())}},t.label({htmlFor:n},t.i({className:app.fieldTypes.json.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name),t.span({hidden:()=>ar(r.value.trim()),className:`json-state`,ariaDescription:app.attrs.tooltip(`Invalid JSON`,`left`)},t.i({className:`ri-error-warning-fill txt-danger`,ariaHidden:!0})),t.span({hidden:()=>!ar(r.value.trim()),className:`json-state`,ariaDescription:app.attrs.tooltip(`Valid JSON`,`left`)},t.i({className:`ri-checkbox-circle-fill txt-success`,ariaHidden:!0}))),app.components.codeEditor({language:`js`,id:n,name:()=>e.field.name,required:()=>e.field.required,value:()=>r.value,oninput:e=>r.value=e,onblur:()=>a()})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function ar(e){if(e===``)return!0;try{return JSON.parse(e),!0}catch{return!1}}function or(e){try{let n=e.record[e.field.name];typeof n==`string`&&JSON.parse(n)}catch(n){throw new y({status:400,response:{message:`Invalid JSON data`,data:{[e.field.name]:{code:`invalid_json`,message:n.toString()}}}})}}function sr(e){let n=`f_`+app.utils.randomString(),r=store({showInfo:!1});return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},t.span(null,`Max size `),t.small(null,`(bytes)`)),t.input({type:`number`,id:n+`.maxSize`,name:()=>`fields.${e.fieldIndex}.maxSize`,min:0,step:1,max:2**53-1,placeholder:`Default to max ~1MB`,value:
Line truncated
|
||||
To target the newly submitted ones you can use @request.body.*.`)})],name:`updateRule`,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.updateRule,oninput:n=>e.collection.updateRule=n})),t.div({className:`col-12`,ariaDescription:r()},app.components.ruleField({label:`Delete rule`,name:`deleteRule`,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.deleteRule,oninput:n=>e.collection.deleteRule=n}))]}),()=>{if(e.collection.type==`auth`)return[t.hr({className:`m-t-base m-b-base`}),t.button({type:`button`,onmount:()=>{n.showAuthRules=e.collection.manageRule!==null||e.collection.authRule!==``},className:()=>`btn secondary sm ${n.showAuthRules?``:`transparent`}`,onclick:()=>{n.showAuthRules=!n.showAuthRules}},t.span({className:`txt`},`Additional auth collection rules`),t.i({ariaHidden:!0,className:()=>n.showAuthRules?`ri-arrow-drop-up-line`:`ri-arrow-drop-down-line`})),app.components.slide(()=>n.showAuthRules,t.div({className:`grid sm m-t-sm`},t.div({className:`col-12`,ariaDescription:r()},app.components.ruleField({label:`Authentication rule`,name:`authRule`,placeholder:``,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.authRule,oninput:n=>e.collection.authRule=n}),t.div({className:`field-help`},t.p(null,`This rule is executed every time `,t.strong(null,`before authentication`),` allowing you to restrict who can authenticate.`),t.p(null,`For example, to allow only verified users you can set it to `,t.code(null,`verified = true`),`.`),t.p(null,`Leave it empty to allow anyone with an account to authenticate.`),t.p(null,`To disable authentication entirely you can change it to "Set superusers only".`))),t.div({className:`col-12`,ariaDescription:r()},app.components.ruleField({label:`Manage rule`,name:`manageRule`,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.manageRule,oninput:n=>e.collection.manageRule=n}),t.div({className:`field-help`},t.p(null,`This rule is executed in addition to the `,t.strong(null,`create`),` and `,t.strong(null,`update`),` API rules.`),t.p(null,`It enables superuser-like permissions to allow fully managing the auth record(s), eg. changing the password without requiring to enter the old one, directly updating the verified state or email, etc.`)))))]})}var Tr=`test_view_query`;function Er(e){let n=`query_`+app.utils.randomString(),r=`SELECT.FROM.WHERE.LEFT JOIN.INNER JOIN.ON.AS.GROUP BY.HAVING.ORDER BY.ASC.DESC.LIMIT.OFFSET.WITH.NOT.IN.AND.OR.EXISTS.LIKE.CAST.REAL.DECIMAL.NUMERIC.INT.TEXT.BOOL`.split(`.`),i=store({testRecords:[],testError:``,isTesting:!1});async function a(n){if(i.isTesting=!0,i.testRecords=[],(app.store.errors?.viewQuery||app.store.errors?.fields)&&(delete app.store.errors.viewQuery,delete app.store.errors.fields),!n){i.testError=``,i.isTesting=!1;return}try{let r=await app.pb.collections.dryRunViewQuery(n,{requestKey:Tr});e.collection?.id?i.testRecords=r.sample.map(n=>(n.collectionId=e.collection?.id,n.collectionName=e.collection?.name,n)):i.testRecords=r.sample,i.testError=``,i.isTesting=!1}catch(e){e.isAbort||(i.testError=e.message||`Invalid query.`,i.isTesting=!1)}}let o,s=[watch(()=>e.collection?.viewQuery,e=>{clearTimeout(o),o=setTimeout(()=>a(e),200)})];return t.div({pbEvent:`collectionViewQueryTabContent`,className:`collection-tab-content collection-view-query-tab-content`,onunmount:()=>{clearTimeout(o),app.pb.cancelRequest(Tr),s.forEach(e=>e?.unwatch())}},t.div({className:`grid`},t.div({className:`col-12`},t.div({className:`txt-right txt-sm m-b-10`},t.button({type:`button`,className:`txt-bold link-hint`,"html-popovertarget":n+`caveats_dropdown`},()=>`Query caveats`)),t.div({id:n+`caveats_dropdown`,className:`dropdown sm query-caveats-dropdown`,popover:`auto`},t.ul(null,t.li(null,`Wildcard columns (*) are not supported.`),t.li(null,`The query must have a unique `,t.code(null,`id`),` column.`,t.br(),`If your query doesn't have a suitable one, you can use the universal `,t.code(null,`(ROW_NUMBER() OVER()) as id`),`.`),t.li(null,`Expressions must be aliased with a valid formatted field name, e.g. `,t.code(null,`MAX(balance) as maxBalance`),`.`),t.li(null,`Combined/multi-spaced expressions must be wrapped in parenthesis, e.g. `,t.code(null,`(MAX(balance) + 1) as maxBalance`),`.`),t.li(null,`UNION expressions are supported but the entire query must be wrapped in parenthesis.`))),t.div({className:`field`},t.label({htmlFor:n+`.viewQuery`},t.span({className:`txt`},`Select query`),t.span({hidden:()=>!i.testError,className:`query-state`,ariaDescription:app.attrs.tooltip(`Invalid query`,`left`)},t.i({className:`ri-error-warning-fill txt-danger`,ariaHidden:!0})),t.span({hidden:()=>!!i.testError,className:`query-state`,ariaDescription:app.attrs.tooltip(`Valid query`,`left`)},t.i({className:`ri-checkbox-circle-fill txt-success`,ariaHidden:!0}))),app.components.codeEditor({id:n+`.viewQuery`,name:`viewQuery`,language:`sql`,required:!0,autocomplete:r,className:`inLine truncated
|
||||
`+e,`top`,`code`)})},t.div({className:`btns`},t.button({type:`button`,className:()=>`btn expanded-lg ${a.isSaving?`loading`:``}`,disabled:()=>!a.canSave,onclick:()=>s(!0)},t.span({className:`txt`},()=>a.isNew?`Create`:`Save changes`)),t.button({type:`button`,title:`Save options`,className:()=>`btn p-5`,disabled:()=>!a.canSave,"html-popovertarget":i+`save_options`},t.i({className:`ri-arrow-up-s-line`,ariaHidden:!0})),t.div({id:i+`save_options`,className:`dropdown nowrap`,popover:`auto`},t.button({type:`button`,className:`dropdown-item`,onclick:e=>{e.target.closest(`.dropdown`).hidePopover(),s(!1)}},t.span({className:`txt`},`Save and continue`),t.small({className:`txt-hint`},`(Ctrl+S)`)),t.hr(),t.button({type:`button`,className:`dropdown-item`,onclick:e=>{e.target.closest(`.dropdown`).hidePopover(),u()}},t.span({className:`txt`},`Reset form`)))))),r}function Or(e){let n=JSON.parse(JSON.stringify(app.store.collectionScaffolds[e.type])),r=JSON.parse(JSON.stringify(e.fields))||[],i=r.filter(e=>!e.system);e.fields=n.fields||[];for(let n of r){if(!n.system)continue;let r=e.fields.find(e=>e.name==n.name);r&&Object.assign(r,n)}for(let n of i)e.fields.push(n);if(e.indexes=e.indexes||[],e.indexes.length){let r=n?.indexes||[];indexesLoop:for(let n=e.indexes.length-1;n>=0;n--){let i=app.utils.parseIndex(e.indexes[n]),a=i.indexName.toLowerCase();for(let i of r)if(a==app.utils.parseIndex(i).indexName.toLowerCase()){e.indexes.splice(n,1);continue indexesLoop}for(let r of i.columns)if(!e.fields.find(e=>e.name.toLowerCase()==r.name.toLowerCase())){e.indexes.splice(n,1);continue indexesLoop}}}app.utils.mergeUnique(e.indexes,n.indexes),e.indexes=e.indexes?.map(n=>app.utils.replaceIndexFields(n,{tableName:e.name||``}))}function kr(e,n){let r=`truncate_`+app.utils.randomString(),i=store({isSubmitting:!1,nameConfirm:``});async function a(){if(i.isSubmitting||!e.originalCollection?.name||e.originalCollection.name!=i.nameConfirm)return!1;i.isSubmitting=!0;try{return await app.pb.collections.truncate(e.originalCollection.name),n.ontruncate?.(JSON.parse(JSON.stringify(e.originalCollection))),app.toasts.success(`Successfully truncated collection "${e.originalCollection.name}".`),i.isSubmitting=!1,!0}catch(e){i.isSubmitting=!1,app.checkApiError(e)}return!1}return t.button({type:`button`,className:`dropdown-item txt-danger`,disabled:()=>i.isSubmitting,onclick:n=>{n.target.closest(`.dropdown`).hidePopover(),app.modals.confirm(t.div(null,t.h6({className:`block txt-center`},`Do you really want to delete all records of the collection?`),t.div({className:`confirm-collection-label txt-bold m-t-sm m-b-sm`},`Type the collection name `,t.div({className:`label`},()=>e.originalCollection.name,app.components.copyButton(()=>e.originalCollection?.name)),` to confirm:`),t.div({className:`field`},t.label({htmlFor:r+`.confirm_name`},`Collection name`),t.input({id:r+`.confirm_name`,type:`text`,required:!0,pattern:()=>RegExp.escape(e.originalCollection.name),value:()=>i.nameConfirm,oninput:e=>i.nameConfirm=e.target.value}))),async()=>{if(document.getElementById(r+`.confirm_name`)?.reportValidity(),!await a())return!1;app.modals.close(n.target.closest(`.modal`))},()=>{i.nameConfirm=``})}},t.i({className:`ri-eraser-line`,ariaHidden:!0}),t.span({className:`txt`},`Truncate`))}function Ar(e,n){let r=`delete_`+app.utils.randomString(),i=store({isSubmitting:!1,nameConfirm:``});async function a(){if(i.isSubmitting||!e.originalCollection?.name||e.originalCollection.name!=i.nameConfirm)return!1;i.isSubmitting=!0;try{return await app.pb.collections.delete(e.originalCollection.name),n?.ondelete?.(JSON.parse(JSON.stringify(e.originalCollection))),app.utils.removeByKey(app.store.collections,`id`,e.originalCollection.id),app.toasts.success(`Successfully deleted collection "${e.originalCollection.name}".`),i.isSubmitting=!1,!0}catch(e){i.isSubmitting=!1,app.checkApiError(e)}return!1}return t.button({type:`button`,className:`dropdown-item txt-danger`,disabled:()=>i.isSubmitting,onclick:n=>{n.target.closest(`.dropdown`).hidePopover();let o=n.target.closest(`.modal`);app.modals.confirm(t.div({className:`block`},t.h6({className:`block txt-center`},()=>e.originalCollection.type==`view`?`Do you really want to delete the selected collection?`:`Do you really want to delete the selected collection and all its records`),t.div({className:`confirm-collection-label txt-bold m-t-sm m-b-sm`},`Type the collection name `,t.div({className:`label`},()=>e.originalCollection.name,app.components.copyButton(()=>e.originalCollection?.name)),` to confirm:`),t.div({className:`field`},t.label({htmlFor:r+`.confirm_name`},`Collection name`),t.input({id:r+`.confirm_name`,type:`text`,required:!0,pattern:()=>RegExp.escape(e.originalCollection.name),value:()=>i.nameConfirm,oninput:e=>i.nameConfirm=e.target.value}))),async()=>{if(document.getElementById(r+`.confirm_name`)?.reportValidity(),!await a())return!1;app.modals.close(o)},()=>{i.nameConfirm=``})}},t.i({className:`ri-delete-bin-7-line`,ariaHidden
Line truncated
|
||||
...
|
||||
-----END PRIVATE KEY-----`,value:()=>a.privateKey||``,oninput:e=>a.privateKey=e.target.value})),t.div({className:`field-help`},`The key is not stored on the server and it is used only for generating the signed JWT.`)))),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,onclick:()=>app.modals.close(n)},t.span({className:`txt`},`Close`)),t.button({"html-form":r+`_form`,type:`submit`,className:`btn expanded`},t.i({className:`ri-key-line`,ariaHidden:!0}),t.span({className:`txt`},`Generate secret`)))),n}function Hr(){return t.div({className:`inline-flex gap-5`},t.span(null,`Default log levels:`),()=>{let e=[];for(let n in app.utils.logLevels)e.push(t.code(null,`${n}:${app.utils.logLevels[n].label}`));return e})}window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openLogsSettings=function(e={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null,onsave:null}){let n=Ur(e);n&&(document.body.appendChild(n),app.modals.open(n))};function Ur(e){let n,r=store({isLoading:!1,isSaving:!1,formSettings:{},initFormSettingsHash:``,get hasChanges(){return r.initFormSettingsHash!=JSON.stringify(r.formSettings)}});function i(e={}){r.formSettings={logs:e?.logs||{}},r.initFormSettingsHash=JSON.stringify(r.formSettings)}async function a(){r.isLoading=!0;try{i(await app.pb.settings.getAll({requestKey:`logsSettings`})),r.isLoading=!1}catch(e){e.isAbort||(r.isLoading=!1,app.checkApiError(e))}}async function o(){if(r.hasChanges){r.isSaving=!0;try{let a=await app.pb.settings.update(app.utils.filterRedactedProps(r.formSettings));e.onsave?.(a),i(a),app.toasts.success(`Successfully saved logs settings.`),r.isSaving=!1,app.modals.close(n)}catch(e){e.isAbort||(r.isSaving=!1,app.checkApiError(e))}}}return n=t.div({pbEvent:`logsSettingsModal`,className:`modal popup sm logs-settings-modal`,onbeforeopen:n=>(a(),e.onbeforeopen?.(n)),onafteropen:n=>{e.onafteropen?.(n)},onbeforeclose:n=>e.onbeforeclose?.(n),onafterclose:n=>{e.onafterclose?.(n),n?.remove()}},t.header({className:`modal-header`},t.h5({className:`m-auto`},`Logs settings`)),()=>r.isLoading?t.div({className:`modal-content flex`,style:`min-height: 200px`},t.span({className:`loader m-auto`})):[t.form({pbEvent:`logsSettingsForm`,id:`logsSettingsForm`,className:`modal-content logs-settings-form`,onsubmit:e=>{e.preventDefault(),o()}},t.div({className:`grid`},t.div({className:`col-lg-12`},t.field({className:`field`},t.label({htmlFor:`logs.maxDays`},`Max days retention`),t.input({type:`number`,id:`logs.maxDays`,name:`logs.maxDays`,min:0,required:!0,value:()=>r.formSettings.logs.maxDays,oninput:e=>r.formSettings.logs.maxDays=e.target.value<<0})),t.div({className:`field-help`},`Set to `,t.code(null,0),` to disable logs persistence.`)),t.div({className:`col-lg-12`},t.field({className:`field`},t.label({htmlFor:`logs.minLevel`},`Min log level`),t.input({type:`number`,id:`logs.minLevel`,name:`logs.minLevel`,min:-100,max:100,required:!0,value:()=>r.formSettings.logs.minLevel,oninput:e=>r.formSettings.logs.minLevel=e.target.value<<0})),t.div({className:`field-help`},t.div(null,`Logs with level below the minimum will be ignored.`),Hr())),t.div({className:`col-lg-12`},t.field({className:`field`},t.input({type:`checkbox`,id:`logs.logIP`,name:`logs.logIP`,className:`switch`,checked:()=>r.formSettings.logs.logIP,onchange:e=>r.formSettings.logs.logIP=e.target.checked}),t.label({htmlFor:`logs.logIP`},`Enable IP logging`))),t.div({className:`col-lg-12`},t.field({className:`field`},t.input({type:`checkbox`,id:`logs.logAuthId`,name:`logs.logAuthId`,className:`switch`,checked:()=>r.formSettings.logs.logAuthId,onchange:e=>r.formSettings.logs.logAuthId=e.target.checked}),t.label({htmlFor:`logs.logAuthId`},`Enable Auth Id logging`))))),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,onclick:()=>app.modals.close(n),disabled:()=>r.isSaving},t.span({className:`txt`},`Close`)),t.button({type:`submit`,"html-form":`logsSettingsForm`,className:()=>`btn ${r.isSaving?`loading`:``}`,disabled:()=>!r.hasChanges||r.isSaving},t.span({className:`txt`},`Save changes`)))]),n}function Wr(e){return t.div({className:()=>`label log-level-label level-${e.level}`},t.span({className:`txt`},()=>`${app.utils.logLevels[e.level]?.label||`UNKN`} (${e.level})`))}window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openLogPreview=function(e,n={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null}){let r=Jr(e,n);r&&(document.body.appendChild(r),app.modals.open(r))};var Gr=[`execTime`,`type`,`auth`,`authId`,`status`,`method`,`url`,`referer`,`remoteIP`,`userIP`,`userAgent`,`error`,`details`];function Kr(e){app.utils.downloadJSON(e,`log_`+e.created.replaceAll(/[-:\. ]/gi,``)+`.json`)}function qr(e){app.utils.copyToClipboard(JSON.stringify(e,null,2)),app.toasts.success(`Log copied to clipboard!`)}function Jr(e,n){let r,i=store({isLoading:!1,log:null,get isRequ
Line truncated
|
||||
(list changed)`:``}`})),t.div({hidden:()=>!app.store.activeCollection?.id,pbEvent:`pageHeaderPrimaryBtns`,className:`page-header-primary-btns`},t.button({type:`button`,className:`btn outline api-preview-btn`,onclick:()=>app.modals.openApiPreview(app.store.activeCollection)},t.i({className:`ri-code-s-slash-line`,ariaHidden:!0}),t.span({className:`txt`,textContent:`API preview`})),()=>{if(app.store.activeCollection?.type!=`view`)return t.button({type:`button`,className:`btn new-record-btn`,onclick:()=>app.modals.openRecordUpsert(app.store.activeCollection)},t.i({className:`ri-add-line`,ariaHidden:!0}),t.span({className:`txt`,textContent:`New record`}))})),t.div({hidden:()=>!app.store.isLoadingCollections||app.store.activeCollection?.id,className:`block txt-center p-base`},t.span({className:`loader lg`})),t.div({hidden:()=>app.store.isLoadingCollections||app.store.activeCollection?.id,className:`block txt-center p-base`},t.h6({className:`txt`},()=>app.store.collections?.length?`Select collection from the sidebar.`:`No collections found.`)),app.components.recordsSearchbar({hidden:()=>!app.store.activeCollection?.id,collection:()=>app.store.activeCollection,value:()=>r.filter,onsubmit:e=>r.filter=e}),()=>{if(app.store.activeCollection?.id)return app.components.recordsList({className:`m-t-sm`,reset:()=>r.reset,collection:()=>app.store.activeCollection,filter:()=>r.filter,sort:()=>r.sort,onselect:e=>{r.activeRecordIdOrModel=e},onchange:(e,n)=>{r.filter=e,r.sort=n},suggestReset:()=>r.suggestReset,onSuggestResetChange:e=>{r.suggestReset=!!e}})},t.footer({className:`page-footer`},t.span({className:()=>`total-count ${r.isTotalCountLoading?`faded`:``}`},`Total: `,()=>r.totalCount),app.components.credits())))}function fi(){return document.body.clientWidth>600&&document.body.clientHeight>=720?150:100}function pi(e){let n=store({stats:[]});async function r(r){if(r){e.isChartLoading=!0;try{let i=(e.presets||[]).concat(app.utils.normalizeSearchFilter(e.filter,[`level`,`message`,`data`])),a=await app.pb.logs.getStats({filter:i.filter(Boolean).map(e=>`(`+e+`)`).join(`&&`)}),o=a.length,s=[],c=[];e.totalFound=0;for(let n=0;n<o;n++){let r=new Date(a[n].date.replace(` `,`T`)).getTime()/1e3;s.push(r),c.push(a[n].total),e.totalFound+=a[n].total,a[n+1]?.date?new Date(a[n+1].date.replace(` `,`T`)).getTime()/1e3-r>3600&&(s.push(r+3600),c.push(0)):n+1==o&&(s.push(r+3600),c.push(0))}n.stats=a,e.isChartLoading=!1,hi(r,[s,c],e)}catch(n){n?.isAbort||(e.isChartLoading=!1,console.warn(`failed to load logs chart:`,n))}}}let i=[];return t.div({pbEvent:`logsChart`,className:()=>[`logs-chart`,e.isChartLoading?`loading`:null,e.zoom?.min&&e.zoom?.max?`zoomed`:``,!n.stats.length||!e.isFirstLoadReady?`nodata`:null].filter(Boolean).join(` `),onmount:n=>{i.push(watch(()=>[e.reset,e.filter,e.presets?.length],()=>r(n))),n._resizeChartFunc=()=>{clearTimeout(n._resizeTimeoutId),n._resizeTimeoutId=setTimeout(()=>{n?._uplot&&n._uplot.setSize({width:n.clientWidth,height:fi()})},100)},window.addEventListener(`resize`,n._resizeChartFunc)},onunmount:e=>{i.forEach(e=>e?.unwatch()),e._uplot?.destroy(),e._resizeChartFunc&&(clearTimeout(e._resizeTimeoutId),window.removeEventListener(`resize`,e._resizeChartFunc),e._resizeChartFunc=null,e._resizeTimeoutId=null)}},t.button({type:`button`,className:()=>`logs-reset-zoom-ctrl ${e.zoom?.min&&e.zoom?.max?``:`hidden`}`,onclick(){e.zoom={}}},t.div({className:`content-primary`},`Reset zoom`),t.div({className:`content-secondary`},`(drag the timeline to pan)`)),t.span({hidden:()=>!e.isChartLoading,className:()=>`loader logs-chart-loader`}))}function mi(e){let n=e?.data?.[0];n&&e.setScale(`x`,{min:n[0],max:n[n.length-1]})}function hi(e,n,r){let i=window.getComputedStyle(e),a=i.getPropertyValue(`--surfaceTxtHintColor`),o=i.getPropertyValue(`--surfaceAlt1Color`),s=i.getPropertyValue(`--surfaceAlt4Color`),c=i.getPropertyValue(`--surfaceAlt2Color`),l={width:e.clientWidth,height:fi(),legend:{show:!1},cursor:{x:!1,y:!1},scales:{x:{range:(e,r,i)=>r<n[0][0]||i>n[0][n[0].length-1]?[e.scales.x.min,e.scales.x.max]:[r,i]},y:{range:{min:{pad:.2,soft:0,mode:1},max:{pad:.2,soft:0,mode:2}}}},series:[{},{paths:uPlot.paths.stepped({align:1}),points:{show:!1,size:1},width:2,fill:c,stroke:s}],axes:[{show:!0,size:35,lineGap:1,space:60,stroke:a,incrs:[300,600,900,1800,3600,3600*2,3600*3,3600*4,3600*6,3600*12,86400,86400*2],values:[[3600,`{h}{aa}`,`
|
||||
{MMM} {DD}`,null,`
|
||||
{MMM} {DD}`,null,null,null,1],[60,`{h}:{mm}{aa}`,`
|
||||
{MMM} {DD}`,null,`
|
||||
{MMM} {DD}`,null,null,null,1]],grid:{show:!0,stroke:o,width:1},ticks:{show:!0,stroke:o,width:1,size:5}},{show:!0,stroke:a,grid:{show:!0,stroke:o,width:1},ticks:{show:!0,stroke:o,width:1,size:5}}],plugins:[vi(),gi(r),_i(r)]};e._uplot?.destroy(),e._uplot=new uPlot(l,n,e)}function gi(e){let n;return{hooks:{init:r=>{r.over.ondblclick=n=>{e.zoom={}},n=watch(()=>{!e.zoom?.min||!e.zoom?.max?mi(r):r.setScale(`x`,{min:e.zoom.min,max:e.zoom.max})})},destroy:e=>{n?.unwatch()},setSelect:n=>{n.select.width>0&&(e.zoom={min:n.posToVal(n.select.left,`x`),max:n.posToVal(n.select.left+n.select.width,`x`)})}}}}function _i(e){let n;return{hooks:{init:r=>{let i=r.root.querySelectorAll(`.u-axis`);if(!i.length){console.warn(`xPanPlugin requires x axis to be defined`);return}i[0].addEventListener(`mousedown`,i=>{if(!e.zoom?.min)return;let a=i.clientX,{min:o,max:s}=r.scales.x,c=r.bbox.width,l=(s-o)/(c/uPlot.pxRatio),u=i=>{let c=(a-i.clientX)*l;r.setScale(`x`,{min:o+c,max:s+c}),clearTimeout(n),n=setTimeout(()=>{r?.scales?.x&&(e.zoom={min:r.scales.x.min,max:r.scales.x.max})},100)},d=e=>{document.removeEventListener(`mousemove`,u),document.removeEventListener(`mouseup`,d)};document.addEventListener(`mousemove`,u),document.addEventListener(`mouseup`,d)})},destroy:e=>{n&&clearTimeout(n)}}}}function vi(e){let n;return{hooks:{init:e=>{let r=e.over;n=store({date:``,total:0,left:0,top:0,show:!1});let i=t.div({className:()=>`chart-tooltip ${n.show?``:`hidden`}`,onmount(e){e._positionWatcher?.unwatch(),e._positionWatcher=watch(()=>[n.left,n.top],()=>{if(!e)return;let i=e.getBoundingClientRect(),a=n.left;a<0?a=0:a+i.width>r.clientWidth&&(a=r.clientWidth-i.width),e.style.left=a+`px`;let o=n.top-i.height-5;o<0&&(o=n.top+5,o+i.high>r.clientHeight&&(o=r.clientHeight-i.height)),e.style.top=o+`px`})},onunmount(e){e._positionWatcher?.unwatch()}},t.div({className:`content-primary`},()=>`${n.total} ${n.total==1?`request`:`requests`}`),t.div({className:`content-secondary`},()=>n.date));r.appendChild(i),r.addEventListener(`mouseleave`,()=>{n&&(n.show=!1)})},destroy:()=>{n.show=!1},setCursor:e=>{if(!n)return;let r=e.data[0][e.cursor.idx]||0,i=e.data[1][e.cursor.idx]||0;if(r==0||i==0){n.show=!1;return}n.show=!0,n.total=i;let a=new Date(r*1e3),o=new Date(r*1e3+36e5),s=a.toLocaleString(`default`,{month:`short`}),c=a.getDate().toString().padStart(2,`0`);n.date=`${s} ${c} ${yi(a)}-${yi(o)}`,n.left=Math.round(e.valToPos(r,`x`)),n.top=Math.round(e.valToPos(i,`y`))}}}}function yi(e){let n=e.getHours(),r=n>=12?`pm`:`am`;return n=n%12||12,n+r}var bi=50;function xi(e){let n=store({logs:[],lastLoadCount:0,lastPage:1,bulkSelected:{},get canLoadMore(){return n.lastLoadCount>=bi},get totalSelected(){return Object.keys(n.bulkSelected).length},get areAllSelected(){return n.logs.length&&n.logs.length==n.totalSelected},get paddedDefaultLogLevels(){let e=[];for(let n in app.utils.logLevels)e.push((``+n).padStart(2,` `)+`: `+app.utils.logLevels[n].label);return e}}),r;async function i(i=!1){e.isListLoading=!0;try{let a;i?(a=1,r=new Date().toISOString().replace(`T`,` `)):a=n.lastPage+1;let o=(e.presets||[]).concat(app.utils.normalizeSearchFilter(e.filter,[`level`,`message`,`data`]));if(e.zoom?.min&&e.zoom?.max){let n=new Date(e.zoom.min*1e3);n.setSeconds(0),n.setMilliseconds(0);let r=app.utils.toRFC3339Datetime(n),i=new Date(e.zoom.max*1e3);i.setSeconds(59),i.setMilliseconds(999);let a=app.utils.toRFC3339Datetime(i);o.push(`created >= "${r}" && created <= "${a}"`)}else o.push(`created <= "${r}"`);let s=await app.pb.logs.getList(a,bi,{skipTotal:1,sort:`-@rowid`,requestKey:`logs_list`,filter:o.filter(Boolean).map(e=>`(`+e+`)`).join(`&&`)});s.page==1&&(n.logs=[],n.bulkSelected={}),n.lastPage=s.page,n.lastLoadCount=s.items.length;for(let e=0;e<s.items.length;e++)app.utils.pushOrReplaceObject(n.logs,s.items[e]),e>1&&e%20==0&&await new Promise(e=>setTimeout(e,0));e.isListLoading=!1,e.isFirstLoadReady||=!0}catch(n){n.isAbort||(e.isListLoading=!1,app.checkApiError(n))}}function a(e=!0){let r={};if(e)for(let e of n.logs)r[e.id]=e;n.bulkSelected=r}function o(e){let n=[];if(!e.data)return n;if(e.data.type==`request`){for(let r of[`status`,`execTime`,`auth`,`authId`,`userIP`])e.data[r]!==void 0&&n.push({key:r});e.data.referer&&!e.data.referer.includes(window.location.host)&&n.push({key:`referer`})}else{let r=Object.keys(e.data);for(let e of r)e!=`error`&&e!=`details`&&n.length<6&&n.push({key:e})}return e.data.error&&n.push({key:`error`,label:`danger`}),e.data.details&&n.push({key:`details`,label:`warning`}),n}let s=/[-:\. ]/gi;function c(){let e=Object.values(n.bulkSelected).sort((e,n)=>e.created<n.created?1:e.created>n.created?-1:0);if(!e.length)return;if(e.length==1)return app.utils.downloadJSON(e[0],`log_`+e[0].created.replace(` `,`T`).replaceAll(s,``)+`.json`);let r=e[0].created.replace(` `,`T`).replaceAll(s,``),i=e[e.length-1].created.replace(` `,`T`).replaceAll(s,``);return app.utils.downloadJSON(e,`${e.length}_logs_${i}_to_${r}.json`)}let l=[];return t.div({pbEvent:`logsList`,clas
Line truncated
|
||||
`+n.paddedDefaultLogLevels.join(`
|
||||
`),`bottom`,`code`)}))),t.th({className:`col-field-name-message`},t.div({className:`inline-flex gap-5`},t.i({className:`ri-file-list-2-line`,ariaHidden:!0}),t.span({textContent:`Message`}))),t.th({className:`col-field-type-date col-field-name-created`},t.div({className:`inline-flex gap-5`},t.i({className:`ri-calendar-line`,ariaHidden:!0}),t.span({textContent:`Created`}))),t.th({className:`col-meta`}))),t.tbody(null,()=>n.logs?.length?n.logs.map(r=>t.tr({rid:r.id,tabIndex:0,role:`button`,className:()=>`handle ${r.data.type==`request`?`log-request`:``}`,onclick:()=>{e.activeLogIdOrModel=r},onkeypress:n=>{(n.key==`Enter`||n.key==` `)&&(n.preventDefault(),e.activeLogIdOrModel=r)}},()=>[t.td({className:`col-bulk-select`,onclick:e=>e.stopPropagation(),onkeypress:e=>e.stopPropagation()},t.div({className:`field`},t.input({id:`cb_`+r.id,type:`checkbox`,checked:()=>!!n.bulkSelected[r.id],onchange:e=>{let i=JSON.parse(JSON.stringify(n.bulkSelected));e.target.checked?i[r.id]=r:delete i[r.id],n.bulkSelected=i}}),t.label({htmlFor:`cb_`+r.id}))),t.td({className:`col-field-name-level`},Wr(r)),t.td({className:`col-field-name-message`},t.div({className:`content-primary`},()=>app.utils.truncate(r.message,1e3)),t.div({className:`content-secondary`},()=>{let e=[],n=o(r);for(let i of n){let n;n=app.utils.logDataFormatters[i.key]?app.utils.logDataFormatters[i.key](r):app.utils.displayValue(r.data[i.key],80),e.push(t.span({className:`label sm ${i.label||``}`},`${i.key}: ${n}`))}return e})),t.td({className:`col-field-type-date col-field-name-created`},app.components.formattedDate({value:()=>r.created,short:!1})),t.td({className:`col-meta`},t.i({className:`ri-arrow-right-line`,ariaHidden:!0}))])):t.tr(null,t.td({colSpan:99},()=>e.isListLoading?t.span({className:`skeleton-loader`}):t.div({className:`sticky-content txt-center txt-hint`},t.div({className:`txt-bold`},`No logs found.`),t.button({hidden:()=>e.filter?.length||app.utils.isEmpty(e.zoom),type:`button`,className:`btn secondary expanded-lg m-t-10`,onclick(){e.zoom={}}},t.span({className:`txt`},`Reset zoom`)),t.button({hidden:()=>!e.filter?.length,type:`button`,className:`btn secondary expanded-lg m-t-10`,onclick(){e.filter=``}},t.span({className:`txt`},`Clear search`))))),t.tr({hidden:()=>!n.canLoadMore},t.td({colSpan:99},t.button({className:()=>`btn lg secondary load-more-btn ${e.isListLoading?`transparent loading`:``}`,disabled:()=>e.isListLoading,onclick:()=>i()},t.span({className:`txt`},`Load older`)))))),t.div({className:`bulkbar-wrapper`},t.div({hidden:()=>n.totalSelected==0,className:`bulkbar logs-bulkbar`},t.span({className:`txt`},`Selected `,t.strong(null,()=>n.totalSelected),()=>` ${n.totalSelected==1?`log`:`logs`}`),t.button({type:`button`,className:`btn sm secondary pill m-r-auto`,onclick:()=>a(!1)},t.span({className:`txt`},`Reset`)),t.button({type:`button`,className:`btn sm pill`,onclick:()=>c()},t.i({className:`ri-download-line`,ariaHidden:!0}),t.span({className:`txt`},`JSON`)))))}function Si(e){app.store.title=`Logs`;let n=`logId`,r=`filter`,i=`superuserRequests`,a=`pbLogSuperuserRequests`,o=`data.auth!='_superusers'`,s=e.query[n]?.[0]||``,c=e.query[r]?.[0]||``,l=!!(e.query[i]?.[0]<<0)||!!(window.localStorage.getItem(a)<<0),u=store({reset:null,isChartLoading:!1,isListLoading:!1,isFirstLoadReady:!1,zoom:{},presets:l?[]:[o],filter:c,totalFound:null,activeLogIdOrModel:s,get hasIncludeRequestsBySuperusers(){return!u.presets.includes(o)},get isLoading(){return u.isListLoading||u.isChartLoading}});function d(e){return e?typeof e==`string`?e:e?.id:null}function f(){u.reset=Date.now()}let p=[];return[t.div({pbEvent:`logsChartContainer`,className:`logs-chart-container accent-surface`},pi(u)),t.div({pbEvent:`pageLogs`,className:`page page-logs`,onmount(e){p.push(watch(()=>{app.utils.replaceHashQueryParams({[r]:u.filter})}),watch(()=>{let e=+!!u.hasIncludeRequestsBySuperusers;app.utils.replaceHashQueryParams({[i]:e}),window.localStorage.setItem(a,e)}),watch(()=>u.activeLogIdOrModel,()=>{app.utils.replaceHashQueryParams({[n]:d(u.activeLogIdOrModel)}),u.activeLogIdOrModel&&(app.modals.close(null,!0),app.modals.openLogPreview(u.activeLogIdOrModel,{onafterclose:()=>{u.activeLogIdOrModel=null}}))}))},onunmount(e){clearTimeout(e._chartTiemoutId),p.forEach(e=>e?.unwatch())}},t.div({className:`page-content full-height`},t.header({className:`page-header`},t.nav({className:`breadcrumbs`},t.div(null,`Logs`)),t.div({className:`inline-flex gap-sm`},t.button({className:`btn circle transparent secondary tooltip-right`,ariaLabel:app.attrs.tooltip(`Logs settings`),onclick:()=>app.modals.openLogsSettings({onsave:()=>f()})},t.i({className:`ri-settings-3-line`,ariaHidden:!0})),app.components.refreshButton({onclick:f})),app.components.searchbar({className:`logs-searchbar`,historyKey:`pbLogsSearchHistory`,placeholder:"Search term or filter like `level > 0`",value:()=>u.filter||``,onsubmit:e=>u.filter=e,autocomplete:[`id`,`level`,`message`,`created`,{value:`data.`,label:`data.*`}]}),t.div({class
Line truncated
|
||||
It is recommend to list it as trusted.`,`right`)});if(i.isEnabled&&i.possibleProxyHeader&&!e.formSettings.trustedProxy.headers.includes(i.possibleProxyHeader))return t.i({className:`ri-alert-line txt-hint`,ariaDescription:app.attrs.tooltip(`The configured proxy header doesn't match with the detected one.`,`right`)})},t.div({className:`flex-fill`}),()=>i.isEnabled?t.span({className:`label success`},`Enabled`):t.span({className:`label`},`Disabled`),()=>{if(!app.utils.isEmpty(app.store.errors?.trustedProxy))return t.i({className:`ri-error-warning-fill txt-danger`,ariaDescription:app.attrs.tooltip(`Has errors`,`left`)})}),t.p({className:`m-t-0`},`Below you should see your real IP. If not - configure the correct proxy header for your environment.`),t.div({className:`alert info m-b-sm`},t.div({className:`flex gap-5`},t.span(null,`Resolved user IP:`),t.strong(null,()=>i.isLoading?`...`:i.realIP||`N/A`)),t.div({className:`flex gap-5`},t.span(null,`Detected proxy header:`),t.strong(null,()=>i.isLoading?`...`:i.possibleProxyHeader||`N/A`))),t.div({className:`content m-b-sm`},t.p(null,`
|
||||
When PocketBase is deployed on platforms like Fly or it is accessible through proxies such as
|
||||
NGINX, requests from different users will originate from the same IP address (the IP of the proxy
|
||||
connecting to your PocketBase app).
|
||||
`),t.p(null,`
|
||||
In this case to retrieve the actual user IP (used for rate limiting, logging, etc.) you need to
|
||||
properly configure your proxy and list below the trusted headers that PocketBase could use to
|
||||
extract the user IP.
|
||||
`),t.p({className:`txt-bold`},`When using such proxy, to avoid spoofing it is recommended to:`),t.ul({className:`txt-bold`},t.li(null,`use headers that are controlled only by the proxy and cannot be manually set by the users`),t.li(null,`make sure that the PocketBase server can be accessed ONLY through the proxy`)),t.p(null,`You can clear the headers field if PocketBase is not deployed behind a proxy.`)),t.div({className:`grid sm`},t.div({className:`col-lg-9`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`trustedProxy.headers`},`Trusted IP proxy headers`),t.input({type:`text`,id:`trustedProxy.headers`,name:`trustedProxy.headers`,placeholder:`Leave empty to disable`,value:()=>app.utils.joinNonEmpty(e.formSettings.trustedProxy.headers),oninput:n=>{let r=app.utils.splitNonEmpty(n.target.value,`,`),i=app.utils.joinNonEmpty(r);app.utils.joinNonEmpty(e.formSettings.trustedProxy.headers)!=i&&(e.formSettings.trustedProxy.headers=r)}})),t.div({className:`field addon`},t.button({type:`button`,className:()=>`btn sm secondary transparent ${app.utils.isEmpty(e.formSettings.trustedProxy.headers)?`hidden`:``}`,onclick:()=>{e.formSettings.trustedProxy.headers=[]}},t.span({className:`txt`},`Clear`)))),t.div({className:`field-help`},`Comma separated list of headers such as: `,t.div({className:`inline-flex gap-5`},()=>i.suggestedProxyHeaders.map(n=>t.div({role:`button`,className:`label sm link-primary`,onclick:()=>{e.formSettings.trustedProxy.headers=[n]},textContent:n}))))),t.div({className:`col-lg-3`},t.div({className:`field`},t.label({htmlFor:`trustedProxy.useLeftmostIP`},t.span({className:`txt`},`IP priority`),t.i({className:`ri-information-line tooltip-right`,ariaDescription:app.attrs.tooltip(`This is in case the proxy returns more than 1 IP as header value. The rightmost IP is usually considered to be the more trustworthy but this could vary depending on the proxy.`)})),app.components.select({id:`trustedProxy.useLeftmostIP`,name:`trustedProxy.useLeftmostIP`,options:r,required:!0,value:()=>e.formSettings.trustedProxy.useLeftmostIP||!1,onchange:n=>{e.formSettings.trustedProxy.useLeftmostIP=n?.[0]?.value}})))))}function Mi(){app.store.title=`Application settings`;let e=store({isLoading:!1,isSaving:!1,formSettings:null,originalFormSettings:null,get originalFormSettingsHash(){return JSON.stringify(e.originalFormSettings)},get formSettingsHash(){return JSON.stringify(e.formSettings)},get hasChanges(){return e.originalFormSettingsHash!=e.formSettingsHash}});n();async function n(){e.isLoading=!0;try{o(await app.pb.settings.getAll()),e.isLoading=!1}catch(e){e.isAbort||app.checkApiError(e)}}function r(){return JSON.stringify(e.formSettings?.superuserIPs)!=JSON.stringify(e.originalFormSettings?.superuserIPs)}async function i(){let n=app.utils.toArray(e.originalFormSettings?.superuserIPs),r=app.utils.toArray(e.formSettings?.superuserIPs);return!r.length||JSON.stringify(n)==JSON.stringify(r)?a():app.modals.confirm(t.div({className:`txt-center`},t.h6(null,`The ONLY allowed superuser IPs will change to: `,t.br(),t.strong(null,r.join(`, `))),t.p(null,`Please make sure that your IP is in the list or you'll be locked.`),t.p({className:`txt-hint`},`In case of lockout, you can reset the setting with the `,t.a({href:`https://pocketbase.io/docs/going-to-production/#limit-superusers-to-specific-ipssubnets`,target:`_blank`,rel:`noopener noreferrer`,className:`link-primary txt-bold txt-sm`},t.code(null,`superuser ips`,t.i({ariaHidden:!0,className:`ri-arrow-right-up-line txt-sm`}))),` console command.`)),()=>a(),null,{yesButton:`Yes, save changes`})}async function a(){if(!(e.isSaving||!e.hasChanges)){e.isSaving=!0,e.formSettings.rateLimits.rules=Oi(e.formSettings.rateLimits.rules);try{let n=app.utils.filterRedactedProps(e.formSettings),i=await app.pb.settings.update(n);if(r())try{await app.pb.collection(`_superusers`).authRefresh()}catch{app.pb.authStore.clear()}o(i),app.toasts.success(`Successfully saved application settings.`)}catch(e){app.checkApiError(e)}e.isSaving=!1}}function o(n={}){if(app.store.settings=JSON.parse(JSON.stringify(n)),!n.meta?.accentColor){let e=window.getComputedStyle(document.documentElement)?.getPropertyValue(`--accentColor`);e?.startsWith(`#`)&&(n.meta=n.meta||{},n.meta.accentColor=e.toLowerCase()||``)}e.originalFormSettings={superuserIPs:n.superuserIPs||[],meta:n.meta||{},batch:n.batch||{},trustedProxy:n.trustedProxy||{headers:[]},rateLimits:n.rateLimits||{excludedIPs:[],rules:[]}},Oi(e.originalFormSettings.rateLimits.rules),e.formSettings=JSON.parse(JSON.stringify(e.originalFormSettings))}function s(){e.formSettings=JSON.parse(e.originalFormSettingsHash)}return t.div({pbEvent:`pageApplicationSettings`,className:`page page-application-settings`},Ci(),t.div({className:`page-content full-height`},t.header({className:`page-header`},t.nav({className:`breadcrumbs`},t.div({className:`breadcrumb-item`},`Settings`),t.div({className:`breadcrumb-item`},`Application`))),t.div({className:`wra
Line truncated
|
||||
Records update will also require an extra unlock step before save.`)})))),t.div({className:`col-lg-12`},t.hr()),t.div({className:`col-lg-12`},t.div({className:`flex`},t.div({className:`m-r-auto`}),t.button({type:`button`,className:`btn transparent secondary`,disabled:()=>e.isSaving,hidden:()=>!e.hasChanges,onclick:s},t.span({className:`txt`},`Cancel`)),t.button({className:()=>`btn expanded-lg ${e.isSaving?`loading`:``}`,disabled:()=>!e.hasChanges||e.isSaving},t.span({className:`txt`},`Save changes`)))))),t.footer({className:`page-footer`},app.components.credits())))}function Ni(e){let n=`accent_`+app.utils.randomString(),r=store({isTooLight:!1}),i,a;function o(e){clearTimeout(a),document.documentElement.style.setProperty(`--animationSpeed`,`0`),e?document.documentElement.style.setProperty(`--accentColor`,e.toLowerCase()):document.documentElement.style.removeProperty(`--accentColor`),a=setTimeout(()=>{document.documentElement.style.removeProperty(`--animationSpeed`)},100)}let s=[watch(()=>e.formSettings?.meta?.accentColor,e=>{clearTimeout(i),i=setTimeout(()=>{o(e)},100)})];return t.div({className:`field`,ariaDescription:app.attrs.tooltip(()=>r.isTooLight?`Invalid - color is too light`:``),onunmount:()=>{clearTimeout(i),o(e.formSettings.meta.accentColor),s.forEach(e=>e?.unwatch())}},t.label({htmlFor:n},t.span({className:`txt`},`Accent`),t.i({hidden:()=>!r.isTooLight,className:`txt-warning ri-alert-line`})),app.components.colorPicker({id:n,name:`meta.accentColor`,predefinedColors:()=>app.store.predefinedAccentColors,value:()=>e.formSettings.meta.accentColor,onchange:n=>{if(r.isTooLight=!1,!app.utils.isDarkEnoughForWhiteText(n)){r.isTooLight=!0;return}e.formSettings.meta.accentColor=n}}))}function Pi(e={}){let n=store({onsave:null}),r=app.utils.extendStore(n,e),i=[{cron:`0 0 * * *`,label:`Every day at 00:00h`},{cron:`0 0 * * 0`,label:`Every sunday at 00:00h`},{cron:`0 0 * * 1,3`,label:`Every Mon and Wed at 00:00h`},{cron:`0 0 1 * *`,label:`Every first day of the month at 00:00h`}],a=store({showForm:!1,isLoading:!1,isSaving:!1,formSettings:null,initSerialized:`null`,enableAutoBackups:!1,get hasChanges(){return a.initSerialized!=JSON.stringify(a.formSettings)}});async function o(){a.isLoading=!0;try{c(await app.pb.settings.getAll()),a.isLoading=!1}catch(e){e.isAbort||app.checkApiError(e)}}async function s(){if(!(a.isSaving||!a.hasChanges)){a.isSaving=!0;try{let e=app.utils.filterRedactedProps(a.formSettings),r=await app.pb.settings.update(e);n.onsave?.(r),c(r),app.toasts.success(`Successfully saved backups settings.`)}catch(e){app.checkApiError(e)}a.isSaving=!1}}function c(e={}){app.store.settings=JSON.parse(JSON.stringify(e)),a.formSettings={backups:e?.backups||{}},a.enableAutoBackups=!!a.formSettings.backups.cron,a.initSerialized=JSON.stringify(a.formSettings)}function l(){a.formSettings=JSON.parse(a.initSerialized),a.enableAutoBackups=!!a.formSettings.backups.cron}return r.push(watch(()=>{!a.enableAutoBackups&&a.formSettings?.backups?.cron&&(a.formSettings.backups.cron=``)})),t.div({className:`block backups-settings-form-wrapper`,onmount:()=>{o()},onunmount:()=>{r.forEach(e=>e?.unwatch())}},t.button({type:`button`,className:()=>`btn secondary ${a.isLoading?`loading`:``}`,disabled:()=>a.isLoading||a.hasChanges,onclick:()=>a.showForm=!a.showForm},t.span({className:`txt`},`Backup options`),t.i({className:()=>a.showForm?`ri-arrow-up-s-line`:`ri-arrow-down-s-line`,ariaHidden:!0})),app.components.slide(()=>a.showForm,t.form({pbEvent:`backupsSettingsForm`,className:`grid backups-settings-form m-t-base`,inert:()=>a.isSaving,onsubmit:e=>{e.preventDefault(),s()}},()=>a.isLoading?t.div({className:`col-lg-12 txt-center`},t.span({className:`loader lg`})):[t.div({className:`col-lg-12`},t.div({className:`field`},t.input({id:`enableAutoBackupsToggle`,type:`checkbox`,className:`switch`,checked:()=>a.enableAutoBackups,onchange:e=>{a.enableAutoBackups=e.target.checked,a.formSettings.backups.cron||(a.formSettings.backups.cron=i[0].cron)}}),t.label({htmlFor:`enableAutoBackupsToggle`},`Enable auto backups`)),app.components.slide(()=>a.enableAutoBackups,t.div({className:`grid m-t-base m-b-base`},t.div({className:`col-lg-6`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`backups.cron`},`Cron expression`),t.input({id:`backups.cron`,name:`backups.cron`,className:`txt-code`,type:`text`,placeholder:`e.g. 0 0 * * *`,required:()=>a.enableAutoBackups,value:()=>a.formSettings.backups.cron,oninput:e=>a.formSettings.backups.cron=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,className:`btn outline sm`,"html-popovertarget":`cron-presets-dropdown`},t.span({className:`txt`},`Presets`),t.i({className:`ri-arrow-drop-down-line`,ariaHidden:!0})),t.div({id:`cron-presets-dropdown`,className:`dropdown sm txt-nowrap`,popover:`auto`},()=>i.map(e=>t.button({type:`button`,className:()=>`dropdown-item ${a.formSettings.backups.cron==e.cron?`active`:``}`,textContent:e.label,onclick:n=>{a.formSettings.backups.
Line truncated
|
||||
@annually
|
||||
@monthly
|
||||
@weekly
|
||||
@daily
|
||||
@midnight
|
||||
@hourly`)},`macros`),`.`,t.br(),`By default the timezone is in UTC.`)),t.div({className:`col-lg-6`},t.div({className:`field`},t.label({htmlFor:`backups.cronMaxKeep`},`Max @auto backups to keep`),t.input({id:`backups.cronMaxKeep`,name:`backups.cronMaxKeep`,type:`number`,required:()=>a.enableAutoBackups,min:1,value:()=>a.formSettings.backups.cronMaxKeep,oninput:e=>{a.formSettings.backups.cronMaxKeep=parseInt(e.target.value,10)}})))))),t.div({className:`col-lg-12`},app.components.s3ConfigFields({toggleLabel:`Store backups in S3 storage`,testFilesystem:`backups`,config:()=>a.formSettings.backups.s3})),t.div({className:`col-lg-12`},t.hr()),t.div({className:`col-lg-12`},t.div({className:`flex`},t.div({className:`m-r-auto`}),t.button({hidden:()=>!a.hasChanges,type:`button`,className:`btn transparent secondary`,onclick:l},t.span({className:`txt`},`Cancel`)),t.button({className:()=>`btn expanded-lg ${a.isSaving?`loading`:``}`,disabled:()=>!a.hasChanges||a.isSaving},t.span({className:`txt`},`Save changes`))))])))}function Fi(e={oncreated:null}){let n=Ii(e);n&&(document.body.appendChild(n),app.modals.open(n))}function Ii(e){let n,r=`backup_create_`+app.utils.randomString(),i=store({name:``,isSubmitting:!1}),a;async function o(){if(!i.isSubmitting){i.isSubmitting=!0,clearTimeout(a),a=setTimeout(()=>{app.modals.close(n)},1500);try{await app.pb.backups.create(i.name,{requestKey:r}),i.isSubmitting=!1,e.oncreated&&e.oncreated(i.name),app.toasts.success(`Successfully generated new backup.`),app.modals.close(n)}catch(e){e.isAbort||(clearTimeout(a),i.isSubmitting=!1,app.checkApiError(e))}}}return n=t.div({pbEvent:`backupCreateModal`,className:`modal popup backup-create-modal`,onbeforeclose:()=>{i.isSubmitting&&app.toasts.info(`The backup was started but may take a while to complete. You can come back later.`)},onafterclose:e=>{clearTimeout(a),e?.remove()}},t.header({className:`modal-header`},t.h5({className:`m-auto txt-center`},`Initialize new backup`)),t.form({id:r,className:`modal-content backup-restore-form`,autocomplete:`off`,onsubmit:e=>{e.preventDefault(),o()}},t.div({className:`grid`},t.div({className:`col-lg-12`},t.div({className:`alert warning`},t.div({className:`content`},t.p(null,`Please note that during the backup other concurrent write requests may fail since the database will be temporary "locked" (this usually happens only during the ZIP generation).`),t.p({className:`txt-bold`},`If you are using S3 storage for the collections file upload, you'll have to backup them separately since they are not locally stored and they will not be included in the generated backup!`)))),t.div({className:`col-lg-12`},t.div({className:`field`},t.label({htmlFor:r+`_name`},`Backup name`),t.input({id:r+`_name`,name:`name`,type:`text`,pattern:`^[a-z0-9_-]+.zip$`,placeholder:`Leave empty to autogenerate`,value:()=>i.name,oninput:e=>i.name=e.target.value})),t.div({className:`field-help`},`Must be in the format [a-z0-9_-].zip`)))),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,disabled:()=>i.isSubmitting,onclick:()=>app.modals.close(n)},t.span({className:`txt`},`Cancel`)),t.button({"html-form":r,type:`submit`,className:()=>`btn ${i.isSubmitting?`loading`:``}`,disabled:()=>i.isSubmitting},t.span({className:`txt`},`Start backup`)))),n}function Li(e){let n=Ri(e);document.body.appendChild(n),app.modals.open(n)}function Ri(e){let n=`backup_restore_`+app.utils.randomString(),r=store({key:e,keyConfirm:``,isSubmitting:!1,get canSubmit(){return r.key&&r.key==r.keyConfirm}}),i;async function a(){if(!(r.isSubmitting||!r.canSubmit)){clearTimeout(i),r.isSubmitting=!0;try{await app.pb.backups.restore(r.keyConfirm),i=setTimeout(()=>{window.location.reload(),r.isSubmitting=!1},2e3)}catch(e){clearTimeout(i),e?.isAbort||(r.isSubmitting=!1,app.checkApiError(e))}}}return t.div({pbEvent:`backupRestoreModal`,className:`modal popup backup-restore-modal`,onbeforeclose:()=>!r.isSubmitting,onafterclose:e=>{e?.remove()},onunmount:()=>{clearTimeout(i)}},t.header({className:`modal-header`},t.h5({className:`m-auto txt-center`},`Restore `,t.strong(null,()=>r.key))),t.form({id:n,className:`modal-content backup-restore-form`,autocomplete:`off`,onsubmit:e=>{e.preventDefault(),a()}},t.div({className:`grid`},t.div({className:`col-lg-12`},t.div({className:`alert danger`},t.div({className:`content`},t.p({className:`txt-bold`},`Please proceed with extreme caution and use it only with trusted backups!`),t.p(null,`Backup restore currently works only on UNIX based systems.`),t.p(null,`The restore operation will attempt to replace your existing `,t.code(null,`pb_data`),` with the one from the backup and will restart the application process.`),t.p(null,`This means that on success all of your data (including app settings, users, superusers, etc.) will be replaced with the ones from the backup.`),t.p(null,`The operation will be reverted if the backup is invalid (ex. missing `,t.code(null,`data.db`),` file).`),t.p(null,`Below is an oLine truncated
|
||||
Vendored
-82
@@ -1,82 +0,0 @@
|
||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./docsList-BAfVNUIM.js","./expandInfo-DGS0CLSa.js","./fieldsInfo-Bz62125-.js","./filterSyntax-Df6AnI5x.js","./docsView-Be6Uhvcf.js","./docsCreate-Be3S3y5K.js","./docsUpdate-LtD8DbWI.js","./docsListAuthMethods-9feSopQX.js","./docsAuthWithPassword-DEWj8Jyn.js","./docsAuthWithOAuth2-DUIE4EoY.js","./docsAuthWithOTP-CJKph96j.js","./docsAuthRefresh-UjveHHwo.js","./pageInstaller-CboSj8bL.js","./pocketbase.es-B_4DUNUU.js","./pageConfirmPasswordReset-xSsXQyFi.js","./pageConfirmVerification-BSn_UexA.js","./pageConfirmEmailChange-DWXtM27Q.js"])))=>i.map(i=>d[i]);
|
||||
import{a as e,n,r,t as i}from"./pocketbase.es-B_4DUNUU.js";var a=Object.create,o=Object.defineProperty,s=Object.getOwnPropertyDescriptor,c=Object.getOwnPropertyNames,l=Object.getPrototypeOf,u=Object.prototype.hasOwnProperty,d=(e,n)=>()=>(n||e((n={exports:{}}).exports,n),n.exports),f=(e,n,r,i)=>{if(n&&typeof n==`object`||typeof n==`function`)for(var a=c(n),l=0,d=a.length,f;l<d;l++)f=a[l],!u.call(e,f)&&f!==r&&o(e,f,{get:(e=>n[e]).bind(null,f),enumerable:!(i=s(n,f))||i.enumerable});return e},p=(e,n,r)=>(r=e==null?{}:a(l(e)),f(n||!e||!e.__esModule?o(r,`default`,{value:e,enumerable:!0}):r,e));(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))r(e);new MutationObserver(e=>{for(let n of e)if(n.type===`childList`)for(let e of n.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&r(e)}).observe(document,{childList:!0,subtree:!0});function n(e){let n={};return e.integrity&&(n.integrity=e.integrity),e.referrerPolicy&&(n.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?n.credentials=`include`:e.crossOrigin===`anonymous`?n.credentials=`omit`:n.credentials=`same-origin`,n}function r(e){if(e.ep)return;e.ep=!0;let r=n(e);fetch(e.href,r)}})();var m=`abcdefghijklmnopqrstuvwxyz0123456789`,h=`pb_redirect`,g=store({hash:window.location.hash});window.addEventListener(`hashchange`,()=>{g.hash=window.location.hash}),Prism.languages.pbrule={string:Prism.languages.js.string,number:Prism.languages.js.number,function:Prism.languages.js.function,boolean:/\b(?:true|false)\b/i,constant:/\b(?:null)\b/i,comment:{pattern:/\/\/.*/,greedy:!0},italic:/_via_|\:\w+/,keyword:/&&|\|\||\??(?:!~|!=|>=|<=|=|~|>|>|<)(?=[@\w\s]|$)/};var _={isObject(e){return typeof e==`object`&&!!e&&e.constructor===Object},isEmpty(e){return e==null||e===``||Array.isArray(e)&&e.length===0||typeof e==`object`&&app.utils.isEmptyObject(e)},isEmptyObject(e){for(let n in e)return!1;return!0},toArray(e,n=!1){return Array.isArray(e)?e.slice():(n||!_.isEmpty(e))&&e!==void 0?[e]:[]},removeByValue(e,n){if(!Array.isArray(e)){console.warn(`[removeByValue] not an array:`,e);return}for(let r=e.length-1;r>=0;r--)if(e[r]==n){e.splice(r,1);break}},removeByKey(e,n,r){if(!Array.isArray(e)){console.warn(`[removeByKey] not an array:`,e);return}for(let i in e)if(e[i][n]==r){e.splice(i,1);break}},pushUnique(e,n){if(!Array.isArray(e)){console.warn(`[pushUnique] not an array:`,e);return}e.includes(n)||e.push(n)},mergeUnique(e,n){for(let r of n)app.utils.pushUnique(e,r);return e},pushOrReplaceObject(e,n,r=`id`){for(let i=e.length-1;i>=0;i--)if(e[i][r]==n[r]){e[i]=n;return}e.push(n)},filterDuplicatesByKey(e,n=`id`){e=Array.isArray(e)?e:[];let r={};for(let i of e)r[i[n]]=i;return Object.values(r)},filterRedactedProps(e,n=`******`){let r=JSON.parse(JSON.stringify(e||{}));for(let e in r)typeof r[e]==`object`&&r[e]!==null?r[e]=_.filterRedactedProps(r[e],n):r[e]===n&&delete r[e];return r},getByPath(e,n,r=null,i=`.`){let a=e||{},o=(n||``).split(i);for(let e of o){if(!_.isObject(a)&&!Array.isArray(a)||a[e]===void 0)return r;a=a[e]}return a},setByPath(e,n,r,i=`.`){if(typeof e!=`object`||!e){console.warn(`setByPath: data not an object or array.`);return}let a=e,o=n.split(i),s=o.pop();for(let e of o)(!_.isObject(a)&&!Array.isArray(a)||!_.isObject(a[e])&&!Array.isArray(a[e]))&&(a[e]={}),a=a[e];a[s]=r},deleteByPath(e,n,r=`.`){let i=e||{},a=(n||``).split(r),o=a.pop();for(let e of a)(!_.isObject(i)&&!Array.isArray(i)||!_.isObject(i[e])&&!Array.isArray(i[e]))&&(i[e]={}),i=i[e];Array.isArray(i)?i.splice(o,1):_.isObject(i)&&delete i[o],a.length>0&&(Array.isArray(i)&&!i.length||_.isObject(i)&&!Object.keys(i).length)&&(Array.isArray(e)&&e.length>0||_.isObject(e)&&Object.keys(e).length>0)&&_.deleteByPath(e,a.join(r),r)},emptyClone(e,n=[]){let r=JSON.parse(JSON.stringify(e));for(let e in r)n.includes(e)||(typeof r[e]==`string`?r[e]=``:typeof r[e]==`number`?r[e]=0:typeof r[e]==`boolean`?r[e]=!1:Array.isArray(r[e])?r[e]=[]:app.utils.isObject(r[e])&&(r[e]={}));return r},randomString(e=8,n=m){let r=``;for(let i=0;i<e;i++)r+=n.charAt(Math.floor(Math.random()*n.length));return r},randomSecret(e=30){if(typeof crypto>`u`)return app.utils.randomString(e);let n=new Uint8Array(e);crypto.getRandomValues(n);let r=``;for(let i=0;i<e;i++)r+=`-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`.charAt(n[i]%64);return r},sentenize(e,n=!0){if(typeof e!=`string`)return``;if(e=e.trim().split(`_`).join(` `),e===``)return e;if(e=e[0].toUpperCase()+e.substring(1),n){let n=e[e.length-1];n!==`.`&&n!==`?`&&n!==`!`&&(e+=`.`)}return e},trimQuotedValue(e){return typeof e==`string`&&(e[0]==`"`||e[0]==`'`||e[0]=="`")&&e[0]==e[e.length-1]?e.slice(1,-1):e},slugify(e,n=`_`,r=[]){if(e===``)return``;let i={a:/а|à|á|å|â/gi,b:/б/gi,c:/ц|ç/gi,d:/д/gi,e:/е|è|é|ê|ẽ|ë/gi,f:/ф/gi,g:/г/gi,h:/х/gi,i:/й|и|ì|í|î/gi,j:/ж/gi,k:/к/gi,l:/л/gi,m:/м/gi,n:/н|ñ/gi,o:/о|ò|ó|ô|øLine truncated
|
||||
`),n+=r.map(e=>{let n=``;return e.name.includes(`(`)||e.name.includes(` `)?n+=e.name:n+="`"+e.name+"`",e.collate&&(n+=` COLLATE `+e.collate),e.sort&&(n+=` `+e.sort.toUpperCase()),n}).join(`,
|
||||
`),r.length>1&&(n+=`
|
||||
`),n+=`)`,e.where&&(n+=` WHERE ${e.where}`),n},replaceIndexFields(e,n){let r=app.utils.parseIndex(e);return typeof n==`function`?Object.assign(r,n(r)||{}):Object.assign(r,n||{}),app.utils.buildIndex(r)},replaceIndexColumn(e,n,r){if(n===r)return e;let i=app.utils.parseIndex(e),a=!1;for(let e of i.columns)e.name===n&&(e.name=r,a=!0);return a?app.utils.buildIndex(i):e}};window.app=window.app||{},window.app.utils=_,window.app=window.app||{},window.app.utils=window.app.utils||{},window.app.utils.mimeTypes=[{ext:``,mimeType:`application/octet-stream`},{ext:`.xpm`,mimeType:`image/x-xpixmap`},{ext:`.7z`,mimeType:`application/x-7z-compressed`},{ext:`.zip`,mimeType:`application/zip`},{ext:`.xlsx`,mimeType:`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`},{ext:`.docx`,mimeType:`application/vnd.openxmlformats-officedocument.wordprocessingml.document`},{ext:`.pptx`,mimeType:`application/vnd.openxmlformats-officedocument.presentationml.presentation`},{ext:`.epub`,mimeType:`application/epub+zip`},{ext:`.apk`,mimeType:`application/vnd.android.package-archive`},{ext:`.jar`,mimeType:`application/jar`},{ext:`.odt`,mimeType:`application/vnd.oasis.opendocument.text`},{ext:`.ott`,mimeType:`application/vnd.oasis.opendocument.text-template`},{ext:`.ods`,mimeType:`application/vnd.oasis.opendocument.spreadsheet`},{ext:`.ots`,mimeType:`application/vnd.oasis.opendocument.spreadsheet-template`},{ext:`.odp`,mimeType:`application/vnd.oasis.opendocument.presentation`},{ext:`.otp`,mimeType:`application/vnd.oasis.opendocument.presentation-template`},{ext:`.odg`,mimeType:`application/vnd.oasis.opendocument.graphics`},{ext:`.otg`,mimeType:`application/vnd.oasis.opendocument.graphics-template`},{ext:`.odf`,mimeType:`application/vnd.oasis.opendocument.formula`},{ext:`.odc`,mimeType:`application/vnd.oasis.opendocument.chart`},{ext:`.sxc`,mimeType:`application/vnd.sun.xml.calc`},{ext:`.pdf`,mimeType:`application/pdf`},{ext:`.fdf`,mimeType:`application/vnd.fdf`},{ext:``,mimeType:`application/x-ole-storage`},{ext:`.msi`,mimeType:`application/x-ms-installer`},{ext:`.aaf`,mimeType:`application/octet-stream`},{ext:`.msg`,mimeType:`application/vnd.ms-outlook`},{ext:`.xls`,mimeType:`application/vnd.ms-excel`},{ext:`.pub`,mimeType:`application/vnd.ms-publisher`},{ext:`.ppt`,mimeType:`application/vnd.ms-powerpoint`},{ext:`.doc`,mimeType:`application/msword`},{ext:`.ps`,mimeType:`application/postscript`},{ext:`.psd`,mimeType:`image/vnd.adobe.photoshop`},{ext:`.p7s`,mimeType:`application/pkcs7-signature`},{ext:`.ogg`,mimeType:`application/ogg`},{ext:`.oga`,mimeType:`audio/ogg`},{ext:`.ogv`,mimeType:`video/ogg`},{ext:`.png`,mimeType:`image/png`},{ext:`.png`,mimeType:`image/vnd.mozilla.apng`},{ext:`.jpg`,mimeType:`image/jpeg`},{ext:`.jxl`,mimeType:`image/jxl`},{ext:`.jp2`,mimeType:`image/jp2`},{ext:`.jpf`,mimeType:`image/jpx`},{ext:`.jpm`,mimeType:`image/jpm`},{ext:`.jxs`,mimeType:`image/jxs`},{ext:`.gif`,mimeType:`image/gif`},{ext:`.webp`,mimeType:`image/webp`},{ext:`.exe`,mimeType:`application/vnd.microsoft.portable-executable`},{ext:``,mimeType:`application/x-elf`},{ext:``,mimeType:`application/x-object`},{ext:``,mimeType:`application/x-executable`},{ext:`.so`,mimeType:`application/x-sharedlib`},{ext:``,mimeType:`application/x-coredump`},{ext:`.a`,mimeType:`application/x-archive`},{ext:`.deb`,mimeType:`application/vnd.debian.binary-package`},{ext:`.tar`,mimeType:`application/x-tar`},{ext:`.xar`,mimeType:`application/x-xar`},{ext:`.bz2`,mimeType:`application/x-bzip2`},{ext:`.fits`,mimeType:`application/fits`},{ext:`.tiff`,mimeType:`image/tiff`},{ext:`.bmp`,mimeType:`image/bmp`},{ext:`.ico`,mimeType:`image/x-icon`},{ext:`.mp3`,mimeType:`audio/mpeg`},{ext:`.flac`,mimeType:`audio/flac`},{ext:`.midi`,mimeType:`audio/midi`},{ext:`.ape`,mimeType:`audio/ape`},{ext:`.mpc`,mimeType:`audio/musepack`},{ext:`.amr`,mimeType:`audio/amr`},{ext:`.wav`,mimeType:`audio/wav`},{ext:`.aiff`,mimeType:`audio/aiff`},{ext:`.au`,mimeType:`audio/basic`},{ext:`.mpeg`,mimeType:`video/mpeg`},{ext:`.mov`,mimeType:`video/quicktime`},{ext:`.mp4`,mimeType:`video/mp4`},{ext:`.avif`,mimeType:`image/avif`},{ext:`.3gp`,mimeType:`video/3gpp`},{ext:`.3g2`,mimeType:`video/3gpp2`},{ext:`.mp4`,mimeType:`audio/mp4`},{ext:`.mqv`,mimeType:`video/quicktime`},{ext:`.m4a`,mimeType:`audio/x-m4a`},{ext:`.m4v`,mimeType:`video/x-m4v`},{ext:`.heic`,mimeType:`image/heic`},{ext:`.heic`,mimeType:`image/heic-sequence`},{ext:`.heif`,mimeType:`image/heif`},{ext:`.heif`,mimeType:`image/heif-sequence`},{ext:`.mj2`,mimeType:`video/mj2`},{ext:`.dvb`,mimeType:`video/vnd.dvb.file`},{ext:`.webm`,mimeType:`video/webm`},{ext:`.avi`,mimeType:`video/x-msvideo`},{ext:`.flv`,mimeType:`video/x-flv`},{ext:`.mkv`,mimeType:`video/x-matroska`},{ext:`.asf`,mimeType:`video/x-ms-asf`},{ext:`.asf`,mimeType:`video/x-ms-wmv`},{ext:`.asf`,mimeType:`video/asf`},{ext:`.aac`,mimeType:`audio/aac`},{ext:`.voc`,mimeType:`audio/x-unknown`},{ext:`.m3u`,mimeType:`application/vnd.apple.mpegurl`},{ext:`.rmvb`,mimeType:`application/Line truncated
|
||||
`)}o&&(n.classList.add(`error`),r.__errListener=function(){ae(r,n),app.utils.deleteByPath(app.store.errors,i)},r.addEventListener(`input`,r.__errListener),r.addEventListener(`change`,r.__errListener),r.setAttribute(`data-error`,!0),r.setCustomValidity&&r.reportValidity&&r.classList.contains(`inline-error`)?(r.setCustomValidity(o),r.reportValidity(),r._oldTitle=r.title,r.title=o):n.after(t.div({"html-data-input-name":i,className:`field-help error generated-error`,textContent:o})))}}),v.onmessage=e=>{e.data?.collections&&JSON.stringify(app.store.collections)!=JSON.stringify(e.data.collections)&&(app.store.collections=e.data.collections),e.data?.settings&&JSON.stringify(app.store.settings)!=JSON.stringify(e.data.settings)&&(app.store.settings=e.data.settings),e.data?.colorScheme&&(app.store.userColorScheme=e.data.colorScheme)},watch(()=>JSON.stringify(app.store.collections),(e,n)=>{e&&e!=`[]`&&n&&n!=`[]`&&e!=n&&v?.postMessage({collections:JSON.parse(e)})}),watch(()=>JSON.stringify(app.store.settings),(e,n)=>{e&&e!=`{}`&&n&&n!=`{}`&&e!=n&&v?.postMessage({settings:JSON.parse(e)}),window.localStorage.setItem(ee,e)});var b=`#/login`,x=window.location.pathname.endsWith(`/`)?window.location.pathname.substring(0,window.location.pathname.length-1):window.location.pathname;window.app=window.app||{},window.app.pb=new i(`../`,new r(`__pb_superusers__`+x)),app.pb.beforeSend=function(e,n){return n.headers[`x-request-source`]=`pbui`,{url:e,options:n}},app.store.superuser=app.pb.authStore.record,app.pb.authStore.onChange((e,n)=>{!n&&window.location.hash!=b&&(app.modals.close(),window.location.hash=b),app.store.superuser=n}),app.pb.authStore.isValid&&app.pb.collection(app.pb.authStore.record?.collectionName||`_superusers`).authRefresh().catch(e=>{console.warn(`Failed to refresh the existing auth token:`,e);let n=e?.status<<0;(n==401||n==403)&&(app.utils.rememberPath(),app.pb.cancelAllRequests(),app.pb.authStore.clear())}),app.pb.authStore.onChange((e,n)=>{n?.id&&(app.store.loadCollections(),app.store.loadSettings(),app.store.loadOAuth2Providers())});var oe=app.pb.collection;app.pb.collection=function(e){let n=oe.call(this,e);return se(n),n};function se(e){if(e.__customUIEvents)return;e.__customUIEvents=!0;let n=e.create;e.create=function(){return n.apply(e,arguments).then(e=>(setTimeout(()=>{document.dispatchEvent(new CustomEvent(`record:create`,{detail:e})),document.dispatchEvent(new CustomEvent(`record:save`,{detail:e}))},0),e))};let r=e.update;e.update=function(){return r.apply(e,arguments).then(e=>(setTimeout(()=>{document.dispatchEvent(new CustomEvent(`record:update`,{detail:e})),document.dispatchEvent(new CustomEvent(`record:save`,{detail:e}))},0),e))};let i=e.delete;e.delete=function(){return i.apply(e,arguments).then(n=>{let r={id:arguments[0],collectionId:e.collectionIdOrName,collectionName:e.collectionIdOrName};return setTimeout(()=>{document.dispatchEvent(new CustomEvent(`record:delete`,{detail:r}))},0),n})}}var ce=`pbLastFileToken`,S=!1,le=[];app.pb.authStore.onChange((e,n)=>{n?.id||window.localStorage.removeItem(ce)}),window.app.getFileToken=async function(n=``){let r=n&&app.store.collections?.find(e=>e.id==n||e.name==n);if(r&&!r.fields?.find(e=>e.type==`file`&&e.protected))return;let i=window.localStorage.getItem(ce);return(!i||e(i,60))&&(i=await C()),i};async function C(){return new Promise(async(e,n)=>{if(le.push({resolve:e,reject:n}),!S){S=!0;try{let e=await app.pb.files.getToken();window.localStorage.setItem(ce,e),le.forEach(n=>n.resolve(e))}catch(e){le.forEach(n=>n.reject(e))}S=!1,le=[]}})}window.app.checkApiError=function(e,n=!0){if(!e||!(e instanceof Error)||e.isAbort){console.warn(`checkApiError - unexpected error type:`,e);return}let r=e?.status<<0,i=e?.response||{},a=n&&(i.message||e.message||`Something went wrong!`);if(a&&app.toasts.error(a),r==0&&console.log(e),app.utils.isEmpty(i.data)||(app.store.errors=i.data),r===401&&window.location.hash!=b)return app.utils.rememberPath(),app.pb.cancelAllRequests(),app.pb.authStore.clear()};function ue(){return()=>{if(!(!app.store._ready||!app.store.showHeader||!app.store.superuser?.id))return t.header({pbEvent:`appHeader`,rid:`appHeader`,className:`app-header accent-surface`,onmount:async e=>{await new Promise(e=>setTimeout(e,0)),e._scrollToActiveMenuItem=function(){e?.querySelector(`.app-main-nav .header-link.active`)?.scrollIntoView()},e._scrollToActiveMenuItem(),window.addEventListener(`hashchange`,e._scrollToActiveMenuItem)},onunmount:e=>{window.removeEventListener(`hashchange`,e?._scrollToActiveMenuItem)}},t.a({href:`#/`,className:`logo`},t.img({src:()=>app.store.headerLogo,alt:`App logo`})),t.nav({pbEvent:`mainNav`,className:`app-main-nav`},()=>app.store.headerLinks.map(e=>{let n=e.href.startsWith(`#/`);return t.a({href:()=>e.href,target:()=>n?void 0:`_blank`,rel:()=>n?void 0:`noopener noreferrer`,className:n=>`header-link ${e.isActive?.(n)||app.utils.isActivePath(e.href)?`active`:``}`},()=>{if(e.icon)return t.i({className:e.icon,ariaHidden:!0})},t.span(
Line truncated
|
||||
`+Fe:null),"html-class":`formatted-date`,className:()=>`formatted-date ${n.short?`short`:`full`}`,onunmount:()=>{r.forEach(e=>e?.unwatch())}},()=>{if(!n.value)return t.span({className:`missing-value`});if(n.short){let e=n.value.split(` `);return[t.span({className:`primary-date`},e[0]),t.span({className:`secondary-date`},e[1])]}return[t.span({className:`primary-date`},app.utils.toLocalDatetime(n.value)),t.span({className:`secondary-date`},n.value)]})},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.refreshButton=function(e={}){let n=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,tooltip:`Refresh`,className:`btn transparent secondary circle rotate-btn`,disabled:!1,onclick:function(e){}}),r=app.utils.extendStore(n,e),i,a=t.button({rid:n.rid,id:()=>n.id,hidden:()=>n.hidden,inert:()=>n.inert,type:`button`,ariaLabel:app.attrs.tooltip(()=>n.tooltip),disabled:()=>n.disabled,className:()=>n.className,onunmount:()=>{clearTimeout(i),r.forEach(e=>e?.unwatch())},onclick:e=>{e.preventDefault(),n.onclick&&n.onclick(e),a.classList.add(`rotate`),a.addEventListener(`animationend`,()=>{a.classList.remove(`rotate`)}),clearTimeout(i),i=setTimeout(()=>{clearTimeout(i),a.classList.remove(`rotate`)},500)}},t.i({className:`ri-refresh-line`,ariaHidden:!0}));return a},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.searchHistoryButton=function(e={}){let n=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,value:void 0,historyKey:`default`,max:15,openInNewTabParam:`filter`,btnClassName:`btn sm pill secondary transparent p-r-5`,onselect:function(e){}}),r=app.utils.extendStore(n,e),i=store({items:app.utils.getLocalHistory(n.historyKey,[])});function a(e){o(e),i.items.unshift(e)}function o(e){app.utils.removeByValue(i.items,e)}let s=`history_dropdown_`+app.utils.randomString();r.push(watch(()=>n.value,e=>{e&&a(e)})),r.push(watch(()=>{i.items.length>n.max&&(i.items=i.items.slice(0,n.max)),app.utils.saveLocalHistory(n.historyKey,i.items)}));let c=t.div({id:s,className:`dropdown sm left nowrap history-searchbar-dropdown`,popover:`hint`,onclick:e=>(e.stopPropagation(),!1)},t.div({className:`block p-5`},t.small({className:`txt-hint`},`Search history`)),()=>i.items?.length?i.items.slice(0,n.max).map(e=>t.button({type:`button`,className:`dropdown-item txt-code`,onclick:()=>{c.hidePopover(),n.onselect?.(e),a(e)},onauxclick:()=>{if(n.openInNewTabParam){a(e),c.hidePopover();let r=app.utils.replaceHashQueryParams({[n.openInNewTabParam]:e},!1);window.open(r,`_blank`)}}},t.span({className:`txt-ellipsis`,title:e,textContent:e}),t.small({role:`button`,className:`remove-btn link-hint m-l-auto p-l-5 p-r-5`,title:`Clear`,onauxclick:e=>(e.stopPropagation(),!1),onclick:n=>(n.stopPropagation(),o(e),!1)},t.i({className:`ri-close-line`,ariaHidden:!0})))):t.div({rid:`no-history`,className:`block p-5`},t.span(null,`Your recent searches will show up here.`)));return t.button({rid:n.rid,id:()=>n.id,hidden:()=>n.hidden,inert:()=>n.inert,type:`button`,title:`Search history`,className:()=>n.btnClassName,"html-popovertarget":s,onunmount:()=>{r?.forEach(e=>e?.unwatch())}},t.i({className:`ri-search-line`,ariaHidden:!0}),t.i({className:`ri-arrow-drop-down-line`,ariaHidden:!0}),c)},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.s3Test=function(e={}){let n=`s3_test_request`,r=store({rid:void 0,config:null,label:`Use S3 storage`,testFilesystem:`storage`}),i=app.utils.extendStore(r,e),a=store({isTesting:!1,testError:null,get hasError(){return!app.utils.isEmpty(a.testError)}}),o,s;function c(e=150){if(!r.config.enabled){clearTimeout(o);return}a.isTesting=!0,clearTimeout(o),o=setTimeout(()=>{l()},e)}async function l(){if(a.isTesting=!0,!r.config.enabled||!r.testFilesystem){a.testError=null,a.isTesting=!1;return}app.pb.cancelRequest(n),clearTimeout(s),s=setTimeout(()=>{app.pb.cancelRequest(n),a.testError=Error(`S3 test connection timeout.`),a.isTesting=!1},3e4);try{await app.pb.props.testS3(r.testFilesystem,{requestKey:n}),a.testError=null,a.isTesting=!1}catch(e){e?.isAbort||(a.testError=e,a.isTesting=!1,clearTimeout(s))}}return i.push(watch(()=>r.testFilesystem&&r.config,()=>c())),t.div({pbEvent:`s3Test`,rid:r.rid,hidden:()=>!r.testFilesystem,className:()=>`label s3-test-label txt-nowrap ${a.hasError?`warning`:`success`}`,ariaDescription:app.attrs.tooltip(()=>a.testError?.data?.message),onunmount:()=>{clearTimeout(s),clearTimeout(o),i.forEach(e=>e?.unwatch())}},()=>a.isTesting?t.span({className:`loader sm`}):a.hasError?[t.i({className:`ri-error-warning-line txt-warning`,ariaHidden:!0}),t.span({className:`txt`},`Failed to establish S3 connection`)]:[t.i({className:`ri-checkbox-circle-line txt-success`,ariaHidden:!0}),t.span({className:`txt`},`S3 connected successfully`)])},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.s3ConfigFields=function(e={}){let n=store({rid:void 0,id
Line truncated
|
||||
<video controls width="300">
|
||||
<source src="${a}" />
|
||||
<p>Download: <a href="${a}" download="${i}">${i}</a>.</p>
|
||||
</video>
|
||||
`)}})}},{type:`menuitem`,text:`Manual embed`,onAction:()=>{tinymce.activeEditor.execCommand(`mceMedia`)}}])}})}var Ke=`DIV.P.A.EM.B.STRONG.H1.H2.H3.H4.H5.H6.TABLE.TR.TD.TH.TBODY.THEAD.TFOOT.BR.HR.Q.SUP.SUB.DEL.IMG.OL.UL.LI.CODE`.split(`.`);function qe(e){if(e){for(let n of e.children)qe(n);Ke.includes(e.tagName)?(e.removeAttribute(`style`),e.removeAttribute(`class`)):Je(e)}}function Je(e){let n=e.parentNode;for(;e.firstChild;)n.insertBefore(e.firstChild,e);n.removeChild(e)}async function Ye(){if(window.tinymce!==void 0)return;let e=`lazy-tinymce-js`;return document.getElementById(e)?new Promise((e,n)=>{function r(){document.removeEventListener(`tinymceLoadSuccess`,i),document.removeEventListener(`tinymceLoadError`,a)}let i=function(){r(),e()},a=function(e){r(),n(e?.details)};document.addEventListener(`tinymceLoadSuccess`,i),document.addEventListener(`tinymceLoadError`,a)}):new Promise((n,r)=>{document.head.querySelector(`#shablon-script`).after(t.script({id:e,src:`./libs/tinymce/tinymce.min.js`,onload:()=>{n()},onerror:e=>{console.warn(`failed to load tinymce.min.js:`,e),r(e)}}))}).then(()=>{document.dispatchEvent(new CustomEvent(`tinymceLoadSuccess`))}).catch(e=>{document.dispatchEvent(new CustomEvent(`tinymceLoadError`,{detail:e}))})}window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.colorPicker=function(e={}){let n=`picker_`+app.utils.randomString(),r=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,className:``,name:``,required:!1,disabled:!1,value:``,predefinedColors:[],onchange:e=>{},onmount:e=>{},onunmount:e=>{}}),i=app.utils.extendStore(r,e),a=store({inputValue:`#ffffff`});function o(e){e=e?.toLowerCase()||``,r.onchange?.(e)}let s,c=t.input({type:`color`,className:`color-picker-input`,id:()=>r.id,name:()=>r.name,required:()=>r.required,disabled:()=>r.disabled,value:()=>(a.inputValue=r.value||`#ffffff`,r.value||void 0),oninput:e=>{a.inputValue=e.target.value,clearTimeout(s),s=setTimeout(()=>{o(e.target.value)},50)}});return t.div({rid:r.rid,hidden:()=>r.hidden,inert:()=>r.inert,className:()=>`color-picker ${r.className}`,onmount:e=>{r.onmount?.(e)},onunmount:e=>{clearTimeout(s),r.onunmount?.(e),i.forEach(e=>e?.unwatch())}},t.div({className:`color-picker-input-wrapper`},c,t.output({className:`result`,style:()=>`color: lch(from ${a.inputValue||`#ffffff`} calc((49 - l) * infinity) 0 0);`,textContent:()=>a.inputValue})),t.button({hidden:()=>!r.predefinedColors.length,type:`button`,title:`Predefined colors`,className:`link-hint predefined-colors-btn`,"html-popovertarget":n+`predefined-colors-dropdown`},t.i({className:`ri-arrow-down-s-line`,ariaHidden:!0})),t.div({pbEvent:`predefinedColorsDropdown`,id:n+`predefined-colors-dropdown`,className:`dropdown predefined-colors-dropdown`,popover:`auto`},t.div({className:`predefined-colors-list`},()=>r.predefinedColors?.map(e=>t.button({type:`button`,className:()=>`color ${r.value==e?`active`:``}`,style:`background:${e}`,onclick:n=>{c&&(n.target.closest(`.dropdown`)?.hidePopover(),c.value=e||void 0,c.dispatchEvent(new Event(`input`,{bubbles:!0})))}})))))},window.app=window.app||{},window.app.components=window.app.components||{},window.app.components.ruleField=function(e={}){let n=`rule_`+app.utils.randomString(),r=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,className:``,required:!1,disabled:!1,name:void 0,label:void 0,help:void 0,value:null,nullable:!0,placeholder:`Leave empty to grant everyone access...`,autocomplete:e=>[],oninput:e=>{},onmount:e=>{},onunmount:e=>{},get isLocked(){return r.value==null}}),i=app.utils.extendStore(r,e,`isLocked`),a,o=``;function s(e){r.value=e,r.oninput?.(e),a?.dispatchEvent(new CustomEvent(`change`,{detail:e}))}function c(){r.value!==null&&(o=r.value,s(null))}function l(){s(o??``),setTimeout(()=>{document.getElementById(n)?.focus()},0)}return a=t.div({rid:r.rid,id:()=>r.id,hidden:()=>r.hidden,inert:()=>r.inert,"html-name":()=>r.name,className:()=>[`field`,`rule-field`,r.required?`required`:null,r.value===null?`locked`:null,r.disabled?`disabled`:null].filter(Boolean).join(` `),onmount:e=>{r.onmount?.(e)},onunmount:e=>{r.onunmount?.(e),i.forEach(e=>e?.unwatch())}},t.label({htmlFor:n},e=>r.label?typeof r.label==`function`?r.label(e):typeof r.label==`string`?t.span({className:`txt`},r.label):r.label:t.span({className:`txt`},`Rule`),t.span({hidden:()=>!r.isLocked,className:`txt superusers-label`},`(Superusers only)`)),e=>r.isLocked?t.button({type:`button`,className:`unlock-overlay`,disabled:()=>r.disabled,onclick:l},t.span({className:`txt`},`Unlock and set custom rule`),t.i({className:`ri-lock-unlock-line`,ariaHidden:!0})):[app.components.codeEditor({id:n,language:`pbrule`,required:()=>r.required,disabled:()=>r.disabled,value:()=>r.value,oninput:s,placeholder:()=>r.placeholder,autocomplete:r.autocomplete,autocompleteContainer:e}),t.button({hidden:()=>!r.nullable,type:`button`,className:`superuser-toggle`,disabled:()=>r.disabled,onclick:c},t.
Line truncated
|
||||
<defs>
|
||||
<marker id="${s}_arrow1" class="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="context-stroke">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
</marker>
|
||||
</defs>
|
||||
<g class="paths-group" marker-end="url(#${s}_arrow1)"></g>
|
||||
`;let h=m.querySelector(`.paths-group`),g=document.createElementNS(`http://www.w3.org/2000/svg`,`svg`);g.classList.add(`erd-paths`,`front`),g.innerHTML=`
|
||||
<defs>
|
||||
<marker id="${s}_arrow2" class="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto" fill="context-stroke">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" />
|
||||
</marker>
|
||||
</defs>
|
||||
<g class="paths-group" marker-end="url(#${s}_arrow2)"></g>
|
||||
`;let _=g.querySelector(`.paths-group`);return a=t.div({tabIndex:-1,rid:n.rid,id:()=>n.id,hidden:()=>n.hidden,inert:()=>n.inert,className:()=>`erd ${n.className} ${i.isUpdating?`updating`:``} ${i.isPanning?`panning`:``} ${i.activeCollection?`active`:``}`,onkeydown:e=>{(e.ctrlKey||e.metaKey)&&e.key==`0`&&f()},onmount:async e=>{e.addEventListener(`wheel`,r=>{r.preventDefault();let a=e.getBoundingClientRect(),o=r.clientX-a.left,s=r.clientY-a.top,c=(o-i.viewX)/n.scale,l=(s-i.viewY)/n.scale,u=Math.min(Math.max(-r.deltaY*.001+n.scale,Qe),Ze);i.viewX=o-c*u,i.viewY=s-l*u,n.scale=u}),e.addEventListener(`pointerdown`,e=>{e.buttons==1&&(i.isPanning=!0,i.panStartX=e.clientX-i.viewX,i.panStartY=e.clientY-i.viewY)}),e._ondragging=function(e){i.isPanning&&(i.viewX=e.clientX-i.panStartX,i.viewY=e.clientY-i.panStartY)},e._ondragstop=function(){i.isPanning=!1},window.addEventListener(`pointermove`,e._ondragging),window.addEventListener(`pointerup`,e._ondragstop),c()},onunmount:e=>{window.removeEventListener(`pointermove`,e._ondragging),window.removeEventListener(`pointerup`,e._ondragstop),r.forEach(e=>e?.unwatch())}},t.div({className:`erd-area`,style:()=>`transform: translate(${i.viewX}px, ${i.viewY}px) scale(${n.scale});`},m,t.div({className:`erd-tables`},()=>n.collections.map(e=>t.div({style:()=>`left:${i.positions[e.id]?.x||0}px;top:${i.positions[e.id]?.y||0}px`,className:()=>`erd-table type-${e.type} ${p(e)?`active`:``} ${e.system?`system`:``}`,"html-data-collection-id":()=>e.id,"html-data-collection-name":()=>e.name,onmouseenter:()=>{h.querySelectorAll(`[data-to="${e.id}"]`)?.forEach(e=>{e.classList.add(`active-to`),_.append(e)}),h.querySelectorAll(`[data-from="${e.id}"]`)?.forEach(e=>{e.classList.add(`active-from`),_.append(e)}),i.activeCollection=e},onmouseleave:()=>{for(let e of _.children)e.classList.remove(`active-from`,`active-to`);h.append(..._.children),i.activeCollection=null}},t.div({className:`erd-table-row header`},()=>e.name),()=>e.fields?.map(e=>t.div({className:`erd-table-row type-${e.type} ${e.primaryKey?`primary-key`:``}`,"html-data-field-id":()=>e.id,"html-data-field-name":()=>e.name},t.i({ariaHidden:!0,title:()=>e.type,className:()=>`field-icon ${app.fieldTypes[e.type].icon||app.utils.fallbackFieldIcon}`}),t.span({className:`field-name`},()=>e.name),()=>{if(e.hidden)return t.span({className:`label danger field-hidden-label`},`Hidden`)},()=>{if(e.maxSelect!==void 0)return t.span({className:`meta`},e.maxSelect>1?`multiple`:`single`)}))))),g),t.nav({className:`erd-nav`,onmousedown:e=>{e.stopImmediatePropagation()}},t.button({type:`button`,className:`btn sm circle secondary`,title:`Zoom in`,onclick:()=>{n.scale+=.05}},t.i({className:`ri-add-line`,ariaHidden:!0})),t.button({type:`button`,className:`btn sm circle secondary`,title:`Zoom out`,onclick:()=>{n.scale-=.05}},t.i({className:`ri-subtract-line`,ariaHidden:!0})))),a};function et(e,n,r=1,i=``,a=``,o=2){if(!e||!n)return;let s=e.closest(`.erd-area`).getBoundingClientRect(),c=e.getBoundingClientRect(),l=n.getBoundingClientRect();o*=r;let u=c.top-s.top+c.height/2,d=l.top-s.top+l.height/2,f,p;c.left<l.left?(f=c.left-s.left+c.width+o,p=l.left-s.left-o):c.left>l.left?(f=c.left-s.left-o,p=l.left-s.left+l.width+o):(f=c.left-s.left-o,p=l.left-s.left-o),f/=r,p/=r,u/=r,d/=r;let m=f+(p-f)/2;f==p&&(m-=20);let h=`M ${f} ${u}
|
||||
L ${m} ${u}
|
||||
L ${m} ${d}
|
||||
L ${p} ${d}`,g=document.createElementNS(`http://www.w3.org/2000/svg`,`path`);return g.setAttribute(`class`,`relation-path`),g.setAttribute(`data-from`,i||``),g.setAttribute(`data-to`,a||``),g.setAttribute(`d`,h),g}window.app=window.app||{},window.app.components=window.app.components||{};var tt=1e3;window.app.components.pageSidebar=function(e={},...n){let r,i=store({rid:void 0,id:void 0,hidden:void 0,inert:void 0,className:``,widthHistoryKey:`pbPageSidebarWidth`,onmount:void 0,onunmount:void 0}),a=app.utils.extendStore(i,e),o=store({responsiveShow:!1}),s;function c(){if(r){if(window.innerWidth>tt){o.responsiveShow=!1,r.dataset.responsive=!1,s?.remove(),s=null;return}r.dataset.responsive=!0,s||(s=t.button({type:`button`,className:`btn transparent secondary responsive-sidebar-btn`,title:`Toggle sidebar`,onclick:e=>{e.stopPropagation(),o.responsiveShow=!o.responsiveShow}},t.i({className:`ri-menu-2-line`,ariaHidden:!0})),document.body.querySelector(`.page-header .breadcrumbs`).before(s))}}function l(e){if(e.target.closest(`.responsive-close`)){o.responsiveShow=!1;return}if(!(e.target.closest(`.page-sidebar`)||e.target.closest(`.app-header`)||e.target.closest(`.modal`)))return e.preventDefault(),e.stopImmediatePropagation(),o.responsiveShow=!1,!1}return a.push(watch(()=>o.responsiveShow,e=>{e?window.addEventListener(`click`,l,!0):window.removeEventListener(`click`,l,!0)})),r=t.aside({pbEvent:`pageSidebar`,rid:i.rid,id:()=>i.id,hidden:()=>i.hidden,inert:()=>i.inert,className:()=>`page-sidebar ${i.className} ${o.responsiveShow?`active`:``}`,onmount:e=>{c(e),window.addEventListener(`resize`,c),i.onmount?.(e)},onunmount:e=>{i.onunmount?.(e),window.removeEventListener(`click`,l,!0),window.removeEventListener(`resize`,c),s?.remove(),a.forEach(e=>e?.unwatch())}},e=>{let n;return i.widthHistoryKey&&(n=localStorage.getItem(i.widthHistoryKey),n&&(e.style.width=n)),app.components.dragline({ondragstart:n=>{e._startWidth=e.offsetWidth},ondragging:(r,a,o)=>{n=e._startWidth+a+`px`,e.style.width=n,i.widthHistoryKey&&localStorage.setItem(i.widthHistoryKey,n)}})},...n),r};var P=`modulepreload`,nt=function(e,n){return new URL(e,n).href},rt={},F=function(e,n,r){let i=Promise.resolve();if(n&&n.length>0){let e=document.getElementsByTagName(`link`),a=document.querySelector(`meta[property=csp-nonce]`),o=a?.nonce||a?.getAttribute(`nonce`);function s(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}i=s(n.map(n=>{if(n=nt(n,r),n in rt)return;rt[n]=!0;let i=n.endsWith(`.css`),a=i?`[rel="stylesheet"]`:``;if(r)for(let r=e.length-1;r>=0;r--){let a=e[r];if(a.href===n&&(!i||a.rel===`stylesheet`))return}else if(document.querySelector(`link[href="${n}"]${a}`))return;let s=document.createElement(`link`);if(s.rel=i?`stylesheet`:P,i||(s.as=`script`),s.crossOrigin=``,s.href=n,o&&s.setAttribute(`nonce`,o),document.head.appendChild(s),i)return new Promise((e,r)=>{s.addEventListener(`load`,e),s.addEventListener(`error`,()=>r(Error(`Unable to preload CSS for ${n}`)))})}))}function a(e){let n=new Event(`vite:preloadError`,{cancelable:!0});if(n.payload=e,window.dispatchEvent(n),!n.defaultPrevented)throw e}return i.then(n=>{for(let e of n||[])e.status===`rejected`&&a(e.reason);return e().catch(a)})};window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openApiPreview=function(e,n={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null}){let r=it(e,n);r&&(document.body.appendChild(r),app.modals.open(r))};function it(e,n){if(!e){console.warn(`[apiPreviewModal] missing required collection`);return}let r,i=store({activeTab:`List/Search`,tabEl:null,isLoading:!1}),a={"List/Search":async e=>{let{docsList:n}=await F(async()=>{let{docsList:e}=await import(`./docsList-BAfVNUIM.js`);return{docsList:e}},__vite__mapDeps([0,1,2,3]),import.meta.url);return i.tabEl=n(e)},View:async e=>{let{docsView:n}=await F(async()=>{let{docsView:e}=await import(`./docsView-Be6Uhvcf.js`);return{docsView:e}},__vite__mapDeps([4,1,2]),import.meta.url);return i.tabEl=n(e)}};e.type!=`view`&&(a.Create=async e=>{let{docsCreate:n}=await F(async()=>{let{docsCreate:e}=await import(`./docsCreate-Be3S3y5K.js`);return{docsCreate:e}},__vite__mapDeps([5,1,2]),import.meta.url);return i.tabEl=n(e)},a.Update=async e=>{let{docsUpdate:n}=await F(async()=>{let{docsUpdate:e}=await import(`./docsUpdate-LtD8DbWI.js`);return{docsUpdate:e}},__vite__mapDeps([6,5,1,2]),import.meta.url);return i.tabEl=n(e)},a.Delete=async e=>{let{docsDelete:n}=await F(async()=>{let{docsDelete:e}=await import(`./docsDelete-CybOn5jy.js`);return{docsDelete:e}},[],import.meta.url);return i.tabEl=n(e)},a.Realtime=async e=>{let{docsRealtime:n}=await F(async()=>{let{docsRealtime:e}=await import(`./docsRealtime-PMESvmJN.js`);return{docsRealtime:e}},[],import.meta.url);return i.tabEl=n(e)},a.Batch=async e=>{let{docsBatch:n}=await F(async()=>{let{docsBatch:e}=await import(`./docsBatch-DNJl1NTn.js`);return{docsBatc
Line truncated
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const pb = new PocketBase('${i}');
|
||||
|
||||
// load the token into the store
|
||||
const token = '...';
|
||||
pb.authStore.save(token, null);
|
||||
`,footnote:t.div({className:`txt-right`},t.a({href:`https://github.com/pocketbase/js-sdk`,target:`_blank`,rel:`noopener noreferrer`,textContent:`JS SDK docs`}))},{title:`Dart SDK`,language:`dart`,value:`
|
||||
import 'package:pocketbase/pocketbase.dart';
|
||||
|
||||
final pb = PocketBase('${i}');
|
||||
|
||||
// load the token into the store
|
||||
final token = '...';
|
||||
pb.authStore.save(token, null);
|
||||
`,footnote:t.div({className:`txt-right`},t.a({href:`https://github.com/pocketbase/dart-sdk`,target:`_blank`,rel:`noopener noreferrer`,textContent:`Dart SDK docs`}))}]})),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,disabled:()=>r.isLoading,onclick:()=>app.modals.close()},t.span({className:`txt`},`Close`)),t.button({hidden:()=>r.token,type:`submit`,"html-form":n+`_form`,className:()=>`btn expanded-lg ${r.isLoading?`loading`:``}`,disabled:()=>r.isLoading},t.span({className:`txt`},`Generate token`)),t.button({hidden:()=>!r.token,type:`button`,className:()=>`btn secondary expanded-lg ${r.isLoading?`loading`:``}`,onclick:()=>o()},t.span({className:`txt`},`Generate new one`))))}window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openRecordUpsert=function(e,n=null,r={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null,onsave:null,ondelete:null,onduplicate:null,ontokensreset:null,onpasswordresetsend:null,onverificationsend:null}){app.store.errors=null;let i=Ot(e,n,r);i&&(document.body.appendChild(i),app.modals.open(i))};var Et=`main`,Dt=`authProviders`;function Ot(e,n,r){if(!e?.id){console.warn(`[recordUpsertModal] missing required collection`);return}let i,a=`record_upsert_`+app.utils.randomString(),o=app.utils.getLocalHistory(app.consts.COLUMNS_STORAGE_PREFIX+e.id,{}),s=store({isLoading:!0,isSaving:!1,isLocked:!1,originalRecord:{},record:{},initialDraft:null,activeTab:Et,get isNew(){return app.utils.isEmpty(s.originalRecord?.id)},get isAuthCollection(){return e.type==`auth`},get isSuperusersCollection(){return e.name==`_superusers`},get showTabs(){return!s.isNew&&s.isAuthCollection&&!s.isSuperusersCollection},get excludedFields(){let e=[`id`];return s.isAuthCollection&&e.push(`email`,`emailVisibility`,`verified`,`password`,`tokenKey`),e},get initialDraftHash(){return l(s.initialDraft)},get recordHash(){return l(s.record)},get originalRecordHash(){return l(s.originalRecord)},get hasChanges(){return s.originalRecordHash!=s.recordHash||s.isAuthCollection&&(!!s.record.password||!!s.record.passwordConfirm)},get isFormDisabled(){return s.isLoading||s.isSaving||!s.isNew&&!s.hasChanges}});function c(e,n){switch(e){case`expand`:return;case`password`:case`passwordConfirm`:case`tokenKey`:return s.isAuthCollection?void 0:n}return n}function l(e){return e?JSON.stringify(e,c):``}function u(){return`draft_`+e.id+`_`+(s.originalRecord?.id||``)}function d(){return window.localStorage.getItem(u())||``}function f(){try{let e=d();if(e)return JSON.parse(e)}catch(e){console.warn(`getDraft failure:`,e),m()}return null}function p(e){try{window.localStorage.setItem(u(),e)}catch(e){console.warn(`saveDraft failure:`,e),m()}}function m(){window.localStorage.removeItem(u()),s.initialDraft=null}function h(){if(!s.initialDraft)return;app.store.errors&&(app.store.errors=null);let e=JSON.parse(JSON.stringify(s.initialDraft));m(),s.record=e}let g;function _(){s.initialDraft=f(),g?.unwatch(),g=watch(()=>s.recordHash,(e,n)=>{n!==void 0&&(s.hasChanges?p(e):m())})}async function v(n){if(s.isLoading=!0,g?.unwatch(),n=app.utils.isObject(n)?n:{id:n||``},!n.id){s.originalRecord=JSON.parse(JSON.stringify(n)),s.record=JSON.parse(JSON.stringify(n)),s.isLoading=!1,s.isLocked=!1,_();return}try{s.isLocked=!!app.store.settings?.meta?.hideControls,s.originalRecord=JSON.parse(JSON.stringify(n)),s.record=JSON.parse(JSON.stringify(n));let r=await app.pb.collection(e.name).getOne(n.id,{requestKey:`upsert_load_`+n.id});n.expand&&(r.expand=JSON.parse(JSON.stringify(n.expand))),Object.assign(s.originalRecord,JSON.parse(JSON.stringify(r))),Object.assign(s.record,JSON.parse(JSON.stringify(r))),s.isLoading=!1,setTimeout(()=>{_()},0)}catch(e){e?.isAbort||(app.checkApiError(e),s.isLoading=!1,setTimeout(()=>app.modals.close(i,!0),0))}}function ee(e){for(let n in e)(n.startsWith(`@@`)||s.isAuthCollection&&(n==`password`||n==`passwordConfirm`))&&delete e[n]}async function te(){let n={};for(let e in s.record){if(e==`expand`||e.startsWith(`@@`))continue;let r=s.record[e]?.__raw||s.record[e];r===void 0&&(r=null),n[e]=r}for(let r of e.fields){let i=app.fieldTypes[r.type]?.onrecordsave;i&&await i({collection:e,originalRecord:s.originalRecord,record:s.record,field:r,payload:n})}return n}async function y(n=!0){if(!(s.isLocked||s.isSaving||!s.isNew&&!s.hasChanges)){s.isSaving=!0;try{let a=await te(),o=app.utils.isEmpty(s.originalRecord?.id),c;c=o?await app.pb.collection(e.name).create(a):await app.pb.collection(e.name).update(s.originalRecord.id,a),m(),o?(s.originalRecord=structuredClone(c),s.record=structuredClone(c)):(Object.assign(s.originalRecord,structuredClone(c)),Object.assign(s.record,structuredClone(c)),ee(s.originalRecord),ee(s.record)),r.onsave?.(c,o),app.store.errors=null;let l;l=o?`Successfully created ${e.name} "${c.id}".`:`Successfully updated ${e.name} "${c.id}".`,app.toasts.success(l,{key:`recordSave`}),s.isSaving=!1,n&&app.modals.close(i,!0
Line truncated
|
||||
The field cannot be presentable if hidden.`),n})})))},()=>{if(i.showHidden)return t.div({className:`field prop-hidden`},t.input({type:`checkbox`,id:r+`.hidden`,className:`sm`,name:()=>`fields.${e.fieldIndex}.hidden`,checked:()=>!!e.field.hidden,onchange:n=>e.field.hidden=n.target.checked}),t.label({htmlFor:r+`.hidden`},t.span({className:`txt`},`Hidden`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Hide from the JSON API response and filters.`)})))},t.button({hidden:()=>!i.showDuplicate&&(!i.showRemove||e.field.system),type:`button`,title:`More options`,className:`btn sm circle transparent secondary more-btn m-l-auto`,"html-popovertarget":r+`_options_dropdown`},t.i({className:`ri-more-line`,ariaHidden:!0})),t.div({id:r+`_options_dropdown`,className:`dropdown sm field-options-dropdown`,popover:`auto`},()=>{if(i.showDuplicate)return t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,textContent:`Duplicate`,onclick:e=>{o(),e.target.closest(`.dropdown`).hidePopover()}})},()=>{if(!(!i.showRemove||e.field.system))return t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,textContent:`Remove`,onclick:e=>{s(),e.target.closest(`.dropdown`).hidePopover(),e.target.closest(`details`).open=!1}})})))};function Kt(e,n=`field`){let r=n,i=2,a=n.match(/\d+$/)?.[0]||``,o=a?n.substring(0,n.length-a.length):n;for(;e?.find(e=>e.name.toLowerCase()==r.toLowerCase());)r=o+((a<<0)+i),i++;return r}function qt(e,n,r){if(!r||n===void 0||n===r||!e?.passwordAuth?.identityFields?.length)return;let i=e.passwordAuth.identityFields;for(let e=0;e<i.length;e++)i[e]==n&&(i[e]=r)}function W(e,n,r){!r||n===void 0||n===r||!e?.indexes?.length||!e?.fields?.length||e.fields.find(e=>!e[`@toDelete`]&&e.name==n)||(e.indexes=e.indexes.map(e=>app.utils.replaceIndexColumn(e,n,r)))}function G(e){let n=`text_`+app.utils.randomString(),r=store({get hasAutogenerate(){return!app.utils.isEmpty(e.field.autogeneratePattern)&&app.utils.isEmpty(e.originalRecord?.id)},get isDisabled(){return!app.utils.isEmpty(e.originalRecord?.id)&&e.field.primaryKey},get isRequired(){return e.field.required&&!r.hasAutogenerate&&!r.isDisabled}});return t.div({className:`record-field-input field-type-text`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:n},t.i({ariaHidden:!0,className:()=>e.field.primaryKey?`ri-key-line`:app.fieldTypes.text.icon}),t.span({className:`txt`},()=>e.field.name)),t.div({className:`autoexpand-wrapper`},t.textarea({id:n,rows:1,name:()=>e.field.name,required:()=>r.isRequired,disabled:()=>r.isDisabled,placeholder:()=>r.hasAutogenerate?`Leave empty to autogenerate...`:``,value:()=>e.record[e.field.name]||``,oninput:n=>e.record[e.field.name]=n.target.value||``}),t.div({className:`input`},()=>e.record[e.field.name],` `))),()=>{if(!e.field.primaryKey||!e.originalRecord?.id)return;let n=e.collection?.fields?.filter(e=>e.type==`autodate`)||[];if(!n.length)return;let r=[];for(let i of n)r.push(`${i.name}: ${app.utils.displayValue(e.record[i.name])}`);return t.div({className:`field addon`},t.i({className:`ri-information-line txt-hint link-faded`,ariaDescription:app.attrs.tooltip(r.join(`
|
||||
`),`left`)}))}),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function K(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.min`},t.span({className:`txt`},`Min length`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Clear the field or set it to 0 for no limit.`)})),t.input({type:`number`,id:n+`.min`,name:()=>`fields.${e.fieldIndex}.min`,step:1,min:0,max:2**53-1,placeholder:`No min limit`,value:()=>e.field.min||``,oninput:n=>{e.field.min=parseInt(n.target.value,10)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.max`},t.span({className:`txt`},`Max length`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Clear the field or set it to 0 to fallback to the default limit.`)})),t.input({type:`number`,id:n+`.max`,name:()=>`fields.${e.fieldIndex}.max`,step:1,min:()=>e.field.min||0,max:2**53-1,placeholder:`Default to max 5000 characters`,value:()=>e.field.max||``,oninput:n=>{e.field.max=parseInt(n.target.value,10)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.pattern`},t.span({className:`txt`},`Validation pattern`),()=>{if(e.field.primaryKey)return t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`All record ids have forbidden characters and unique case-insensitive (ASCII) validations in addition to the user defined regex pattern.`)})}),t.input({type:`text`,id:n+`.pattern`,name:()=>`fields.${e.fieldIndex}.pattern`,value:()=>e.field.pattern||``,oninput:n=>e.field.pattern=n.target.value})),t.div({className:`field-help`},`Ex. `,t.code(null,`^[a-z0-9]+$`))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.autogeneratePattern`},t.span({className:`txt`},`Autogenerate pattern`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Set and autogenerate text matching the pattern on missing record create value.`)})),t.input({type:`text`,id:n+`.autogeneratePattern`,name:()=>`fields.${e.fieldIndex}.autogeneratePattern`,value:()=>e.field.autogeneratePattern||``,oninput:n=>e.field.autogeneratePattern=n.target.value})),t.div({className:`field-help`},`Ex. `,t.code(null,`[a-z0-9]{30}`))),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function Jt(e){return t.div({className:`record-field-view field-type-text`},()=>{let n=e.record[e.field.name]||``;if(n==``)return t.span({className:`missing-value`});if(e.field.primaryKey){let r=null;return e.record?.collectionName==`_superusers`&&app.store.superuser?.id==e.record?.id&&(r=t.strong({className:`txt`,textContent:` (you)`})),t.span({className:`label`},app.components.copyButton(n),t.span({className:`txt-ellipsis`},app.utils.truncate(n),r))}return e.short?t.span({className:`txt txt-ellipsis`,textContent:app.utils.truncate(n)}):n})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.text={icon:`ri-text`,label:`Plain text`,settings:K,input:G,view:Jt,filterModifiers:e=>[`lower`],dummyData:(e,n=!1)=>e.primaryKey?app.utils.randomString(15):`example text`};function Yt(e){let n=`editor_`+app.utils.randomString(),r=store({lazyEditor:``}),i;return t.div({className:`record-field-input field-type-editor large-modal`,onmount:()=>{i=setTimeout(()=>{r.lazyEditor=app.components.tinymce({id:n,name:()=>e.field.name,required:()=>e.field.required,convertURLs:()=>e.field.convertURLs,value:()=>e.record[e.field.name]||``,onchange:n=>{e.record[e.field.name]=n}})},0)},onunmount:()=>{clearTimeout(i)}},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.editor.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name)),()=>r.lazyEditor),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function Xt(e){let n=`f_`+app.utils.randomString();return store({showInfo:!1}),app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},t.span(null,`Max size `),t.small(null,`(bytes)`)),t.input({type:`number`,id:n+`.maxSize`,name:()=>`fields.${e.fieldIndex}.maxSize`,min:0,step:1,max:2**53-1,placeholder:`DeLine truncated
|
||||
This field is disabled if "Only domains" is set.`)})),t.input({type:`text`,id:n+`.exceptDomains`,disabled:()=>!app.utils.isEmpty(e.field.onlyDomains),name:()=>`fields.${e.fieldIndex}.exceptDomains`,value:()=>app.utils.joinNonEmpty(e.field.exceptDomains),onchange:n=>e.field.exceptDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.onlyDomains`},t.span({className:`txt`},`Only domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are ONLY allowed.
|
||||
This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:n+`.onlyDomains`,disabled:()=>!app.utils.isEmpty(e.field.exceptDomains),name:()=>`fields.${e.fieldIndex}.onlyDomains`,value:()=>app.utils.joinNonEmpty(e.field.onlyDomains),onchange:n=>e.field.onlyDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function on(e){return t.div({className:`record-field-view field-type-email`},()=>{let n=e.record[e.field.name]||``;return n?e.short?t.span({className:`txt txt-ellipsis`,textContent:app.utils.truncate(n)}):n:t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.email={icon:`ri-mail-line`,label:`Email`,settings:an,input:rn,view:on,filterModifiers:e=>[`lower`],dummyData:(e,n=!1)=>`test_${app.utils.randomString(3,`123567890`)}@example.com`};function sn(e){let n=`url_`+app.utils.randomString();return t.div({className:`record-field-input field-type-url`},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.url.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name)),t.input({type:`url`,id:n,spellcheck:!1,name:()=>e.field.name,required:()=>e.field.required,value:()=>e.record[e.field.name]||``,oninput:n=>e.record[e.field.name]=n.target.value})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function cn(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.exceptDomains`},t.span({className:`txt`},`Except domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are NOT allowed.
|
||||
This field is disabled if "Only domains" is set.`)})),t.input({type:`text`,id:n+`.exceptDomains`,disabled:()=>!app.utils.isEmpty(e.field.onlyDomains),name:()=>`fields.${e.fieldIndex}.exceptDomains`,value:()=>app.utils.joinNonEmpty(e.field.exceptDomains),onchange:n=>e.field.exceptDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.onlyDomains`},t.span({className:`txt`},`Only domains`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of domains that are ONLY allowed.
|
||||
This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:n+`.onlyDomains`,disabled:()=>!app.utils.isEmpty(e.field.exceptDomains),name:()=>`fields.${e.fieldIndex}.onlyDomains`,value:()=>app.utils.joinNonEmpty(e.field.onlyDomains),onchange:n=>e.field.onlyDomains=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},`Use comma as separator.`)),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function ln(e){return t.div({className:`record-field-view field-type-url`},()=>{let n=e.record[e.field.name]||``;return n?t.a({href:()=>n,className:`txt txt-ellipsis`,rel:`noopener noreferrer`,target:`_blank`,textContent:app.utils.truncate(n),ariaDescription:app.attrs.tooltip(`Open in new tab`),onclick:e=>{e.stopPropagation()}}):t.span({className:`missing-value`})})}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.url={icon:`ri-link`,label:`URL`,settings:cn,input:sn,view:ln,dummyData:(e,n=!1)=>`https://example.com`};var J=Intl.DateTimeFormat().resolvedOptions().timeZone;function un(e){let n=`date_`+app.utils.randomString();return t.div({className:`record-field-input field-type-date`},t.div({className:`field`},t.label({htmlFor:n},t.i({className:app.fieldTypes.date.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name,` (`,J,`)`)),t.input({id:n,step:1,type:`datetime-local`,name:()=>e.field.name,required:()=>e.field.required,value:()=>app.utils.toDatetimeLocalInputValue(e.record[e.field.name]),onchange:n=>{e.record[e.field.name]=app.utils.toRFC3339Datetime(n.target.value)}})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function Y(e){let n=`f_`+app.utils.randomString();return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.min`},t.span({className:`txt`},`Min date (Local)`)),t.input({type:`datetime-local`,id:n+`.min`,step:1,name:()=>`fields.${e.fieldIndex}.min`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.min),onchange:n=>{e.field.min=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-6`},t.div({className:`field`},t.label({htmlFor:n+`.max`},t.span({className:`txt`},`Max date (Local)`)),t.input({type:`datetime-local`,id:n+`.max`,step:1,name:()=>`fields.${e.fieldIndex}.max`,value:()=>app.utils.toDatetimeLocalInputValue(e.field.max),onchange:n=>{e.field.max=app.utils.toRFC3339Datetime(n.target.value)}}))),t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`},`(!='')`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`Requires the field value to be nonempty string`)})))]})}function dn(e){return t.div({className:`record-field-view field-type-date`},app.components.formattedDate({value:()=>e.record[e.field.name],short:()=>e.short}))}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.date={icon:`ri-calendar-line`,label:`Datetime`,settings:Y,input:un,view:dn,dummyData:(e,n=!1)=>new Date().toISOString().replaceAll(`T`,` `)};function fn(e){delete e.clone[e.field.name]}function pn(e){let n=[{label:`Create`,value:1},{label:`Update`,value:2},{label:`Create/Update`,value:3}];function r(e){return e.onCreate&&e.onUpdate?3:e.onUpdate?2:1}function i(n){switch(n){case 1:e.field.onCreate=!0,e.field.onUpdate=!1;break;case 2:e.field.onCreate=!1,e.field.onUpdate=!0;break;case 3:e.field.onCreate=!0,e.field.onUpdate=!0;break}}let a=store({isDropdownOpen:!1});return app.components.fieldSettings(e,{header:t.div({className:`field header-select autodate-select`,ariaDescription:app.attrs.tooltip(`Auto set on`,`left`),onmount:()=>{i(r(e.field))}},app.components.select({required:!0,options:n,disabled:()=>e.originalCollection?.system,value:()=>r(e.field),onchange:e=>i(e?.[0]?.value),on
Line truncated
|
||||
Leave empty for no restriction.`)})),app.components.select({max:99,placeholder:`No restriction`,options:app.utils.mimeTypes.map(e=>({value:e.mimeType,label:()=>t.div({className:`inline-flex gap-10`},t.span({className:`txt`},e.ext||`-`),t.small({className:`txt-hint`},e.mimeType))})),name:()=>`fields.${e.fieldIndex}.mimeTypes`,value:()=>app.utils.toArray(e.field.mimeTypes),onchange:n=>e.field.mimeTypes=n.map(e=>e.value)})),t.div({className:`field-help`},t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`mimeTypesDropdown`},t.span({className:`txt`},`Choose presets`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`mimeTypesDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`image/jpeg`,`image/png`,`image/svg+xml`,`image/gif`,`image/webp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Images (jpg, png, svg, gif, webp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/pdf`,`application/msword`,`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,`application/vnd.ms-excel`,`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Documents (pdf, doc/docx, xls/xlsx)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`video/mp4`,`video/mpeg`,`video/x-msvideo`,`video/quicktime`,`video/3gpp`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Videos (mp4, mpeg, avi, mov, 3gp)`}),t.button({type:`button`,className:`dropdown-item`,role:`menuitem`,onclick:n=>{e.field.mimeTypes=[`application/zip`,`application/x-7z-compressed`,`application/x-rar-compressed`],n.target.closest(`.dropdown`).hidePopover()},textContent:`Archives (zip, 7zip, rar)`})))),t.div({className:()=>e.field.maxSelect>1?`col-sm-6`:`col-sm-9`},t.div({className:`field`},t.label({htmlFor:n+`.thumbs`},t.span({className:`txt`},`Thumb sizes`),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(`List of additional thumb sizes for image files, along with the default thumb size of 100x100. The thumbs are generated lazily on first access.`)})),t.input({type:`text`,id:n+`.thumbs`,placeholder:`e.g. 50x50, 480x720`,name:()=>`fields.${e.fieldIndex}.thumbs`,value:()=>app.utils.joinNonEmpty(e.field.thumbs),onchange:n=>e.field.thumbs=app.utils.splitNonEmpty(n.target.value,`,`)})),t.div({className:`field-help`},t.span({className:`txt m-r-5`},`Use comma as separator.`),t.button({type:`button`,className:`link-hint gap-0`,"html-popovertarget":n+`thumbFormatsDropdown`},t.span({className:`txt`},`Supported formats`),t.i({className:`ri-arrow-drop-down-fill`,ariaHidden:!0})),t.div({id:n+`thumbFormatsDropdown`,className:`dropdown sm nowrap left p-10`,popover:`auto`},t.ul({className:`m-0 p-l-sm`},t.li(null,t.strong(null,`WxH`),t.span(null,` (e.g. 100x50) - crop to WxH viewbox (from center)`)),t.li(null,t.strong(null,`WxHt`),t.span(null,` (e.g. 100x50t) - crop to WxH viewbox (from top)`)),t.li(null,t.strong(null,`WxHb`),t.span(null,` (e.g. 100x50b) - crop to WxH viewbox (from bottom)`)),t.li(null,t.strong(null,`WxHf`),t.span(null,` (e.g. 100x50f) - fit inside a WxH viewbox (without cropping)`)),t.li(null,t.strong(null,`0xH`),t.span(null,` (e.g. 0x50) - resize to H height preserving the aspect ratio`)),t.li(null,t.strong(null,`Wx0`),t.span(null,` (e.g. 100x0) - resize to W width preserving the aspect ratio`)))))),t.div({className:`col-sm-3`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},`Max size`),t.input({type:`number`,id:n+`.maxSize`,step:1,min:0,max:2**53-1,placeholder:`~5MB default`,name:()=>`fields.${e.fieldIndex}.maxSize`,value:()=>e.field.maxSize||``,oninput:n=>e.field.maxSize=parseInt(n.target.value,10)})),t.div({className:`field-help`},`In bytes.`)),()=>{if(e.field.maxSelect>1)return t.div({className:`col-sm-3`},t.div({className:`field`},t.label({htmlFor:n+`.maxSelect`},`Max select`),t.input({type:`number`,id:n+`.maxSelect`,placeholder:`Default to single`,step:1,min:2,required:!0,max:2**53-1,name:()=>`fields.${e.fieldIndex}.maxSelect`,value:()=>e.field.maxSelect||``,onchange:e=>{let n=parseInt(e.target.value,10);n>1?props.field.maxSelect=n:props.field.maxSelect=1}})))},t.div({className:`col-sm-12`},t.div({className:`field m-t-5 m-b-5`},t.input({className:`switch`,type:`checkbox`,id:n+`.protected`,name:()=>`fields.${e.fieldIndex}.protected`,checked:()=>!!e.field.protected,onchange:n=>e.field.protected=n.target.checked}),t.label({htmlFor:n+`.protected`},t.span({className:`txt`},`Protected`),t.small({className:`txt-hint`},`File download requests will need to satisfy the View API rule (`,t.a({href:`https://pocketbase.io/docs/files-handling#protected-files`,target:`_blank`,rel:`noopener noreferrer`,textContent:`Learn more`}),`).`)))),t.div({className:`col-sm-12`},t.
Line truncated
|
||||
`),oninput:n=>{let r=n.target.value.trimStart().replaceAll(`
|
||||
|
||||
`,`
|
||||
`).split(`
|
||||
`);e.field.values=r,app.utils.deleteByPath(app.store.errors,`fields.${e.fieldIndex}.values`)},onchange:n=>{let r=new Set,i=n.target.value.split(`
|
||||
`);for(let e of i)e!=``&&r.add(e);e.field.values=Array.from(r)},onblur:e=>{(!e.relatedTarget||!i.contains(e.relatedTarget))&&i.hidePopover()}}))),a=[watch(()=>{e.field.values?.length&&e.field.maxSelect>e.field.values.length&&(e.field.maxSelect=e.field.values.length)})];return app.components.fieldSettings(e,{header:[t.div({className:`field header-select field-select-choices-input`,onunmount:()=>{a.forEach(e=>e?.unwatch())}},t.input({type:`text`,placeholder:`Add choices*`,className:`txt-left inline-error`,value:()=>e.field.values?.join(` • `)||``,name:()=>`fields.${e.fieldIndex}.values`,onfocus:e=>(i?.showPopover({source:e.target}),i.querySelector(`textarea`)?.focus(),!1)}),i),t.div({className:`field header-select single-multiple-select`},app.components.select({required:!0,options:r,value:()=>e.field.maxSelect>1,onchange:n=>{n?.[0]?.value?e.field.maxSelect=e.field.values.length||2:e.field.maxSelect=1}}))],content:()=>t.div({className:`grid sm`},()=>{if(e.field.maxSelect>1)return t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.maxSelect`},`Max select`),t.input({type:`number`,id:n+`.maxSelect`,placeholder:`Default to single`,step:1,min:2,max:()=>e.field.values?.length||2,name:()=>`fields.${e.fieldIndex}.maxSelect`,value:()=>e.field.maxSelect||``,onchange:n=>{let r=parseInt(n.target.value,10);r>1?e.field.maxSelect=r:e.field.maxSelect=1}})))},t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.help`},`Help text`),t.input({type:`text`,id:n+`.help`,name:()=>`fields.${e.fieldIndex}.help`,value:()=>e.field.help||``,oninput:n=>e.field.help=n.target.value})))),footer:()=>[t.div({className:`field`},t.input({className:`sm`,type:`checkbox`,id:n+`.required`,name:()=>`fields.${e.fieldIndex}.required`,checked:()=>!!e.field.required,onchange:n=>e.field.required=n.target.checked}),t.label({htmlFor:n+`.required`},t.span({className:`txt`},`Required`),t.small({className:`txt-hint`,textContent:()=>e.field.maxSelect>1?`(!=[])`:`(!='')`}),t.i({className:`ri-information-line link-hint`,ariaDescription:app.attrs.tooltip(()=>`Requires the field value to be nonempty ${e.field.maxSelect>1?`array`:`string`}.`)})))]})}function En(e){return t.div({className:`record-field-view field-type-select`},t.div({className:`inline-flex gap-5`},()=>{let n=app.utils.toArray(e.record[e.field.name],!1);return n.length?n.map(e=>t.span({className:`label`,title:e,textContent:app.utils.truncate(e,100)})):t.span({className:`missing-value`})}))}window.app=window.app||{},window.app.fieldTypes=window.app.fieldTypes||{},window.app.fieldTypes.select={icon:`ri-list-check`,label:`Select`,settings:Tn,input:wn,view:En,filterModifiers:e=>e.maxSelect>1?[`each`,`length`]:[],dummyData:(e,n=!1)=>e.maxSelect>1?e.values?.slice(0,2)||[]:e.values?.[0]||``};function Dn(e){let n=`json_`+app.utils.randomString(),r=store({value:``}),i=[watch(()=>e.record[e.field.name],(n,i)=>{if(!(n!==``&&n===r.value)){if(typeof n==`string`&&!n.startsWith(`"`)&&!n.endsWith(`"`)){r.value=JSON.stringify(n===void 0?null:n),e.record[e.field.name]=r.value;return}typeof n==`string`&&n.startsWith(`"`)&&n.endsWith(`"`)?r.value=n:n===null?r.value=`null`:r.value=JSON.stringify(n===void 0?null:n,null,2)}})];function a(){let n=r.value.trim();if(n===``){e.record[e.field.name]=null;return}try{let r=JSON.parse(n);typeof r==`string`?e.record[e.field.name]=JSON.stringify(r):e.record[e.field.name]=r}catch{e.record[e.field.name]=n}}return t.div({className:`record-field-input field-type-json`},t.div({className:`field`,onunmount:()=>{clearTimeout(void 0),i.forEach(e=>e?.unwatch())}},t.label({htmlFor:n},t.i({className:app.fieldTypes.json.icon,ariaHidden:!0}),t.span({className:`txt`},()=>e.field.name),t.span({hidden:()=>On(r.value.trim()),className:`json-state`,ariaDescription:app.attrs.tooltip(`Invalid JSON`,`left`)},t.i({className:`ri-error-warning-fill txt-danger`,ariaHidden:!0})),t.span({hidden:()=>!On(r.value.trim()),className:`json-state`,ariaDescription:app.attrs.tooltip(`Valid JSON`,`left`)},t.i({className:`ri-checkbox-circle-fill txt-success`,ariaHidden:!0}))),app.components.codeEditor({language:`js`,id:n,name:()=>e.field.name,required:()=>e.field.required,value:()=>r.value,oninput:e=>r.value=e,onblur:()=>a()})),()=>{if(e.field.help)return t.div({className:`field-help`},e.field.help)})}function On(e){if(e===``)return!0;try{return JSON.parse(e),!0}catch{return!1}}function kn(e){try{let n=e.record[e.field.name];typeof n==`string`&&JSON.parse(n)}catch(r){throw new n({status:400,response:{message:`Invalid JSON data`,data:{[e.field.name]:{code:`invalid_json`,message:r.toString()}}}})}}function An(e){let n=`f_`+app.utils.randomString(),r=store({showInfo:!1});return app.components.fieldSettings(e,{content:()=>t.div({className:`grid sm`},t.div({className:`col-sm-12`},t.div({className:`field`},t.label({htmlFor:n+`.maxSize`},t.span(null,`Max size `),t.small(null,`(bytes)`)),t.input({type:`number`,id:n+`.maxSize`,name:()=>`fields.${e.fieldIndex}.maxSize`,min:0,step:1
Line truncated
|
||||
To target the newly submitted ones you can use @request.body.*.`)})],name:`updateRule`,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.updateRule,oninput:n=>e.collection.updateRule=n})),t.div({className:`col-12`,ariaDescription:r()},app.components.ruleField({label:`Delete rule`,name:`deleteRule`,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.deleteRule,oninput:n=>e.collection.deleteRule=n}))]}),()=>{if(e.collection.type==`auth`)return[t.hr({className:`m-t-base m-b-base`}),t.button({type:`button`,onmount:()=>{n.showAuthRules=e.collection.manageRule!==null||e.collection.authRule!==``},className:()=>`btn secondary sm ${n.showAuthRules?``:`transparent`}`,onclick:()=>{n.showAuthRules=!n.showAuthRules}},t.span({className:`txt`},`Additional auth collection rules`),t.i({ariaHidden:!0,className:()=>n.showAuthRules?`ri-arrow-drop-up-line`:`ri-arrow-drop-down-line`})),app.components.slide(()=>n.showAuthRules,t.div({className:`grid sm m-t-sm`},t.div({className:`col-12`,ariaDescription:r()},app.components.ruleField({label:`Authentication rule`,name:`authRule`,placeholder:``,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.authRule,oninput:n=>e.collection.authRule=n}),t.div({className:`field-help`},t.p(null,`This rule is executed every time `,t.strong(null,`before authentication`),` allowing you to restrict who can authenticate.`),t.p(null,`For example, to allow only verified users you can set it to `,t.code(null,`verified = true`),`.`),t.p(null,`Leave it empty to allow anyone with an account to authenticate.`),t.p(null,`To disable authentication entirely you can change it to "Set superusers only".`))),t.div({className:`col-12`,ariaDescription:r()},app.components.ruleField({label:`Manage rule`,name:`manageRule`,autocomplete:i,disabled:()=>e.originalCollection?.system,value:()=>e.collection.manageRule,oninput:n=>e.collection.manageRule=n}),t.div({className:`field-help`},t.p(null,`This rule is executed in addition to the `,t.strong(null,`create`),` and `,t.strong(null,`update`),` API rules.`),t.p(null,`It enables superuser-like permissions to allow fully managing the auth record(s), eg. changing the password without requiring to enter the old one, directly updating the verified state or email, etc.`)))))]})}var qn=`test_view_query`;function Z(e){let n=`query_`+app.utils.randomString(),r=`SELECT.FROM.WHERE.LEFT JOIN.INNER JOIN.ON.AS.GROUP BY.HAVING.ORDER BY.ASC.DESC.LIMIT.OFFSET.WITH.NOT.IN.AND.OR.EXISTS.LIKE.CAST.REAL.DECIMAL.NUMERIC.INT.TEXT.BOOL`.split(`.`),i=store({testRecords:[],testError:``,isTesting:!1});async function a(n){if(i.isTesting=!0,i.testRecords=[],(app.store.errors?.viewQuery||app.store.errors?.fields)&&(delete app.store.errors.viewQuery,delete app.store.errors.fields),!n){i.testError=``,i.isTesting=!1;return}try{let r=await app.pb.send(`/api/collections/meta/dry-run-view`,{method:`POST`,body:{query:n},requestKey:qn});e.collection?.id?i.testRecords=r.sample.map(n=>(n.collectionId=e.collection?.id,n.collectionName=e.collection?.name,n)):i.testRecords=r.sample,i.testError=``,i.isTesting=!1}catch(e){e.isAbort||(i.testError=e.message||`Invalid query.`,i.isTesting=!1)}}let o,s=[watch(()=>e.collection?.viewQuery,e=>{clearTimeout(o),o=setTimeout(()=>a(e),200)})];return t.div({pbEvent:`collectionViewQueryTabContent`,className:`collection-tab-content collection-view-query-tab-content`,onunmount:()=>{clearTimeout(o),app.pb.cancelRequest(qn),s.forEach(e=>e?.unwatch())}},t.div({className:`grid`},t.div({className:`col-12`},t.div({className:`txt-right txt-sm m-b-10`},t.button({type:`button`,className:`txt-bold link-hint`,"html-popovertarget":n+`caveats_dropdown`},()=>`Query caveats`)),t.div({id:n+`caveats_dropdown`,className:`dropdown sm query-caveats-dropdown`,popover:`auto`},t.ul(null,t.li(null,`Wildcard columns (*) are not supported.`),t.li(null,`The query must have a unique `,t.code(null,`id`),` column.`,t.br(),`If your query doesn't have a suitable one, you can use the universal `,t.code(null,`(ROW_NUMBER() OVER()) as id`),`.`),t.li(null,`Expressions must be aliased with a valid formatted field name, e.g. `,t.code(null,`MAX(balance) as maxBalance`),`.`),t.li(null,`Combined/multi-spaced expressions must be wrapped in parenthesis, e.g. `,t.code(null,`(MAX(balance) + 1) as maxBalance`),`.`),t.li(null,`UNION expressions are supported but the entire query must be wrapped in parenthesis.`))),t.div({className:`field`},t.label({htmlFor:n+`.viewQuery`},t.span({className:`txt`},`Select query`),t.span({hidden:()=>!i.testError,className:`query-state`,ariaDescription:app.attrs.tooltip(`Invalid query`,`left`)},t.i({className:`ri-error-warning-fill txt-danger`,ariaHidden:!0})),t.span({hidden:()=>!!i.testError,className:`query-state`,ariaDescription:app.attrs.tooltip(`Valid query`,`left`)},t.i({className:`ri-checkbox-circle-fill txt-success`,ariaHidden:!0}))),app.components.codeEditor({id:n+`.viewQuery`,name:`viewQuery`,language:`sql`,
Line truncated
|
||||
`+e,`top`,`code`)})},t.div({className:`btns`},t.button({type:`button`,className:()=>`btn expanded-lg ${a.isSaving?`loading`:``}`,disabled:()=>!a.canSave,onclick:()=>s(!0)},t.span({className:`txt`},()=>a.isNew?`Create`:`Save changes`)),t.button({type:`button`,title:`Save options`,className:()=>`btn p-5`,disabled:()=>!a.canSave,"html-popovertarget":i+`save_options`},t.i({className:`ri-arrow-up-s-line`,ariaHidden:!0})),t.div({id:i+`save_options`,className:`dropdown nowrap`,popover:`auto`},t.button({type:`button`,className:`dropdown-item`,onclick:e=>{e.target.closest(`.dropdown`).hidePopover(),s(!1)}},t.span({className:`txt`},`Save and continue`),t.small({className:`txt-hint`},`(Ctrl+S)`)),t.hr(),t.button({type:`button`,className:`dropdown-item`,onclick:e=>{e.target.closest(`.dropdown`).hidePopover(),u()}},t.span({className:`txt`},`Reset form`)))))),r}function Yn(e){let n=JSON.parse(JSON.stringify(app.store.collectionScaffolds[e.type])),r=JSON.parse(JSON.stringify(e.fields))||[],i=r.filter(e=>!e.system);e.fields=n.fields||[];for(let n of r){if(!n.system)continue;let r=e.fields.find(e=>e.name==n.name);r&&Object.assign(r,n)}for(let n of i)e.fields.push(n);if(e.indexes=e.indexes||[],e.indexes.length){let r=n?.indexes||[];indexesLoop:for(let n=e.indexes.length-1;n>=0;n--){let i=app.utils.parseIndex(e.indexes[n]),a=i.indexName.toLowerCase();for(let i of r)if(a==app.utils.parseIndex(i).indexName.toLowerCase()){e.indexes.splice(n,1);continue indexesLoop}for(let r of i.columns)if(!e.fields.find(e=>e.name.toLowerCase()==r.name.toLowerCase())){e.indexes.splice(n,1);continue indexesLoop}}}app.utils.mergeUnique(e.indexes,n.indexes),e.indexes=e.indexes?.map(n=>app.utils.replaceIndexFields(n,{tableName:e.name||``}))}function Xn(e,n){let r=`truncate_`+app.utils.randomString(),i=store({isSubmitting:!1,nameConfirm:``});async function a(){if(i.isSubmitting||!e.originalCollection?.name||e.originalCollection.name!=i.nameConfirm)return!1;i.isSubmitting=!0;try{return await app.pb.collections.truncate(e.originalCollection.name),n.ontruncate?.(JSON.parse(JSON.stringify(e.originalCollection))),app.toasts.success(`Successfully truncated collection "${e.originalCollection.name}".`),i.isSubmitting=!1,!0}catch(e){i.isSubmitting=!1,app.checkApiError(e)}return!1}return t.button({type:`button`,className:`dropdown-item txt-danger`,disabled:()=>i.isSubmitting,onclick:n=>{n.target.closest(`.dropdown`).hidePopover(),app.modals.confirm(t.div(null,t.h6({className:`block txt-center`},`Do you really want to delete all records of the collection?`),t.div({className:`confirm-collection-label txt-bold m-t-sm m-b-sm`},`Type the collection name `,t.div({className:`label`},()=>e.originalCollection.name,app.components.copyButton(()=>e.originalCollection?.name)),` to confirm:`),t.div({className:`field`},t.label({htmlFor:r+`.confirm_name`},`Collection name`),t.input({id:r+`.confirm_name`,type:`text`,required:!0,pattern:()=>RegExp.escape(e.originalCollection.name),value:()=>i.nameConfirm,oninput:e=>i.nameConfirm=e.target.value}))),async()=>{if(document.getElementById(r+`.confirm_name`)?.reportValidity(),!await a())return!1;app.modals.close(n.target.closest(`.modal`))},()=>{i.nameConfirm=``})}},t.i({className:`ri-eraser-line`,ariaHidden:!0}),t.span({className:`txt`},`Truncate`))}function Zn(e,n){let r=`delete_`+app.utils.randomString(),i=store({isSubmitting:!1,nameConfirm:``});async function a(){if(i.isSubmitting||!e.originalCollection?.name||e.originalCollection.name!=i.nameConfirm)return!1;i.isSubmitting=!0;try{return await app.pb.collections.delete(e.originalCollection.name),n?.ondelete?.(JSON.parse(JSON.stringify(e.originalCollection))),app.utils.removeByKey(app.store.collections,`id`,e.originalCollection.id),app.toasts.success(`Successfully deleted collection "${e.originalCollection.name}".`),i.isSubmitting=!1,!0}catch(e){i.isSubmitting=!1,app.checkApiError(e)}return!1}return t.button({type:`button`,className:`dropdown-item txt-danger`,disabled:()=>i.isSubmitting,onclick:n=>{n.target.closest(`.dropdown`).hidePopover();let o=n.target.closest(`.modal`);app.modals.confirm(t.div({className:`block`},t.h6({className:`block txt-center`},()=>e.originalCollection.type==`view`?`Do you really want to delete the selected collection?`:`Do you really want to delete the selected collection and all its records`),t.div({className:`confirm-collection-label txt-bold m-t-sm m-b-sm`},`Type the collection name `,t.div({className:`label`},()=>e.originalCollection.name,app.components.copyButton(()=>e.originalCollection?.name)),` to confirm:`),t.div({className:`field`},t.label({htmlFor:r+`.confirm_name`},`Collection name`),t.input({id:r+`.confirm_name`,type:`text`,required:!0,pattern:()=>RegExp.escape(e.originalCollection.name),value:()=>i.nameConfirm,oninput:e=>i.nameConfirm=e.target.value}))),async()=>{if(document.getElementById(r+`.confirm_name`)?.reportValidity(),!await a())return!1;app.modals.close(o)},()=>{i.nameConfirm=``})}},t.i({className:`ri-delete-bin-7-line`,ariaHidden
Line truncated
|
||||
...
|
||||
-----END PRIVATE KEY-----`,value:()=>a.privateKey||``,oninput:e=>a.privateKey=e.target.value})),t.div({className:`field-help`},`The key is not stored on the server and it is used only for generating the signed JWT.`)))),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,onclick:()=>app.modals.close(n)},t.span({className:`txt`},`Close`)),t.button({"html-form":r+`_form`,type:`submit`,className:`btn expanded`},t.i({className:`ri-key-line`,ariaHidden:!0}),t.span({className:`txt`},`Generate secret`)))),n}function lr(){return t.div({className:`inline-flex gap-5`},t.span(null,`Default log levels:`),()=>{let e=[];for(let n in app.utils.logLevels)e.push(t.code(null,`${n}:${app.utils.logLevels[n].label}`));return e})}window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openLogsSettings=function(e={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null,onsave:null}){let n=ur(e);n&&(document.body.appendChild(n),app.modals.open(n))};function ur(e){let n,r=store({isLoading:!1,isSaving:!1,formSettings:{},initFormSettingsHash:``,get hasChanges(){return r.initFormSettingsHash!=JSON.stringify(r.formSettings)}});function i(e={}){r.formSettings={logs:e?.logs||{}},r.initFormSettingsHash=JSON.stringify(r.formSettings)}async function a(){r.isLoading=!0;try{i(await app.pb.settings.getAll({requestKey:`logsSettings`})),r.isLoading=!1}catch(e){e.isAbort||(r.isLoading=!1,app.checkApiError(e))}}async function o(){if(r.hasChanges){r.isSaving=!0;try{let a=await app.pb.settings.update(app.utils.filterRedactedProps(r.formSettings));e.onsave?.(a),i(a),app.toasts.success(`Successfully saved logs settings.`),r.isSaving=!1,app.modals.close(n)}catch(e){e.isAbort||(r.isSaving=!1,app.checkApiError(e))}}}return n=t.div({pbEvent:`logsSettingsModal`,className:`modal popup sm logs-settings-modal`,onbeforeopen:n=>(a(),e.onbeforeopen?.(n)),onafteropen:n=>{e.onafteropen?.(n)},onbeforeclose:n=>e.onbeforeclose?.(n),onafterclose:n=>{e.onafterclose?.(n),n?.remove()}},t.header({className:`modal-header`},t.h5({className:`m-auto`},`Logs settings`)),()=>r.isLoading?t.div({className:`modal-content flex`,style:`min-height: 200px`},t.span({className:`loader m-auto`})):[t.form({pbEvent:`logsSettingsForm`,id:`logsSettingsForm`,className:`modal-content logs-settings-form`,onsubmit:e=>{e.preventDefault(),o()}},t.div({className:`grid`},t.div({className:`col-lg-12`},t.field({className:`field`},t.label({htmlFor:`logs.maxDays`},`Max days retention`),t.input({type:`number`,id:`logs.maxDays`,name:`logs.maxDays`,min:0,required:!0,value:()=>r.formSettings.logs.maxDays,oninput:e=>r.formSettings.logs.maxDays=e.target.value<<0})),t.div({className:`field-help`},`Set to `,t.code(null,0),` to disable logs persistence.`)),t.div({className:`col-lg-12`},t.field({className:`field`},t.label({htmlFor:`logs.minLevel`},`Min log level`),t.input({type:`number`,id:`logs.minLevel`,name:`logs.minLevel`,min:-100,max:100,required:!0,value:()=>r.formSettings.logs.minLevel,oninput:e=>r.formSettings.logs.minLevel=e.target.value<<0})),t.div({className:`field-help`},t.div(null,`Logs with level below the minimum will be ignored.`),lr())),t.div({className:`col-lg-12`},t.field({className:`field`},t.input({type:`checkbox`,id:`logs.logIP`,name:`logs.logIP`,className:`switch`,checked:()=>r.formSettings.logs.logIP,onchange:e=>r.formSettings.logs.logIP=e.target.checked}),t.label({htmlFor:`logs.logIP`},`Enable IP logging`))),t.div({className:`col-lg-12`},t.field({className:`field`},t.input({type:`checkbox`,id:`logs.logAuthId`,name:`logs.logAuthId`,className:`switch`,checked:()=>r.formSettings.logs.logAuthId,onchange:e=>r.formSettings.logs.logAuthId=e.target.checked}),t.label({htmlFor:`logs.logAuthId`},`Enable Auth Id logging`))))),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,onclick:()=>app.modals.close(n),disabled:()=>r.isSaving},t.span({className:`txt`},`Close`)),t.button({type:`submit`,"html-form":`logsSettingsForm`,className:()=>`btn ${r.isSaving?`loading`:``}`,disabled:()=>!r.hasChanges||r.isSaving},t.span({className:`txt`},`Save changes`)))]),n}function dr(e){return t.div({className:()=>`label log-level-label level-${e.level}`},t.span({className:`txt`},()=>`${app.utils.logLevels[e.level]?.label||`UNKN`} (${e.level})`))}window.app=window.app||{},window.app.modals=window.app.modals||{},window.app.modals.openLogPreview=function(e,n={onbeforeopen:null,onafteropen:null,onbeforeclose:null,onafterclose:null}){let r=hr(e,n);r&&(document.body.appendChild(r),app.modals.open(r))};var fr=[`execTime`,`type`,`auth`,`authId`,`status`,`method`,`url`,`referer`,`remoteIP`,`userIP`,`userAgent`,`error`,`details`];function pr(e){app.utils.downloadJSON(e,`log_`+e.created.replaceAll(/[-:\. ]/gi,``)+`.json`)}function mr(e){app.utils.copyToClipboard(JSON.stringify(e,null,2)),app.toasts.success(`Log copied to clipboard!`)}function hr(e,n){let r,i=store({isLoading:!1,log:null,get isRequ
Line truncated
|
||||
{MMM} {DD}`,null,`
|
||||
{MMM} {DD}`,null,null,null,1],[60,`{h}:{mm}{aa}`,`
|
||||
{MMM} {DD}`,null,`
|
||||
{MMM} {DD}`,null,null,null,1]],grid:{show:!0,stroke:o,width:1},ticks:{show:!0,stroke:o,width:1,size:5}},{show:!0,stroke:a,grid:{show:!0,stroke:o,width:1},ticks:{show:!0,stroke:o,width:1,size:5}}],plugins:[Rr(),Ir(r),Lr(r)]};e._uplot?.destroy(),e._uplot=new uPlot(l,n,e)}function Ir(e){let n;return{hooks:{init:r=>{r.over.ondblclick=n=>{e.zoom={}},n=watch(()=>{!e.zoom?.min||!e.zoom?.max?Pr(r):r.setScale(`x`,{min:e.zoom.min,max:e.zoom.max})})},destroy:e=>{n?.unwatch()},setSelect:n=>{n.select.width>0&&(e.zoom={min:n.posToVal(n.select.left,`x`),max:n.posToVal(n.select.left+n.select.width,`x`)})}}}}function Lr(e){let n;return{hooks:{init:r=>{let i=r.root.querySelectorAll(`.u-axis`);if(!i.length){console.warn(`xPanPlugin requires x axis to be defined`);return}i[0].addEventListener(`mousedown`,i=>{if(!e.zoom?.min)return;let a=i.clientX,{min:o,max:s}=r.scales.x,c=r.bbox.width,l=(s-o)/(c/uPlot.pxRatio),u=i=>{let c=(a-i.clientX)*l;r.setScale(`x`,{min:o+c,max:s+c}),clearTimeout(n),n=setTimeout(()=>{r?.scales?.x&&(e.zoom={min:r.scales.x.min,max:r.scales.x.max})},100)},d=e=>{document.removeEventListener(`mousemove`,u),document.removeEventListener(`mouseup`,d)};document.addEventListener(`mousemove`,u),document.addEventListener(`mouseup`,d)})},destroy:e=>{n&&clearTimeout(n)}}}}function Rr(e){let n;return{hooks:{init:e=>{let r=e.over;n=store({date:``,total:0,left:0,top:0,show:!1});let i=t.div({className:()=>`chart-tooltip ${n.show?``:`hidden`}`,onmount(e){e._positionWatcher?.unwatch(),e._positionWatcher=watch(()=>[n.left,n.top],()=>{if(!e)return;let i=e.getBoundingClientRect(),a=n.left;a<0?a=0:a+i.width>r.clientWidth&&(a=r.clientWidth-i.width),e.style.left=a+`px`;let o=n.top-i.height-5;o<0&&(o=n.top+5,o+i.high>r.clientHeight&&(o=r.clientHeight-i.height)),e.style.top=o+`px`})},onunmount(e){e._positionWatcher?.unwatch()}},t.div({className:`content-primary`},()=>`${n.total} ${n.total==1?`request`:`requests`}`),t.div({className:`content-secondary`},()=>n.date));r.appendChild(i),r.addEventListener(`mouseleave`,()=>{n&&(n.show=!1)})},destroy:()=>{n.show=!1},setCursor:e=>{if(!n)return;let r=e.data[0][e.cursor.idx]||0,i=e.data[1][e.cursor.idx]||0;if(r==0||i==0){n.show=!1;return}n.show=!0,n.total=i;let a=new Date(r*1e3),o=new Date(r*1e3+36e5),s=a.toLocaleString(`default`,{month:`short`}),c=a.getDate().toString().padStart(2,`0`);n.date=`${s} ${c} ${zr(a)}-${zr(o)}`,n.left=Math.round(e.valToPos(r,`x`)),n.top=Math.round(e.valToPos(i,`y`))}}}}function zr(e){let n=e.getHours(),r=n>=12?`pm`:`am`;return n=n%12||12,n+r}var Br=50;function Vr(e){let n=store({logs:[],lastLoadCount:0,lastPage:1,bulkSelected:{},get canLoadMore(){return n.lastLoadCount>=Br},get totalSelected(){return Object.keys(n.bulkSelected).length},get areAllSelected(){return n.logs.length&&n.logs.length==n.totalSelected}}),r;async function i(i=!1){e.isListLoading=!0;try{let a;i?(a=1,r=new Date().toISOString().replace(`T`,` `)):a=n.lastPage+1;let o=(e.presets||[]).concat(app.utils.normalizeSearchFilter(e.filter,[`level`,`message`,`data`]));if(e.zoom?.min&&e.zoom?.max){let n=new Date(e.zoom.min*1e3);n.setSeconds(0),n.setMilliseconds(0);let r=app.utils.toRFC3339Datetime(n),i=new Date(e.zoom.max*1e3);i.setSeconds(59),i.setMilliseconds(999);let a=app.utils.toRFC3339Datetime(i);o.push(`created >= "${r}" && created <= "${a}"`)}else o.push(`created <= "${r}"`);let s=await app.pb.logs.getList(a,Br,{skipTotal:1,sort:`-@rowid`,requestKey:`logs_list`,filter:o.filter(Boolean).map(e=>`(`+e+`)`).join(`&&`)});s.page==1&&(n.logs=[],n.bulkSelected={}),n.lastPage=s.page,n.lastLoadCount=s.items.length;for(let e=0;e<s.items.length;e++)app.utils.pushOrReplaceObject(n.logs,s.items[e]),e>1&&e%20==0&&await new Promise(e=>setTimeout(e,20));e.isListLoading=!1,e.isFirstLoadReady||=!0}catch(n){n.isAbort||(e.isListLoading=!1,app.checkApiError(n))}}function a(e=!0){let r={};if(e)for(let e of n.logs)r[e.id]=e;n.bulkSelected=r}function o(e){let n=[];if(!e.data)return n;if(e.data.type==`request`){for(let r of[`status`,`execTime`,`auth`,`authId`,`userIP`])e.data[r]!==void 0&&n.push({key:r});e.data.referer&&!e.data.referer.includes(window.location.host)&&n.push({key:`referer`})}else{let r=Object.keys(e.data);for(let e of r)e!=`error`&&e!=`details`&&n.length<6&&n.push({key:e})}return e.data.error&&n.push({key:`error`,label:`danger`}),e.data.details&&n.push({key:`details`,label:`warning`}),n}let s=/[-:\. ]/gi;function c(){let e=Object.values(n.bulkSelected).sort((e,n)=>e.created<n.created?1:e.created>n.created?-1:0);if(!e.length)return;if(e.length==1)return app.utils.downloadJSON(e[0],`log_`+e[0].created.replaceAll(s,``)+`.json`);let r=e[0].created.replaceAll(s,``),i=e[e.length-1].created.replaceAll(s,``);return app.utils.downloadJSON(e,`${e.length}_logs_${i}_to_${r}.json`)}let l=[];return t.div({pbEvent:`logsList`,className:`page-table-wrapper`,onmount(n){l.push(watch(()=>[e.filter,e.presets?.length],()=>{e.zoom={}}),watch(()=>[e.reset,e.filter,e.presets?.length,e.zoom?.min,e.zoom?.max],()=>{i(!0),n&&(n.scrollT
Line truncated
|
||||
It is recommend to list it as trusted.`,`right`)});if(i.isEnabled&&i.possibleProxyHeader&&!e.formSettings.trustedProxy.headers.includes(i.possibleProxyHeader))return t.i({className:`ri-alert-line txt-hint`,ariaDescription:app.attrs.tooltip(`The configured proxy header doesn't match with the detected one.`,`right`)})},t.div({className:`flex-fill`}),()=>i.isEnabled?t.span({className:`label success`},`Enabled`):t.span({className:`label`},`Disabled`),()=>{if(!app.utils.isEmpty(app.store.errors?.trustedProxy))return t.i({className:`ri-error-warning-fill txt-danger`,ariaDescription:app.attrs.tooltip(`Has errors`,`left`)})}),t.p({className:`m-t-0`},`Below you should see your real IP. If not - configure the correct proxy header for your environment.`),t.div({className:`alert info m-b-sm`},t.div({className:`flex gap-5`},t.span(null,`Resolved user IP:`),t.strong(null,()=>i.isLoading?`...`:i.realIP||`N/A`)),t.div({className:`flex gap-5`},t.span(null,`Detected proxy header:`),t.strong(null,()=>i.isLoading?`...`:i.possibleProxyHeader||`N/A`))),t.div({className:`content m-b-sm`},t.p(null,`
|
||||
When PocketBase is deployed on platforms like Fly or it is accessible through proxies such as
|
||||
NGINX, requests from different users will originate from the same IP address (the IP of the proxy
|
||||
connecting to your PocketBase app).
|
||||
`),t.p(null,`
|
||||
In this case to retrieve the actual user IP (used for rate limiting, logging, etc.) you need to
|
||||
properly configure your proxy and list below the trusted headers that PocketBase could use to
|
||||
extract the user IP.
|
||||
`),t.p({className:`txt-bold`},`When using such proxy, to avoid spoofing it is recommended to:`),t.ul({className:`txt-bold`},t.li(null,`use headers that are controlled only by the proxy and cannot be manually set by the users`),t.li(null,`make sure that the PocketBase server can be accessed ONLY through the proxy`)),t.p(null,`You can clear the headers field if PocketBase is not deployed behind a proxy.`)),t.div({className:`grid sm`},t.div({className:`col-lg-9`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`trustedProxy.headers`},`Trusted IP proxy headers`),t.input({type:`text`,id:`trustedProxy.headers`,name:`trustedProxy.headers`,placeholder:`Leave empty to disable`,value:()=>app.utils.joinNonEmpty(e.formSettings.trustedProxy.headers),oninput:n=>{let r=app.utils.splitNonEmpty(n.target.value,`,`),i=app.utils.joinNonEmpty(r);app.utils.joinNonEmpty(e.formSettings.trustedProxy.headers)!=i&&(e.formSettings.trustedProxy.headers=r)}})),t.div({className:`field addon`},t.button({type:`button`,className:()=>`btn sm secondary transparent ${app.utils.isEmpty(e.formSettings.trustedProxy.headers)?`hidden`:``}`,onclick:()=>{e.formSettings.trustedProxy.headers=[]}},t.span({className:`txt`},`Clear`)))),t.div({className:`field-help`},`Comma separated list of headers such as: `,t.div({className:`inline-flex gap-5`},()=>i.suggestedProxyHeaders.map(n=>t.div({role:`button`,className:`label sm link-primary`,onclick:()=>{e.formSettings.trustedProxy.headers=[n]},textContent:n}))))),t.div({className:`col-lg-3`},t.div({className:`field`},t.label({htmlFor:`trustedProxy.useLeftmostIP`},t.span({className:`txt`},`IP priority`),t.i({className:`ri-information-line tooltip-right`,ariaDescription:app.attrs.tooltip(`This is in case the proxy returns more than 1 IP as header value. The rightmost IP is usually considered to be the more trustworthy but this could vary depending on the proxy.`)})),app.components.select({id:`trustedProxy.useLeftmostIP`,name:`trustedProxy.useLeftmostIP`,options:r,required:!0,value:()=>e.formSettings.trustedProxy.useLeftmostIP||!1,onchange:n=>{e.formSettings.trustedProxy.useLeftmostIP=n?.[0]?.value}})))))}function Zr(){app.store.title=`Application settings`;let e=store({isLoading:!1,isSaving:!1,formSettings:null,originalFormSettings:null,get originalFormSettingsHash(){return JSON.stringify(e.originalFormSettings)},get formSettingsHash(){return JSON.stringify(e.formSettings)},get hasChanges(){return e.originalFormSettingsHash!=e.formSettingsHash}});n();async function n(){e.isLoading=!0;try{o(await app.pb.settings.getAll()),e.isLoading=!1}catch(e){e.isAbort||app.checkApiError(e)}}function r(){return JSON.stringify(e.formSettings?.superuserIPs)!=JSON.stringify(e.originalFormSettings?.superuserIPs)}async function i(){let n=app.utils.toArray(e.originalFormSettings?.superuserIPs),r=app.utils.toArray(e.formSettings?.superuserIPs);return!r.length||JSON.stringify(n)==JSON.stringify(r)?a():app.modals.confirm(t.div({className:`txt-center`},t.h6(null,`The ONLY allowed superuser IPs will change to: `,t.br(),t.strong(null,r.join(`, `))),t.p(null,`Please make sure that your IP is in the list or you'll be locked.`),t.p({className:`txt-hint`},`In case of lockout, you can reset the setting with the `,t.a({href:`https://pocketbase.io/docs/going-to-production/#limit-superusers-to-specific-ipssubnets`,target:`_blank`,rel:`noopener noreferrer`,className:`link-primary txt-bold txt-sm`},t.code(null,`superuser ips`,t.i({ariaHidden:!0,className:`ri-arrow-right-up-line txt-sm`}))),` console command.`)),()=>a(),null,{yesButton:`Yes, save changes`})}async function a(){if(!(e.isSaving||!e.hasChanges)){e.isSaving=!0,e.formSettings.rateLimits.rules=qr(e.formSettings.rateLimits.rules);try{let n=app.utils.filterRedactedProps(e.formSettings),i=await app.pb.settings.update(n);if(r())try{await app.pb.collection(`_superusers`).authRefresh()}catch{app.pb.authStore.clear()}o(i),app.toasts.success(`Successfully saved application settings.`)}catch(e){app.checkApiError(e)}e.isSaving=!1}}function o(n={}){if(app.store.settings=JSON.parse(JSON.stringify(n)),!n.meta?.accentColor){let e=window.getComputedStyle(document.documentElement)?.getPropertyValue(`--accentColor`);e?.startsWith(`#`)&&(n.meta=n.meta||{},n.meta.accentColor=e.toLowerCase()||``)}e.originalFormSettings={superuserIPs:n.superuserIPs||[],meta:n.meta||{},batch:n.batch||{},trustedProxy:n.trustedProxy||{headers:[]},rateLimits:n.rateLimits||{excludedIPs:[],rules:[]}},qr(e.originalFormSettings.rateLimits.rules),e.formSettings=JSON.parse(JSON.stringify(e.originalFormSettings))}function s(){e.formSettings=JSON.parse(e.originalFormSettingsHash)}return t.div({pbEvent:`pageApplicationSettings`,className:`page page-application-settings`},$(),t.div({className:`page-content full-height`},t.header({className:`page-header`},t.nav({className:`breadcrumbs`},t.div({className:`breadcrumb-item`},`Settings`),t.div({className:`breadcrumb-item`},`Application`))),t.div({className:`wrap
Line truncated
|
||||
Records update will also require an extra unlock step before save.`)})))),t.div({className:`col-lg-12`},t.hr()),t.div({className:`col-lg-12`},t.div({className:`flex`},t.div({className:`m-r-auto`}),t.button({type:`button`,className:`btn transparent secondary`,disabled:()=>e.isSaving,hidden:()=>!e.hasChanges,onclick:s},t.span({className:`txt`},`Cancel`)),t.button({className:()=>`btn expanded-lg ${e.isSaving?`loading`:``}`,disabled:()=>!e.hasChanges||e.isSaving},t.span({className:`txt`},`Save changes`)))))),t.footer({className:`page-footer`},app.components.credits())))}function Qr(e){let n=`accent_`+app.utils.randomString(),r=store({isTooLight:!1}),i,a;function o(e){clearTimeout(a),document.documentElement.style.setProperty(`--animationSpeed`,`0`),e?document.documentElement.style.setProperty(`--accentColor`,e.toLowerCase()):document.documentElement.style.removeProperty(`--accentColor`),a=setTimeout(()=>{document.documentElement.style.removeProperty(`--animationSpeed`)},100)}let s=[watch(()=>e.formSettings?.meta?.accentColor,e=>{clearTimeout(i),i=setTimeout(()=>{o(e)},100)})];return t.div({className:`field`,ariaDescription:app.attrs.tooltip(()=>r.isTooLight?`Invalid - color is too light`:``),onunmount:()=>{clearTimeout(i),o(e.formSettings.meta.accentColor),s.forEach(e=>e?.unwatch())}},t.label({htmlFor:n},t.span({className:`txt`},`Accent`),t.i({hidden:()=>!r.isTooLight,className:`txt-warning ri-alert-line`})),app.components.colorPicker({id:n,name:`meta.accentColor`,predefinedColors:()=>app.store.predefinedAccentColors,value:()=>e.formSettings.meta.accentColor,onchange:n=>{if(r.isTooLight=!1,!app.utils.isDarkEnoughForWhiteText(n)){r.isTooLight=!0;return}e.formSettings.meta.accentColor=n}}))}function $r(e={}){let n=store({onsave:null}),r=app.utils.extendStore(n,e),i=[{cron:`0 0 * * *`,label:`Every day at 00:00h`},{cron:`0 0 * * 0`,label:`Every sunday at 00:00h`},{cron:`0 0 * * 1,3`,label:`Every Mon and Wed at 00:00h`},{cron:`0 0 1 * *`,label:`Every first day of the month at 00:00h`}],a=store({showForm:!1,isLoading:!1,isSaving:!1,formSettings:null,initSerialized:`null`,enableAutoBackups:!1,get hasChanges(){return a.initSerialized!=JSON.stringify(a.formSettings)}});async function o(){a.isLoading=!0;try{c(await app.pb.settings.getAll()),a.isLoading=!1}catch(e){e.isAbort||app.checkApiError(e)}}async function s(){if(!(a.isSaving||!a.hasChanges)){a.isSaving=!0;try{let e=app.utils.filterRedactedProps(a.formSettings),r=await app.pb.settings.update(e);n.onsave?.(r),c(r),app.toasts.success(`Successfully saved backups settings.`)}catch(e){app.checkApiError(e)}a.isSaving=!1}}function c(e={}){app.store.settings=JSON.parse(JSON.stringify(e)),a.formSettings={backups:e?.backups||{}},a.enableAutoBackups=!!a.formSettings.backups.cron,a.initSerialized=JSON.stringify(a.formSettings)}function l(){a.formSettings=JSON.parse(a.initSerialized),a.enableAutoBackups=!!a.formSettings.backups.cron}return r.push(watch(()=>{!a.enableAutoBackups&&a.formSettings?.backups?.cron&&(a.formSettings.backups.cron=``)})),t.div({className:`block backups-settings-form-wrapper`,onmount:()=>{o()},onunmount:()=>{r.forEach(e=>e?.unwatch())}},t.button({type:`button`,className:()=>`btn secondary ${a.isLoading?`loading`:``}`,disabled:()=>a.isLoading||a.hasChanges,onclick:()=>a.showForm=!a.showForm},t.span({className:`txt`},`Backup options`),t.i({className:()=>a.showForm?`ri-arrow-up-s-line`:`ri-arrow-down-s-line`,ariaHidden:!0})),app.components.slide(()=>a.showForm,t.form({pbEvent:`backupsSettingsForm`,className:`grid backups-settings-form m-t-base`,inert:()=>a.isSaving,onsubmit:e=>{e.preventDefault(),s()}},()=>a.isLoading?t.div({className:`col-lg-12 txt-center`},t.span({className:`loader lg`})):[t.div({className:`col-lg-12`},t.div({className:`field`},t.input({id:`enableAutoBackupsToggle`,type:`checkbox`,className:`switch`,checked:()=>a.enableAutoBackups,onchange:e=>{a.enableAutoBackups=e.target.checked,a.formSettings.backups.cron||(a.formSettings.backups.cron=i[0].cron)}}),t.label({htmlFor:`enableAutoBackupsToggle`},`Enable auto backups`)),app.components.slide(()=>a.enableAutoBackups,t.div({className:`grid m-t-base m-b-base`},t.div({className:`col-lg-6`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`backups.cron`},`Cron expression`),t.input({id:`backups.cron`,name:`backups.cron`,className:`txt-code`,type:`text`,placeholder:`e.g. 0 0 * * *`,required:()=>a.enableAutoBackups,value:()=>a.formSettings.backups.cron,oninput:e=>a.formSettings.backups.cron=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,className:`btn outline sm`,"html-popovertarget":`cron-presets-dropdown`},t.span({className:`txt`},`Presets`),t.i({className:`ri-arrow-drop-down-line`,ariaHidden:!0})),t.div({id:`cron-presets-dropdown`,className:`dropdown sm txt-nowrap`,popover:`auto`},()=>i.map(e=>t.button({type:`button`,className:()=>`dropdown-item ${a.formSettings.backups.cron==e.cron?`active`:``}`,textContent:e.label,onclick:n=>{a.formSettings.backups.
Line truncated
|
||||
@annually
|
||||
@monthly
|
||||
@weekly
|
||||
@daily
|
||||
@midnight
|
||||
@hourly`)},`macros`),`.`,t.br(),`By default the timezone is in UTC.`)),t.div({className:`col-lg-6`},t.div({className:`field`},t.label({htmlFor:`backups.cronMaxKeep`},`Max @auto backups to keep`),t.input({id:`backups.cronMaxKeep`,name:`backups.cronMaxKeep`,type:`number`,required:()=>a.enableAutoBackups,min:1,value:()=>a.formSettings.backups.cronMaxKeep,oninput:e=>{a.formSettings.backups.cronMaxKeep=parseInt(e.target.value,10)}})))))),t.div({className:`col-lg-12`},app.components.s3ConfigFields({toggleLabel:`Store backups in S3 storage`,testFilesystem:`backups`,config:()=>a.formSettings.backups.s3})),t.div({className:`col-lg-12`},t.hr()),t.div({className:`col-lg-12`},t.div({className:`flex`},t.div({className:`m-r-auto`}),t.button({hidden:()=>!a.hasChanges,type:`button`,className:`btn transparent secondary`,onclick:l},t.span({className:`txt`},`Cancel`)),t.button({className:()=>`btn expanded-lg ${a.isSaving?`loading`:``}`,disabled:()=>!a.hasChanges||a.isSaving},t.span({className:`txt`},`Save changes`))))])))}function ei(e={oncreated:null}){let n=ti(e);n&&(document.body.appendChild(n),app.modals.open(n))}function ti(e){let n,r=`backup_create_`+app.utils.randomString(),i=store({name:``,isSubmitting:!1}),a;async function o(){if(!i.isSubmitting){i.isSubmitting=!0,clearTimeout(a),a=setTimeout(()=>{app.modals.close(n)},1500);try{await app.pb.backups.create(i.name,{requestKey:r}),i.isSubmitting=!1,e.oncreated&&e.oncreated(i.name),app.toasts.success(`Successfully generated new backup.`),app.modals.close(n)}catch(e){e.isAbort||(clearTimeout(a),i.isSubmitting=!1,app.checkApiError(e))}}}return n=t.div({pbEvent:`backupCreateModal`,className:`modal popup backup-create-modal`,onbeforeclose:()=>{i.isSubmitting&&app.toasts.info(`The backup was started but may take a while to complete. You can come back later.`)},onafterclose:e=>{clearTimeout(a),e?.remove()}},t.header({className:`modal-header`},t.h5({className:`m-auto txt-center`},`Initialize new backup`)),t.form({id:r,className:`modal-content backup-restore-form`,autocomplete:`off`,onsubmit:e=>{e.preventDefault(),o()}},t.div({className:`grid`},t.div({className:`col-lg-12`},t.div({className:`alert warning`},t.div({className:`content`},t.p(null,`Please note that during the backup other concurrent write requests may fail since the database will be temporary "locked" (this usually happens only during the ZIP generation).`),t.p({className:`txt-bold`},`If you are using S3 storage for the collections file upload, you'll have to backup them separately since they are not locally stored and they will not be included in the generated backup!`)))),t.div({className:`col-lg-12`},t.div({className:`field`},t.label({htmlFor:r+`_name`},`Backup name`),t.input({id:r+`_name`,name:`name`,type:`text`,pattern:`^[a-z0-9_-]+.zip$`,placeholder:`Leave empty to autogenerate`,value:()=>i.name,oninput:e=>i.name=e.target.value})),t.div({className:`field-help`},`Must be in the format [a-z0-9_-].zip`)))),t.footer({className:`modal-footer`},t.button({type:`button`,className:`btn transparent m-r-auto`,disabled:()=>i.isSubmitting,onclick:()=>app.modals.close(n)},t.span({className:`txt`},`Cancel`)),t.button({"html-form":r,type:`submit`,className:()=>`btn ${i.isSubmitting?`loading`:``}`,disabled:()=>i.isSubmitting},t.span({className:`txt`},`Start backup`)))),n}function ni(e){let n=ri(e);document.body.appendChild(n),app.modals.open(n)}function ri(e){let n=`backup_restore_`+app.utils.randomString(),r=store({key:e,keyConfirm:``,isSubmitting:!1,get canSubmit(){return r.key&&r.key==r.keyConfirm}}),i;async function a(){if(!(r.isSubmitting||!r.canSubmit)){clearTimeout(i),r.isSubmitting=!0;try{await app.pb.backups.restore(r.keyConfirm),i=setTimeout(()=>{window.location.reload(),r.isSubmitting=!1},2e3)}catch(e){clearTimeout(i),e?.isAbort||(r.isSubmitting=!1,app.checkApiError(e))}}}return t.div({pbEvent:`backupRestoreModal`,className:`modal popup backup-restore-modal`,onbeforeclose:()=>!r.isSubmitting,onafterclose:e=>{e?.remove()},onunmount:()=>{clearTimeout(i)}},t.header({className:`modal-header`},t.h5({className:`m-auto txt-center`},`Restore `,t.strong(null,()=>r.key))),t.form({id:n,className:`modal-content backup-restore-form`,autocomplete:`off`,onsubmit:e=>{e.preventDefault(),a()}},t.div({className:`grid`},t.div({className:`col-lg-12`},t.div({className:`alert danger`},t.div({className:`content`},t.p({className:`txt-bold`},`Please proceed with extreme caution and use it only with trusted backups!`),t.p(null,`Backup restore currently works only on UNIX based systems.`),t.p(null,`The restore operation will attempt to replace your existing `,t.code(null,`pb_data`),` with the one from the backup and will restart the application process.`),t.p(null,`This means that on success all of your data (including app settings, users, superusers, etc.) will be replaced with the ones from the backup.`),t.p(null,`The operation will be reverted if the backup is invalid (ex. missing `,t.code(null,`data.db`),` file).`),t.p(null,`Below is an oLine truncated
|
||||
@@ -0,0 +1 @@
|
||||
import{n as e,t as n}from"./index-DoRNK5jP.js";function r(r){let i=r.params?.token||``,a=e(i);if(!a.newEmail||!a.collectionId){app.toasts.error(`Invalid or expired email change token.`),window.location.hash=`#/`;return}app.store.title=`Confirm email change`;let o=store({password:``,isSubmitting:!1,isSuccess:!1,showPassword:!1});async function s(){if(o.isSubmitting)return;o.isSubmitting=!0;let e=new n(app.pb.baseURL);try{await e.collection(a.collectionId).confirmEmailChange(i,o.password),o.isSuccess=!0}catch(e){app.checkApiError(e),o.isSuccess=!1}o.isSubmitting=!1}return t.div({pbEvent:`pageConfirmEmailChange`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),()=>o.isSuccess?t.div({pbEvent:`confirmEmailChangeAlert`,className:`alert success txt-center`},t.p(null,`The email was successfully changed.`),t.p(null,`You can go back and sign in with your new email address.`)):t.form({pbEvent:`confirmEmailChangeForm`,className:`grid confirm-email-change-form`,onsubmit:e=>{e.preventDefault(),s()}},t.div({className:`col-12`},t.div({className:`content txt-center m-b-sm`},`Type your password to confirm changing your email address to `,t.strong(null,a.newEmail),`:`),t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`password_confirm`},`Password`),t.input({id:`password_confirm`,name:`password`,required:!0,autofocus:!0,type:()=>o.showPassword?`text`:`password`,value:()=>o.password,oninput:e=>o.password=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>o.showPassword?`Hide password`:`Show password`),onclick:()=>o.showPassword=!o.showPassword},t.i({className:()=>o.showPassword?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.button({className:()=>`btn lg block ${o.isSubmitting?`loading`:``}`,disabled:()=>o.isSubmitting},t.span({className:`txt`},`Confirm new email`)))))}export{r as pageConfirmEmailChange};
|
||||
@@ -1 +0,0 @@
|
||||
import{i as e,t as n}from"./pocketbase.es-B_4DUNUU.js";function r(r){let i=r.params?.token||``,a=e(i);if(!a.newEmail||!a.collectionId){app.toasts.error(`Invalid or expired email change token.`),window.location.hash=`#/`;return}app.store.title=`Confirm email change`;let o=store({password:``,isSubmitting:!1,isSuccess:!1,showPassword:!1});async function s(){if(o.isSubmitting)return;o.isSubmitting=!0;let e=new n(app.pb.baseURL);try{await e.collection(a.collectionId).confirmEmailChange(i,o.password),o.isSuccess=!0}catch(e){app.checkApiError(e),o.isSuccess=!1}o.isSubmitting=!1}return t.div({pbEvent:`pageConfirmEmailChange`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),()=>o.isSuccess?t.div({pbEvent:`confirmEmailChangeAlert`,className:`alert success txt-center`},t.p(null,`The email was successfully changed.`),t.p(null,`You can go back and sign in with your new email address.`)):t.form({pbEvent:`confirmEmailChangeForm`,className:`grid confirm-email-change-form`,onsubmit:e=>{e.preventDefault(),s()}},t.div({className:`col-12`},t.div({className:`content txt-center m-b-sm`},`Type your password to confirm changing your email address to `,t.strong(null,a.newEmail),`:`),t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`password_confirm`},`Password`),t.input({id:`password_confirm`,name:`password`,required:!0,autofocus:!0,type:()=>o.showPassword?`text`:`password`,value:()=>o.password,oninput:e=>o.password=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>o.showPassword?`Hide password`:`Show password`),onclick:()=>o.showPassword=!o.showPassword},t.i({className:()=>o.showPassword?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.button({className:()=>`btn lg block ${o.isSubmitting?`loading`:``}`,disabled:()=>o.isSubmitting},t.span({className:`txt`},`Confirm new email`)))))}export{r as pageConfirmEmailChange};
|
||||
@@ -0,0 +1 @@
|
||||
import{n as e,t as n}from"./index-DoRNK5jP.js";function r(r){let i=r.params?.token||``,a=e(i);if(!a.email||!a.collectionId){app.toasts.error(`Invalid or expired password reset token.`),window.location.hash=`#/`;return}app.store.title=`Confirm password reset`;let o=store({newPassword:``,newPasswordConfirm:``,showNewPassword:!1,showNewPasswordConfirm:!1,isSubmitting:!1,isSuccess:!1});async function s(){if(o.isSubmitting)return;o.isSubmitting=!0;let e=new n(app.pb.baseURL);try{await e.collection(a.collectionId).confirmPasswordReset(i,o.newPassword,o.newPasswordConfirm),o.isSuccess=!0}catch(e){app.checkApiError(e)}o.isSubmitting=!1}return t.div({pbEvent:`pageConfirmPasswordReset`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),()=>o.isSuccess?t.div({pbEvent:`confirmPasswordResetAlert`,className:`alert success txt-center`},t.p(null,`The password was successfully changed.`),t.p(null,`You can go back to sign in with your new password.`)):t.form({pbEvent:`confirmPasswordResetForm`,className:`grid confirm-password-reset-form`,onsubmit:e=>{e.preventDefault(),s()}},t.div({className:`col-12`},t.div({className:`content txt-center m-b-sm`},`Type your new password for `,t.strong(null,a.email),`:`),t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`newPassword`},`New password`),t.input({id:`newPassword`,name:`password`,required:!0,autofocus:!0,autocomplete:`new-password`,type:()=>o.showNewPassword?`text`:`password`,value:()=>o.newPassword,oninput:e=>o.newPassword=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>o.showNewPassword?`Hide password`:`Show password`),onclick:()=>o.showNewPassword=!o.showNewPassword},t.i({className:()=>o.showNewPassword?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`newPasswordConfirm`},`New password confirm`),t.input({id:`newPasswordConfirm`,name:`passwordConfirm`,required:!0,autocomplete:`new-password`,type:()=>o.showNewPasswordConfirm?`text`:`password`,value:()=>o.newPasswordConfirm,oninput:e=>o.newPasswordConfirm=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>o.showNewPasswordConfirm?`Hide password`:`Show password`),onclick:()=>o.showNewPasswordConfirm=!o.showNewPasswordConfirm},t.i({className:()=>o.showNewPasswordConfirm?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.button({className:()=>`btn lg block ${o.isSubmitting?`loading`:``}`,disabled:()=>o.isSubmitting},t.span({className:`txt`},`Set new password`)))))}export{r as pageConfirmPasswordReset};
|
||||
@@ -1 +0,0 @@
|
||||
import{i as e,t as n}from"./pocketbase.es-B_4DUNUU.js";function r(r){let i=r.params?.token||``,a=e(i);if(!a.email||!a.collectionId){app.toasts.error(`Invalid or expired password reset token.`),window.location.hash=`#/`;return}app.store.title=`Confirm password reset`;let o=store({newPassword:``,newPasswordConfirm:``,showNewPassword:!1,showNewPasswordConfirm:!1,isSubmitting:!1,isSuccess:!1});async function s(){if(o.isSubmitting)return;o.isSubmitting=!0;let e=new n(app.pb.baseURL);try{await e.collection(a.collectionId).confirmPasswordReset(i,o.newPassword,o.newPasswordConfirm),o.isSuccess=!0}catch(e){app.checkApiError(e)}o.isSubmitting=!1}return t.div({pbEvent:`pageConfirmPasswordReset`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),()=>o.isSuccess?t.div({pbEvent:`confirmPasswordResetAlert`,className:`alert success txt-center`},t.p(null,`The password was successfully changed.`),t.p(null,`You can go back to sign in with your new password.`)):t.form({pbEvent:`confirmPasswordResetForm`,className:`grid confirm-password-reset-form`,onsubmit:e=>{e.preventDefault(),s()}},t.div({className:`col-12`},t.div({className:`content txt-center m-b-sm`},`Type your new password for `,t.strong(null,a.email),`:`),t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`newPassword`},`New password`),t.input({id:`newPassword`,name:`password`,required:!0,autofocus:!0,autocomplete:`new-password`,type:()=>o.showNewPassword?`text`:`password`,value:()=>o.newPassword,oninput:e=>o.newPassword=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>o.showNewPassword?`Hide password`:`Show password`),onclick:()=>o.showNewPassword=!o.showNewPassword},t.i({className:()=>o.showNewPassword?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`newPasswordConfirm`},`New password confirm`),t.input({id:`newPasswordConfirm`,name:`passwordConfirm`,required:!0,autocomplete:`new-password`,type:()=>o.showNewPasswordConfirm?`text`:`password`,value:()=>o.newPasswordConfirm,oninput:e=>o.newPasswordConfirm=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>o.showNewPasswordConfirm?`Hide password`:`Show password`),onclick:()=>o.showNewPasswordConfirm=!o.showNewPasswordConfirm},t.i({className:()=>o.showNewPasswordConfirm?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.button({className:()=>`btn lg block ${o.isSubmitting?`loading`:``}`,disabled:()=>o.isSubmitting},t.span({className:`txt`},`Set new password`)))))}export{r as pageConfirmPasswordReset};
|
||||
@@ -0,0 +1 @@
|
||||
import{n as e,t as n}from"./index-DoRNK5jP.js";function r(r){let i=r.params?.token||``,a=e(i);if(!a.email||!a.collectionId){app.toasts.error(`Invalid or expired verification token.`),window.location.hash=`#/`;return}app.store.title=`Confirm verification`;let o=store({isConfirming:!1,isConfirmSuccess:!1,isResending:!1,isResendSuccess:!1});s();async function s(){if(o.isConfirming)return;o.isConfirming=!0;let e=new n(app.pb.baseURL);try{await e.collection(a.collectionId).confirmVerification(i),o.isConfirmSuccess=!0}catch{o.isConfirmSuccess=!1}o.isConfirming=!1}async function c(){if(o.isResending)return;o.isResending=!0;let e=new n(`../`);try{await e.collection(a.collectionId).requestVerification(a.email),o.isResendSuccess=!0}catch(e){app.checkApiError(e),o.isResendSuccess=!1}o.isResending=!1}return t.div({pbEvent:`pageConfirmVerification`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),()=>o.isConfirming?t.div({className:`block txt-center`},t.span({className:`loader`},`Please wait...`)):o.isConfirmSuccess?t.div({pbEvent:`confirmVerificationSuccessAlert`,className:`alert success txt-center`},t.p(null,`Successfully verified `,t.strong(null,a.email),`.`)):o.isResendSuccess?t.div({pbEvent:`confirmVerificationResendAlert`,className:`alert success txt-center`},t.p(null,`Please check your email for the new verification link.`)):[t.div({pbEvent:`confirmVerificationErrorAlert`,className:`alert danger txt-center m-b-base`},t.p(null,`Invalid or expired verification token.`)),t.button({type:`button`,className:()=>`btn transparent lg block ${o.isResending?`loading`:``}`,disabled:()=>o.isResending,onclick:()=>c()},t.span({className:`txt`},`Resend`))])}export{r as pageConfirmVerification};
|
||||
@@ -1 +0,0 @@
|
||||
import{i as e,t as n}from"./pocketbase.es-B_4DUNUU.js";function r(r){let i=r.params?.token||``,a=e(i);if(!a.email||!a.collectionId){app.toasts.error(`Invalid or expired verification token.`),window.location.hash=`#/`;return}app.store.title=`Confirm verification`;let o=store({isConfirming:!1,isConfirmSuccess:!1,isResending:!1,isResendSuccess:!1});s();async function s(){if(o.isConfirming)return;o.isConfirming=!0;let e=new n(app.pb.baseURL);try{await e.collection(a.collectionId).confirmVerification(i),o.isConfirmSuccess=!0}catch{o.isConfirmSuccess=!1}o.isConfirming=!1}async function c(){if(o.isResending)return;o.isResending=!0;let e=new n(`../`);try{await e.collection(a.collectionId).requestVerification(a.email),o.isResendSuccess=!0}catch(e){app.checkApiError(e),o.isResendSuccess=!1}o.isResending=!1}return t.div({pbEvent:`pageConfirmVerification`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),()=>o.isConfirming?t.div({className:`block txt-center`},t.span({className:`loader`},`Please wait...`)):o.isConfirmSuccess?t.div({pbEvent:`confirmVerificationSuccessAlert`,className:`alert success txt-center`},t.p(null,`Successfully verified `,t.strong(null,a.email),`.`)):o.isResendSuccess?t.div({pbEvent:`confirmVerificationResendAlert`,className:`alert success txt-center`},t.p(null,`Please check your email for the new verification link.`)):[t.div({pbEvent:`confirmVerificationErrorAlert`,className:`alert danger txt-center m-b-base`},t.p(null,`Invalid or expired verification token.`)),t.button({type:`button`,className:()=>`btn transparent lg block ${o.isResending?`loading`:``}`,disabled:()=>o.isResending,onclick:()=>c()},t.span({className:`txt`},`Resend`))])}export{r as pageConfirmVerification};
|
||||
+1
@@ -0,0 +1 @@
|
||||
import{n as e,r as n}from"./index-DoRNK5jP.js";function r(r){let i=r.params?.token||``;if(e(i).type!=`auth`||n(i)){app.toasts.error(`The installer token is invalid or has expired.`),window.location.hash=`#/`;return}app.store.title=`Setup your PocketBase instance`;let a=store({email:``,password:``,passwordConfirm:``,showPassword:!1,showPasswordConfirm:!1,isSubmitting:!1,isUploading:!1,get isBusy(){return a.isSubmitting||a.isUploading}});async function o(){if(!a.isBusy){a.isSubmitting=!0;try{await app.pb.collection(`_superusers`).create({email:a.email,password:a.password,passwordConfirm:a.passwordConfirm},{headers:{Authorization:i}}),await app.pb.collection(`_superusers`).authWithPassword(a.email,a.password),window.location.hash=`#/`}catch(e){app.checkApiError(e)}a.isSubmitting=!1}}let s=`backupFileInput`;function c(){let e=document.getElementById(s);e&&(e.value=``)}function l(e){e&&app.modals.confirm(t.h6(null,`Note that we don't perform validations for the uploaded backup files. Proceed with caution and only if you trust the file source.\n\nDo you really want to upload and initialize "${e.name}"?`),()=>{u(e)},()=>{c()})}async function u(e){if(!(!e||a.isBusy)){a.isUploading=!0;try{await app.pb.backups.upload({file:e},{headers:{Authorization:i}}),await app.pb.backups.restore(e.name,{headers:{Authorization:i}}),app.toasts.info(`Please wait while extracting the uploaded archive!`),await new Promise(e=>setTimeout(e,3e3)),window.location.href=`#/`}catch(e){app.checkApiError(e)}c(),a.isUploading=!1}}return t.div({pbEvent:`pageInstaller`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),t.form({pbEvent:`installerForm`,className:`grid installer-form`,onsubmit:e=>{e.preventDefault(),o(a)}},t.div({className:`col-12 txt-center`},`Create your first superuser account in order to continue:`),t.div({className:`col-12`},t.div({className:`field`},t.label({htmlFor:`superuser_email`},`Email`),t.input({id:`superuser_email`,name:`email`,type:`email`,required:!0,autofocus:!0,autocomplete:`off`,disabled:()=>a.isBusy,value:()=>a.email,oninput:e=>a.email=e.target.value}))),t.div({className:`col-12`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`superuser_password`},`Password`),t.input({id:`superuser_password`,name:`password`,min:10,required:!0,disabled:()=>a.isBusy,type:()=>a.showPassword?`text`:`password`,value:()=>a.password,oninput:e=>a.password=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>a.showPassword?`Hide password`:`Show password`),onclick:()=>a.showPassword=!a.showPassword},t.i({className:()=>a.showPassword?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0})))),t.div({className:`field-help`},`Recommended at least 10 characters.`)),t.div({className:`col-12`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`superuser_password_confirm`},`Password confirm`),t.input({id:`superuser_password_confirm`,name:`passwordConfirm`,required:!0,disabled:()=>a.isBusy,type:()=>a.showPasswordConfirm?`text`:`password`,value:()=>a.passwordConfirm,oninput:e=>a.passwordConfirm=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>a.showPasswordConfirm?`Hide password`:`Show password`),onclick:()=>a.showPasswordConfirm=!a.showPasswordConfirm},t.i({className:()=>a.showPasswordConfirm?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.button({className:()=>`btn lg next block ${a.isSubmitting?`loading`:``}`,disabled:()=>a.isBusy},t.span({className:`txt`},`Create superuser and login`),t.i({className:`ri-arrow-right-line`,ariaHidden:!0})))),t.hr(),t.label({htmlFor:s,className:()=>`btn secondary transparent lg block ${a.isBusy?`disabled`:``} ${a.isUploading?`loading`:``}`},t.i({className:`ri-upload-cloud-line`,ariaHidden:!0}),t.span({className:`txt`},`Or initialize from backup`)),t.input({id:s,type:`file`,className:`hidden`,accept:`.zip`,onchange:e=>{l(e.target?.files?.[0])}}))}export{r as pageInstaller};
|
||||
-1
@@ -1 +0,0 @@
|
||||
import{a as e,i as n}from"./pocketbase.es-B_4DUNUU.js";function r(r){let i=r.params?.token||``;if(n(i).type!=`auth`||e(i)){app.toasts.error(`The installer token is invalid or has expired.`),window.location.hash=`#/`;return}app.store.title=`Setup your PocketBase instance`;let a=store({email:``,password:``,passwordConfirm:``,showPassword:!1,showPasswordConfirm:!1,isSubmitting:!1,isUploading:!1,get isBusy(){return a.isSubmitting||a.isUploading}});async function o(){if(!a.isBusy){a.isSubmitting=!0;try{await app.pb.collection(`_superusers`).create({email:a.email,password:a.password,passwordConfirm:a.passwordConfirm},{headers:{Authorization:i}}),await app.pb.collection(`_superusers`).authWithPassword(a.email,a.password),window.location.hash=`#/`}catch(e){app.checkApiError(e)}a.isSubmitting=!1}}let s=`backupFileInput`;function c(){let e=document.getElementById(s);e&&(e.value=``)}function l(e){e&&app.modals.confirm(t.h6(null,`Note that we don't perform validations for the uploaded backup files. Proceed with caution and only if you trust the file source.\n\nDo you really want to upload and initialize "${e.name}"?`),()=>{u(e)},()=>{c()})}async function u(e){if(!(!e||a.isBusy)){a.isUploading=!0;try{await app.pb.backups.upload({file:e},{headers:{Authorization:i}}),await app.pb.backups.restore(e.name,{headers:{Authorization:i}}),app.toasts.info(`Please wait while extracting the uploaded archive!`),await new Promise(e=>setTimeout(e,3e3)),window.location.href=`#/`}catch(e){app.checkApiError(e)}c(),a.isUploading=!1}}return t.div({pbEvent:`pageInstaller`,className:`wrapper sm m-auto p-b-base`},t.header({className:`txt-center m-b-base`},t.img({className:`main-logo`,src:()=>app.store.mainLogo,ariaHidden:!0,alt:`App logo`}),t.h5({className:`m-t-10`},()=>app.store.title)),t.form({pbEvent:`installerForm`,className:`grid installer-form`,onsubmit:e=>{e.preventDefault(),o(a)}},t.div({className:`col-12 txt-center`},`Create your first superuser account in order to continue:`),t.div({className:`col-12`},t.div({className:`field`},t.label({htmlFor:`superuser_email`},`Email`),t.input({id:`superuser_email`,name:`email`,type:`email`,required:!0,autofocus:!0,autocomplete:`off`,disabled:()=>a.isBusy,value:()=>a.email,oninput:e=>a.email=e.target.value}))),t.div({className:`col-12`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`superuser_password`},`Password`),t.input({id:`superuser_password`,name:`password`,min:10,required:!0,disabled:()=>a.isBusy,type:()=>a.showPassword?`text`:`password`,value:()=>a.password,oninput:e=>a.password=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>a.showPassword?`Hide password`:`Show password`),onclick:()=>a.showPassword=!a.showPassword},t.i({className:()=>a.showPassword?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0})))),t.div({className:`field-help`},`Recommended at least 10 characters.`)),t.div({className:`col-12`},t.div({className:`fields`},t.div({className:`field`},t.label({htmlFor:`superuser_password_confirm`},`Password confirm`),t.input({id:`superuser_password_confirm`,name:`passwordConfirm`,required:!0,disabled:()=>a.isBusy,type:()=>a.showPasswordConfirm?`text`:`password`,value:()=>a.passwordConfirm,oninput:e=>a.passwordConfirm=e.target.value})),t.div({className:`field addon`},t.button({type:`button`,tabIndex:-1,className:`btn sm transparent secondary circle tooltip-right`,ariaLabel:app.attrs.tooltip(()=>a.showPasswordConfirm?`Hide password`:`Show password`),onclick:()=>a.showPasswordConfirm=!a.showPasswordConfirm},t.i({className:()=>a.showPasswordConfirm?`ri-eye-off-line`:`ri-eye-line`,ariaHidden:!0}))))),t.div({className:`col-12`},t.button({className:()=>`btn lg next block ${a.isSubmitting?`loading`:``}`,disabled:()=>a.isBusy},t.span({className:`txt`},`Create superuser and login`),t.i({className:`ri-arrow-right-line`,ariaHidden:!0})))),t.hr(),t.label({htmlFor:s,className:()=>`btn secondary transparent lg block ${a.isBusy?`disabled`:``} ${a.isUploading?`loading`:``}`},t.i({className:`ri-upload-cloud-line`,ariaHidden:!0}),t.span({className:`txt`},`Or initialize from backup`)),t.input({id:s,type:`file`,className:`hidden`,accept:`.zip`,onchange:e=>{l(e.target?.files?.[0])}}))}export{r as pageInstaller};
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
-1
@@ -1 +0,0 @@
|
||||
var e=class e extends Error{constructor(t){super(`ClientResponseError`),this.url=``,this.status=0,this.response={},this.isAbort=!1,this.originalError=null,Object.setPrototypeOf(this,e.prototype),typeof t==`object`&&t&&(this.originalError=t.originalError,this.url=typeof t.url==`string`?t.url:``,this.status=typeof t.status==`number`?t.status:0,this.isAbort=!!t.isAbort||t.name===`AbortError`||t.message===`Aborted`,t.response!==null&&typeof t.response==`object`?this.response=t.response:t.data!==null&&typeof t.data==`object`?this.response=t.data:this.response={}),this.originalError||t instanceof e||(this.originalError=t),this.name=`ClientResponseError `+this.status,this.message=this.response?.message,this.message||(this.isAbort?this.message=`The request was aborted (most likely autocancelled; you can find more info in https://github.com/pocketbase/js-sdk#auto-cancellation).`:this.originalError?.cause?.message?.includes(`ECONNREFUSED ::1`)?this.message=`Failed to connect to the PocketBase server. Try changing the SDK URL from localhost to 127.0.0.1 (https://github.com/pocketbase/js-sdk/issues/21).`:this.message=`Something went wrong.`),this.cause=this.originalError}get data(){return this.response}toJSON(){return{...this}}},t=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;function n(e,t){let n={};if(typeof e!=`string`)return n;let r=Object.assign({},t||{}).decode||i,a=0;for(;a<e.length;){let t=e.indexOf(`=`,a);if(t===-1)break;let i=e.indexOf(`;`,a);if(i===-1)i=e.length;else if(i<t){a=e.lastIndexOf(`;`,t-1)+1;continue}let o=e.slice(a,t).trim();if(n[o]===void 0){let a=e.slice(t+1,i).trim();a.charCodeAt(0)===34&&(a=a.slice(1,-1));try{n[o]=r(a)}catch{n[o]=a}}a=i+1}return n}function r(e,n,r){let i=Object.assign({},r||{}),o=i.encode||a;if(!t.test(e))throw TypeError(`argument name is invalid`);let s=o(n);if(s&&!t.test(s))throw TypeError(`argument val is invalid`);let c=e+`=`+s;if(i.maxAge!=null){let e=i.maxAge-0;if(isNaN(e)||!isFinite(e))throw TypeError(`option maxAge is invalid`);c+=`; Max-Age=`+Math.floor(e)}if(i.domain){if(!t.test(i.domain))throw TypeError(`option domain is invalid`);c+=`; Domain=`+i.domain}if(i.path){if(!t.test(i.path))throw TypeError(`option path is invalid`);c+=`; Path=`+i.path}if(i.expires){if(!function(e){return Object.prototype.toString.call(e)===`[object Date]`||e instanceof Date}(i.expires)||isNaN(i.expires.valueOf()))throw TypeError(`option expires is invalid`);c+=`; Expires=`+i.expires.toUTCString()}if(i.httpOnly&&(c+=`; HttpOnly`),i.secure&&(c+=`; Secure`),i.priority)switch(typeof i.priority==`string`?i.priority.toLowerCase():i.priority){case`low`:c+=`; Priority=Low`;break;case`medium`:c+=`; Priority=Medium`;break;case`high`:c+=`; Priority=High`;break;default:throw TypeError(`option priority is invalid`)}if(i.sameSite)switch(typeof i.sameSite==`string`?i.sameSite.toLowerCase():i.sameSite){case!0:c+=`; SameSite=Strict`;break;case`lax`:c+=`; SameSite=Lax`;break;case`strict`:c+=`; SameSite=Strict`;break;case`none`:c+=`; SameSite=None`;break;default:throw TypeError(`option sameSite is invalid`)}return c}function i(e){return e.indexOf(`%`)===-1?e:decodeURIComponent(e)}function a(e){return encodeURIComponent(e)}var o=typeof navigator<`u`&&navigator.product===`ReactNative`||typeof global<`u`&&global.HermesInternal,s;function c(e){if(e)try{let t=decodeURIComponent(s(e.split(`.`)[1]).split(``).map((function(e){return`%`+(`00`+e.charCodeAt(0).toString(16)).slice(-2)})).join(``));return JSON.parse(t)||{}}catch{}return{}}function l(e,t=0){let n=c(e);return!(Object.keys(n).length>0&&(!n.exp||n.exp-t>Date.now()/1e3))}s=typeof atob!=`function`||o?e=>{let t=String(e).replace(/=+$/,``);if(t.length%4==1)throw Error(`'atob' failed: The string to be decoded is not correctly encoded.`);for(var n,r,i=0,a=0,o=``;r=t.charAt(a++);~r&&(n=i%4?64*n+r:r,i++%4)&&(o+=String.fromCharCode(255&n>>(-2*i&6))))r=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=`.indexOf(r);return o}:atob;var u=`pb_auth`,d=class{constructor(){this.baseToken=``,this.baseModel=null,this._onChangeCallbacks=[]}get token(){return this.baseToken}get record(){return this.baseModel}get model(){return this.baseModel}get isValid(){return!l(this.token)}get isSuperuser(){let e=c(this.token);return e.type==`auth`&&(this.record?.collectionName==`_superusers`||!this.record?.collectionName&&e.collectionId==`pbc_3142635823`)}get isAdmin(){return console.warn(`Please replace pb.authStore.isAdmin with pb.authStore.isSuperuser OR simply check the value of pb.authStore.record?.collectionName`),this.isSuperuser}get isAuthRecord(){return console.warn(`Please replace pb.authStore.isAuthRecord with !pb.authStore.isSuperuser OR simply check the value of pb.authStore.record?.collectionName`),c(this.token).type==`auth`&&!this.isSuperuser}save(e,t){this.baseToken=e||``,this.baseModel=t||null,this.triggerChange()}clear(){this.baseToken=``,this.baseModel=null,this.triggerChange()}loadFromCookie(e,t=u){let r=n(e||``)[t]||``,i={};try{i=JSON.parse(r),(typeof i!=`obje
Line truncated
|
||||
Vendored
+2
-3
@@ -13,9 +13,8 @@
|
||||
|
||||
<!-- prism -->
|
||||
<script src="./libs/prism/prism.js" data-manual></script>
|
||||
<script type="module" crossorigin src="./assets/index-_S-h6ORT.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/pocketbase.es-B_4DUNUU.js">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DRWtZ-mO.css">
|
||||
<script type="module" crossorigin src="./assets/index-DoRNK5jP.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-CUsGqqpi.css">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
||||
Vendored
+2
-2
@@ -1,3 +1,3 @@
|
||||
(function(e){let t,n=new Set,r=new Map,i=[],a,o=Symbol(),s=Symbol(),c=Symbol(),l=Symbol(),u=Symbol(),d=Symbol(),f=Symbol(),p=Symbol(),m=Symbol(),h=Symbol();function g(e,n){let d={[o]:`_`+Math.random(),[p]:e,[m]:n};return r.set(d[o],d),d.run=()=>{let e;t&&(e=t,d[s]=t[o],t[c]=t[c]||[],t[c].push(d[o])),d[l]?.forEach(e=>{e.delete(d[o])}),t=d;let n=d[p](d[h],d);d[m]&&(t=null,d[m](n,d[h],d)),d[h]=n,t=e},d.unwatch=function(){d[u]=1,i.push(d[o]),a&&clearTimeout(a),a=setTimeout(()=>{for(let e of i)_(e);i=[],a=null},50)},d.run(),d}function _(e){let t=r.get(e);if(t?.[d]?.(),t?.[c]){for(let e of t[c])_(e);t[s]=null,t[c]=null}if(t?.[l]){for(let n of t[l])n.delete(e);t[l]=null}r.delete(e)}function v(e){return y(e,new Map)}function y(e,n){let r=typeof e==`object`&&!Array.isArray(e)?Object.getOwnPropertyDescriptors(e):{};return new Proxy(e,{get(e,i,a){if(typeof i==`symbol`)return e[i];if(i==`__raw`)return e;if(r[i]?.get&&!e[s]){let n=i;if(i=`@@`+i,!t){let t=r[n].get.call(e);return r[n]._watcher&&(a[i]=t),t}let s=t[o];if(r[n]._refs=r[n]._refs||new Set,!r[n]._refs.has(s)){r[n]._refs.add(s);let e=t[d];t[d]=()=>{e?.(),r[n]._refs.delete(s),!r[n]._refs.size&&r[n]._watcher&&_(r[n]._watcher[o])}}if(!r[n]._watcher){let o=t;t=null;let s=g(r[n].get.bind(e),t=>{e.hasOwnProperty(i)?a[i]=t:Object.defineProperty(e,i,{writable:!0,enumerable:!1,value:t})});r[n]._watcher=s,s[d]=()=>{r[n]._watcher=null},t=o}}let c=e?.[i];if(typeof c==`function`)return c;if(typeof c==`object`&&c&&!c[s]&&(c.constructor?.name==`Object`||c.constructor?.name==`Array`||c.constructor?.name==null)&&(c[s]=[a,i],c=y(c,n),e[i]=c),t){let r=b(e,i),a=t[o],s=[r];t[l]=t[l]||new Set;for(let e of s){let r=n.get(e);r||(r=new Set,n.set(e,r)),r.add(a),t[l].add(r)}}return c},set(e,t,r){if(typeof t==`symbol`)return e[t]=r,!0;let i=e[t];i?.[s]&&(i[f]=!0),r?.[s]&&Array.isArray(e)&&!isNaN(t)&&(r[s][1]=t);let a=!1;return i===void 0&&!e.hasOwnProperty(t)&&(a=!0),e[t]=r,a&&x(e,`toJSON`,n),(r!==i||t===`length`)&&x(e,t,n),!0},deleteProperty(e,t){if(typeof t!=`symbol`){x(e,t,n);let r=b(e,t);for(let e of n)(e[0]==r||e[0].startsWith(r+`/`))&&n.delete(e[0])}return delete e[t]}})}function b(e,t){let n=t,r=e?.[s];for(;r;)n=r[1]+`/`+n,r=r[0][s];return n}function x(e,t,i){let a=b(e,t),o=i.get(a);if(o)for(let e of o)n.delete(e),n.add(e),n.size==1&&queueMicrotask(()=>{let e={},t;for(let i of n)if(t=r.get(i),!(!t||t[u])&&!(t[s]&&n.has(t[s]))){if(e[i]=(e[i]||0)+1,e[i]>250){console.warn(`Possible infinite loop for watcher `+i+`:`,`
|
||||
(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});let t,n=new Set,r=new Map,i=[],a,o=Symbol(),s=Symbol(),c=Symbol(),l=Symbol(),u=Symbol(),d=Symbol(),f=Symbol(),p=Symbol(),m=Symbol(),h=Symbol();function g(e,n){let d={[o]:`_`+Math.random(),[p]:e,[m]:n};return r.set(d[o],d),d.run=()=>{let e;t&&(e=t,d[s]=t[o],t[c]=t[c]||[],t[c].push(d[o])),d[l]?.forEach(e=>{e.delete(d[o])}),t=d;let n=d[p](d[h],d);d[m]&&(t=null,d[m](n,d[h],d)),d[h]=n,t=e},d.unwatch=function(){d[u]=1,i.push(d[o]),a&&clearTimeout(a),a=setTimeout(()=>{for(let e of i)_(e);i=[],a=null},50)},d.run(),d}function _(e){let t=r.get(e);if(t?.[d]?.(),t?.[c]){for(let e of t[c])_(e);t[s]=null,t[c]=null}if(t?.[l]){for(let n of t[l])n.delete(e);t[l]=null}r.delete(e)}function v(e){return y(e,new Map)}function y(e,n){let r=typeof e==`object`&&!Array.isArray(e)?Object.getOwnPropertyDescriptors(e):{};return new Proxy(e,{get(e,i,a){if(typeof i==`symbol`)return e[i];if(i==`__raw`)return e;if(r[i]?.get&&!e[s]){let n=i;if(i=`@@`+i,!t){let t=r[n].get.call(e);return r[n]._watcher&&(a[i]=t),t}let s=t[o];if(r[n]._refs=r[n]._refs||new Set,!r[n]._refs.has(s)){r[n]._refs.add(s);let e=t[d];t[d]=()=>{e?.(),r[n]._refs.delete(s),!r[n]._refs.size&&r[n]._watcher&&_(r[n]._watcher[o])}}if(!r[n]._watcher){let o=t;t=null;let s=g(r[n].get.bind(e),t=>{e.hasOwnProperty(i)?a[i]=t:Object.defineProperty(e,i,{writable:!0,enumerable:!1,value:t})});r[n]._watcher=s,s[d]=()=>{r[n]._watcher=null},t=o}}let c=e?.[i];if(typeof c==`function`)return c;if(typeof c==`object`&&c&&!c[s]&&(c.constructor?.name==`Object`||c.constructor?.name==`Array`||c.constructor?.name==null)&&(c[s]=[a,i],c=y(c,n),e[i]=c),t){let r=b(e,i),a=t[o],s=[r];t[l]=t[l]||new Set;for(let e of s){let r=n.get(e);r||(r=new Set,n.set(e,r)),r.add(a),t[l].add(r)}}return c},set(e,t,r){if(typeof t==`symbol`)return e[t]=r,!0;let i=e[t];i?.[s]&&(i[f]=!0),r?.[s]&&Array.isArray(e)&&!isNaN(t)&&(r[s][1]=t);let a=!1;return i===void 0&&!e.hasOwnProperty(t)&&(a=!0),e[t]=r,a&&x(e,`toJSON`,n),(r!==i||t===`length`)&&x(e,t,n),!0},deleteProperty(e,t){if(typeof t!=`symbol`){x(e,t,n);let r=b(e,t);for(let e of n)(e[0]==r||e[0].startsWith(r+`/`))&&n.delete(e[0])}return delete e[t]}})}function b(e,t){let n=t,r=e?.[s];for(;r;)n=r[1]+`/`+n,r=r[0][s];return n}function x(e,t,i){let a=b(e,t),o=i.get(a);if(o)for(let e of o)n.delete(e),n.add(e),n.size==1&&queueMicrotask(()=>{let e={},t;for(let i of n)if(t=r.get(i),!(!t||t[u])&&!(t[s]&&n.has(t[s]))){if(e[i]=(e[i]||0)+1,e[i]>250){console.warn(`Possible infinite loop for watcher `+i+`:`,`
|
||||
watch(`+t[p]?.toString()+(t[m]?`, `+t[m].toString():``)+`)`);continue}t.run()}n.clear()})}let S=new Proxy({},{get(e,t){return function(){return w(),O.call(void 0,t,...arguments)}}}),C=!1;function w(){if(C)return;C=!0;function e(t,n){for(let r of n)r[t]&&r[t](r),r.childNodes&&e(t,r.childNodes)}new MutationObserver(t=>{for(let n of t)e(`onmount`,n.addedNodes),e(`onunmount`,n.removedNodes)}).observe(document,{childList:!0,subtree:!0})}let T=Symbol(),E=Symbol(),D=Symbol();function O(e,t={},...n){let r=document.createElement(e);if(t)for(let e in t){let n=t[e],i=!1;e.length>5&&e.startsWith(`html-`)&&(i=!0,e=e.substring(5)),n===void 0?r.removeAttribute(e):typeof n!=`function`||e.length>2&&e.startsWith(`on`)?i?r.setAttribute(e,n):r[e]=n:(e==`rid`&&console.warn(`rid is provided as reactive function and will not have effect for `+r+`.
|
||||
Consider using a plain number, string or object/array reference instead.`),r[T]=r[T]||[],r[T].push(()=>{if(!r)return;let t=n(r,e);t===void 0?(r[e]!==void 0&&(r[e]=void 0),r.removeAttribute(e)):i?r.setAttribute(e,t):r[e]=t}))}let i=r.onmount;r.onmount=()=>{if(!r[D]){if(r[D]=!0,r[T]){r[E]=r[E]||[];for(let e of r[T])r[E].push(g(e))}i?.(r)}};let a=r.onunmount;return r.onunmount=()=>{if(r[D]){if(r[D]=!1,r[E])for(let e of r[E])e.unwatch();r[E]=null,a?.(r)}},k(r,n),r}function k(e,t){t=N(t);for(let n of t)if(typeof n==`function`)A(e,n);else{let t=P(n);Array.isArray(t)?k(e,t):t&&e.appendChild(t)}}function A(e,t){let n=document.createComment(``);e.appendChild(n);let r=[],i=new Map,a=e.moveBefore||e.insertBefore;e[T]=e[T]||[],e[T].push(()=>{if(!e)return;let o=N(t(e)),s=o.length,c=new Map;if(!r?.length){let t=document.createDocumentFragment();for(let e=0;e<s;e++){o[e]=P(o[e]),t.appendChild(o[e]);let n=o[e].rid;n!==void 0&&(c.has(n)?console.warn(`Duplicated rid:`,n,o[e]):c.set(n,e))}e.insertBefore(t,n),t=null,r=o,i=c;return}let l=[],u=[],d=new Set,f=[];for(let e=0;e<s;e++){o[e]=P(o[e]);let t=o[e].rid;if(t!==void 0){c.has(t)?console.warn(`Duplicated rid:`,t,o[e]):c.set(t,e);let n=i.get(t);if(n>=0){d.add(r[n]),o[e]=r[n],f.push(n);continue}}u.push({child:o[e],prev:o[e-1]})}let p=j(f);f.length!=p.length&&f.forEach((e,t)=>{p.has(e)||l.push({child:r[e],targetPos:t})});let m,h,g;for(let t of l)m=r.findIndex(e=>e===t.child),h=t.targetPos,m<t.targetPos&&(h=t.targetPos+1),g=r[h]||n,M(r,m,t.targetPos),a.call(e,t.child,g);for(let e of u)e.prev?e.prev.after(e.child):(r[0]||n).before(e.child);for(let e=0;e<r.length;e++)d.has(r[e])||r[e].remove?.();r=o,i=c,o=null,c=null,d=null,l=null,u=null})}function j(e){let t=[],n=[];for(let r=0;r<e.length;r++){let i=e[r],a=0,o=0,s=t.length;for(;a<s;)o=Math.floor((a+s)/2),e[t[o]]>=i?s=o:a=o+1;a>0&&(n[r]=t[a-1]),t[a]=r}let r=new Set,i=t[t.length-1];for(;i!==void 0;)r.add(e[i]),i=n[i];return r}function M(e,t,n){if(t==n)return e;let r=t>n?-1:1,i=e[t];for(let i=t;i!=n;i+=r)e[i]=e[i+r];e[n]=i}function N(e){return e==null?[]:Array.isArray(e)?e:[e]}function P(e){return typeof e==`string`||typeof e==`number`||typeof e==`boolean`?document.createTextNode(e):e?.__raw===void 0?e:e.__raw}function F(e,t={fallbackPath:`#/`,transition:!0}){let n=L(e),r,i=()=>{let e=window.location.hash,i=I(n,e);if(!i){if(t.fallbackPath!=e){window.location.hash=t.fallbackPath;return}console.warn(`missing route:`,e);return}let a=async()=>{try{await r?.(),r=await i.handler(i)}catch(e){console.warn(`route navigation failed:`,e)}};t.transition&&document.startViewTransition?document.startViewTransition(a):a()};return window.addEventListener(`hashchange`,i),i(),()=>{window.removeEventListener(`hashchange`,i)}}function I(e,t){for(let n of e){let e=t.match(n.regex);if(!e)continue;let r={},i=t.split(`?`)?.[1];if(i){let e=new URLSearchParams(i);for(let[t,n]of e.entries())Array.isArray(r[t])||(r[t]=r[t]?[r[t]]:[]),r[t].push(n)}return Object.assign({path:t,query:r,params:e.groups||{}},n)}}function L(e){let t=[];for(let n in e){let r=n.split(`/`);for(let e in r)r[e].length>2&&r[e].startsWith(`{`)&&r[e].endsWith(`}`)?r[e]=`(?<`+r[e].substring(1,r[e].length-1)+`>[^\\/#?]+)`:r[e]=RegExp.escape(r[e]);t.push({regex:RegExp(`^`+r.join(`\\/`)+`(?:[?#].*)?$`),pattern:n,handler:e[n]})}return t}e.router=F,e.store=v,e.t=S,e.watch=g})(this.window=this.window||{});
|
||||
Consider using a plain number, string or object/array reference instead.`),r[T]=r[T]||[],r[T].push(()=>{if(!r)return;let t=n(r,e);t===void 0?(r[e]!==void 0&&(r[e]=void 0),r.removeAttribute(e)):i?r.setAttribute(e,t):r[e]=t}))}let i=r.onmount;r.onmount=()=>{if(!r[D]){if(r[D]=!0,r[T]){r[E]=r[E]||[];for(let e of r[T])r[E].push(g(e))}i?.(r)}};let a=r.onunmount;return r.onunmount=()=>{if(r[D]){if(r[D]=!1,r[E])for(let e of r[E])e.unwatch();r[E]=null,a?.(r)}},k(r,n),r}function k(e,t){t=M(t);for(let n of t)if(typeof n==`function`)A(e,n);else{let t=N(n);Array.isArray(t)?k(e,t):t&&e.appendChild(t)}}function A(e,t){let n=document.createComment(``);e.appendChild(n);let r=[],i=new Map,a=e.moveBefore||e.insertBefore;e[T]=e[T]||[],e[T].push(()=>{if(!e)return;let o=M(t(e)),s=o.length,c=new Map;if(!r?.length){let t=document.createDocumentFragment();for(let e=0;e<s;e++){o[e]=N(o[e],!0),t.appendChild(o[e]);let n=o[e].rid;n!==void 0&&(c.has(n)?console.warn(`Duplicated rid:`,n,o[e]):c.set(n,e))}e.insertBefore(t,n),t=null,r=o,i=c;return}let l=[],u=[],d=new Set,f=[];for(let e=0;e<s;e++){o[e]=N(o[e],!0);let t=o[e].rid;if(t!==void 0){c.has(t)?console.warn(`Duplicated rid:`,t,o[e]):c.set(t,e);let n=i.get(t);if(n>=0){d.add(r[n]),o[e]=r[n],f.push(n);continue}}u.push({child:o[e],prev:o[e-1]})}let p=j(f);f.length!=p.length&&f.forEach((e,t)=>{p.has(e)||l.push({newPos:t,child:r[e],insertBefore:r[f[t+1]]||n})});for(let t=l.length-1;t>=0;t--)a.call(e,l[t].child,l[t].insertBefore);for(let e of u)e.prev?e.prev.after(e.child):(r[f[0]||0]||n).before(e.child);for(let e=0;e<r.length;e++)d.has(r[e])||r[e].remove?.();r=o,i=c,o=null,c=null,d=null,l=null,u=null})}function j(e){let t=[],n=[];for(let r=0;r<e.length;r++){let i=e[r],a=0,o=0,s=t.length;for(;a<s;)o=Math.floor((a+s)/2),e[t[o]]>=i?s=o:a=o+1;a>0&&(n[r]=t[a-1]),t[a]=r}let r=new Set,i=t[t.length-1];for(;i!==void 0;)r.add(e[i]),i=n[i];return r}function M(e){return e==null?[]:Array.isArray(e)?e:[e]}function N(e,t=!1){return t&&e==null&&(e=``),typeof e==`string`||typeof e==`number`||typeof e==`boolean`?document.createTextNode(e):e?.__raw===void 0?e:e.__raw}function P(e,t={fallbackPath:`#/`,transition:!0}){let n=I(e),r,i=()=>{let e=window.location.hash,i=F(n,e);if(!i){if(t.fallbackPath!=e){window.location.hash=t.fallbackPath;return}console.warn(`missing route:`,e);return}let a=async()=>{try{await r?.(),r=await i.handler(i)}catch(e){console.warn(`route navigation failed:`,e)}};t.transition&&document.startViewTransition?document.startViewTransition(a):a()};return window.addEventListener(`hashchange`,i),i(),()=>{window.removeEventListener(`hashchange`,i)}}function F(e,t){for(let n of e){let e=t.match(n.regex);if(!e)continue;let r={},i=t.split(`?`)?.[1];if(i){let e=new URLSearchParams(i);for(let[t,n]of e.entries())Array.isArray(r[t])||(r[t]=r[t]?[r[t]]:[]),r[t].push(n)}return Object.assign({path:t,query:r,params:e.groups||{}},n)}}function I(e){let t=[];for(let n in e){let r=n.split(`/`);for(let e in r)r[e].length>2&&r[e].startsWith(`{`)&&r[e].endsWith(`}`)?r[e]=`(?<`+r[e].substring(1,r[e].length-1)+`>[^\\/#?]+)`:r[e]=RegExp.escape(r[e]);t.push({regex:RegExp(`^`+r.join(`\\/`)+`(?:[?#].*)?$`),pattern:n,handler:e[n]})}return t}e.router=P,e.store=v,e.t=S,e.watch=g})(this.window=this.window||{});
|
||||
Generated
+102
-102
@@ -7,7 +7,7 @@
|
||||
"name": "PocketBase Superuser UI",
|
||||
"dependencies": {
|
||||
"leaflet": "^1.9.4",
|
||||
"pocketbase": "^0.26.8"
|
||||
"pocketbase": "^0.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dprint": "^0.52.0",
|
||||
@@ -190,9 +190,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
|
||||
"integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -202,9 +202,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
|
||||
"integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -243,9 +243,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.124.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
|
||||
"integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==",
|
||||
"version": "0.132.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz",
|
||||
"integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
@@ -253,9 +253,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz",
|
||||
"integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -270,9 +270,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz",
|
||||
"integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -287,9 +287,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz",
|
||||
"integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -304,9 +304,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz",
|
||||
"integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -321,9 +321,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz",
|
||||
"integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -338,9 +338,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz",
|
||||
"integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -358,9 +358,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz",
|
||||
"integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -378,9 +378,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz",
|
||||
"integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -398,9 +398,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz",
|
||||
"integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -418,9 +418,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz",
|
||||
"integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -438,9 +438,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz",
|
||||
"integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -458,9 +458,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz",
|
||||
"integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -475,9 +475,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz",
|
||||
"integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
@@ -485,18 +485,18 @@
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "1.9.2",
|
||||
"@emnapi/runtime": "1.9.2",
|
||||
"@napi-rs/wasm-runtime": "^1.1.3"
|
||||
"@emnapi/core": "1.10.0",
|
||||
"@emnapi/runtime": "1.10.0",
|
||||
"@napi-rs/wasm-runtime": "^1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz",
|
||||
"integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -511,9 +511,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz",
|
||||
"integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -528,16 +528,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
"integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
|
||||
"version": "0.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz",
|
||||
"integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -892,9 +892,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"version": "3.3.12",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -931,15 +931,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/pocketbase": {
|
||||
"version": "0.26.8",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.26.8.tgz",
|
||||
"integrity": "sha512-aQ/ewvS7ncvAE8wxoW10iAZu6ElgbeFpBhKPnCfvRovNzm2gW8u/sQNPGN6vNgVEagz44kK//C61oKjfa+7Low==",
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.27.0.tgz",
|
||||
"integrity": "sha512-K5N6d93UP/BNMbMnlZ6BUfy9VPCIvLyqhJFOsNI8OsZwzvKWEAfyD36boi5K4ECIOl5HMlo0TzuaeGdKpMwizQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz",
|
||||
"integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==",
|
||||
"version": "8.5.15",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -957,7 +957,7 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"nanoid": "^3.3.12",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
@@ -966,14 +966,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz",
|
||||
"integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz",
|
||||
"integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.124.0",
|
||||
"@rolldown/pluginutils": "1.0.0-rc.15"
|
||||
"@oxc-project/types": "=0.132.0",
|
||||
"@rolldown/pluginutils": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"rolldown": "bin/cli.mjs"
|
||||
@@ -982,21 +982,21 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-android-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-darwin-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-darwin-x64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-freebsd-x64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15",
|
||||
"@rolldown/binding-linux-x64-musl": "1.0.0-rc.15",
|
||||
"@rolldown/binding-openharmony-arm64": "1.0.0-rc.15",
|
||||
"@rolldown/binding-wasm32-wasi": "1.0.0-rc.15",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15"
|
||||
"@rolldown/binding-android-arm64": "1.0.2",
|
||||
"@rolldown/binding-darwin-arm64": "1.0.2",
|
||||
"@rolldown/binding-darwin-x64": "1.0.2",
|
||||
"@rolldown/binding-freebsd-x64": "1.0.2",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.0.2",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.0.2",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.0.2",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.0.2",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.0.2",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.0.2",
|
||||
"@rolldown/binding-linux-x64-musl": "1.0.2",
|
||||
"@rolldown/binding-openharmony-arm64": "1.0.2",
|
||||
"@rolldown/binding-wasm32-wasi": "1.0.2",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.0.2",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
@@ -1035,17 +1035,17 @@
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.0.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz",
|
||||
"integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==",
|
||||
"version": "8.0.14",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz",
|
||||
"integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.32.0",
|
||||
"picomatch": "^4.0.4",
|
||||
"postcss": "^8.5.8",
|
||||
"rolldown": "1.0.0-rc.15",
|
||||
"tinyglobby": "^0.2.15"
|
||||
"postcss": "^8.5.15",
|
||||
"rolldown": "1.0.2",
|
||||
"tinyglobby": "^0.2.16"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -1061,7 +1061,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^20.19.0 || >=22.12.0",
|
||||
"@vitejs/devtools": "^0.1.0",
|
||||
"@vitejs/devtools": "^0.1.18",
|
||||
"esbuild": "^0.27.0 || ^0.28.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "^4.0.0",
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"leaflet": "^1.9.4",
|
||||
"pocketbase": "^0.26.8"
|
||||
"pocketbase": "^0.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dprint": "^0.52.0",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
(function(e){let t,n=new Set,r=new Map,i=[],a,o=Symbol(),s=Symbol(),c=Symbol(),l=Symbol(),u=Symbol(),d=Symbol(),f=Symbol(),p=Symbol(),m=Symbol(),h=Symbol();function g(e,n){let d={[o]:`_`+Math.random(),[p]:e,[m]:n};return r.set(d[o],d),d.run=()=>{let e;t&&(e=t,d[s]=t[o],t[c]=t[c]||[],t[c].push(d[o])),d[l]?.forEach(e=>{e.delete(d[o])}),t=d;let n=d[p](d[h],d);d[m]&&(t=null,d[m](n,d[h],d)),d[h]=n,t=e},d.unwatch=function(){d[u]=1,i.push(d[o]),a&&clearTimeout(a),a=setTimeout(()=>{for(let e of i)_(e);i=[],a=null},50)},d.run(),d}function _(e){let t=r.get(e);if(t?.[d]?.(),t?.[c]){for(let e of t[c])_(e);t[s]=null,t[c]=null}if(t?.[l]){for(let n of t[l])n.delete(e);t[l]=null}r.delete(e)}function v(e){return y(e,new Map)}function y(e,n){let r=typeof e==`object`&&!Array.isArray(e)?Object.getOwnPropertyDescriptors(e):{};return new Proxy(e,{get(e,i,a){if(typeof i==`symbol`)return e[i];if(i==`__raw`)return e;if(r[i]?.get&&!e[s]){let n=i;if(i=`@@`+i,!t){let t=r[n].get.call(e);return r[n]._watcher&&(a[i]=t),t}let s=t[o];if(r[n]._refs=r[n]._refs||new Set,!r[n]._refs.has(s)){r[n]._refs.add(s);let e=t[d];t[d]=()=>{e?.(),r[n]._refs.delete(s),!r[n]._refs.size&&r[n]._watcher&&_(r[n]._watcher[o])}}if(!r[n]._watcher){let o=t;t=null;let s=g(r[n].get.bind(e),t=>{e.hasOwnProperty(i)?a[i]=t:Object.defineProperty(e,i,{writable:!0,enumerable:!1,value:t})});r[n]._watcher=s,s[d]=()=>{r[n]._watcher=null},t=o}}let c=e?.[i];if(typeof c==`function`)return c;if(typeof c==`object`&&c&&!c[s]&&(c.constructor?.name==`Object`||c.constructor?.name==`Array`||c.constructor?.name==null)&&(c[s]=[a,i],c=y(c,n),e[i]=c),t){let r=b(e,i),a=t[o],s=[r];t[l]=t[l]||new Set;for(let e of s){let r=n.get(e);r||(r=new Set,n.set(e,r)),r.add(a),t[l].add(r)}}return c},set(e,t,r){if(typeof t==`symbol`)return e[t]=r,!0;let i=e[t];i?.[s]&&(i[f]=!0),r?.[s]&&Array.isArray(e)&&!isNaN(t)&&(r[s][1]=t);let a=!1;return i===void 0&&!e.hasOwnProperty(t)&&(a=!0),e[t]=r,a&&x(e,`toJSON`,n),(r!==i||t===`length`)&&x(e,t,n),!0},deleteProperty(e,t){if(typeof t!=`symbol`){x(e,t,n);let r=b(e,t);for(let e of n)(e[0]==r||e[0].startsWith(r+`/`))&&n.delete(e[0])}return delete e[t]}})}function b(e,t){let n=t,r=e?.[s];for(;r;)n=r[1]+`/`+n,r=r[0][s];return n}function x(e,t,i){let a=b(e,t),o=i.get(a);if(o)for(let e of o)n.delete(e),n.add(e),n.size==1&&queueMicrotask(()=>{let e={},t;for(let i of n)if(t=r.get(i),!(!t||t[u])&&!(t[s]&&n.has(t[s]))){if(e[i]=(e[i]||0)+1,e[i]>250){console.warn(`Possible infinite loop for watcher `+i+`:`,`
|
||||
(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});let t,n=new Set,r=new Map,i=[],a,o=Symbol(),s=Symbol(),c=Symbol(),l=Symbol(),u=Symbol(),d=Symbol(),f=Symbol(),p=Symbol(),m=Symbol(),h=Symbol();function g(e,n){let d={[o]:`_`+Math.random(),[p]:e,[m]:n};return r.set(d[o],d),d.run=()=>{let e;t&&(e=t,d[s]=t[o],t[c]=t[c]||[],t[c].push(d[o])),d[l]?.forEach(e=>{e.delete(d[o])}),t=d;let n=d[p](d[h],d);d[m]&&(t=null,d[m](n,d[h],d)),d[h]=n,t=e},d.unwatch=function(){d[u]=1,i.push(d[o]),a&&clearTimeout(a),a=setTimeout(()=>{for(let e of i)_(e);i=[],a=null},50)},d.run(),d}function _(e){let t=r.get(e);if(t?.[d]?.(),t?.[c]){for(let e of t[c])_(e);t[s]=null,t[c]=null}if(t?.[l]){for(let n of t[l])n.delete(e);t[l]=null}r.delete(e)}function v(e){return y(e,new Map)}function y(e,n){let r=typeof e==`object`&&!Array.isArray(e)?Object.getOwnPropertyDescriptors(e):{};return new Proxy(e,{get(e,i,a){if(typeof i==`symbol`)return e[i];if(i==`__raw`)return e;if(r[i]?.get&&!e[s]){let n=i;if(i=`@@`+i,!t){let t=r[n].get.call(e);return r[n]._watcher&&(a[i]=t),t}let s=t[o];if(r[n]._refs=r[n]._refs||new Set,!r[n]._refs.has(s)){r[n]._refs.add(s);let e=t[d];t[d]=()=>{e?.(),r[n]._refs.delete(s),!r[n]._refs.size&&r[n]._watcher&&_(r[n]._watcher[o])}}if(!r[n]._watcher){let o=t;t=null;let s=g(r[n].get.bind(e),t=>{e.hasOwnProperty(i)?a[i]=t:Object.defineProperty(e,i,{writable:!0,enumerable:!1,value:t})});r[n]._watcher=s,s[d]=()=>{r[n]._watcher=null},t=o}}let c=e?.[i];if(typeof c==`function`)return c;if(typeof c==`object`&&c&&!c[s]&&(c.constructor?.name==`Object`||c.constructor?.name==`Array`||c.constructor?.name==null)&&(c[s]=[a,i],c=y(c,n),e[i]=c),t){let r=b(e,i),a=t[o],s=[r];t[l]=t[l]||new Set;for(let e of s){let r=n.get(e);r||(r=new Set,n.set(e,r)),r.add(a),t[l].add(r)}}return c},set(e,t,r){if(typeof t==`symbol`)return e[t]=r,!0;let i=e[t];i?.[s]&&(i[f]=!0),r?.[s]&&Array.isArray(e)&&!isNaN(t)&&(r[s][1]=t);let a=!1;return i===void 0&&!e.hasOwnProperty(t)&&(a=!0),e[t]=r,a&&x(e,`toJSON`,n),(r!==i||t===`length`)&&x(e,t,n),!0},deleteProperty(e,t){if(typeof t!=`symbol`){x(e,t,n);let r=b(e,t);for(let e of n)(e[0]==r||e[0].startsWith(r+`/`))&&n.delete(e[0])}return delete e[t]}})}function b(e,t){let n=t,r=e?.[s];for(;r;)n=r[1]+`/`+n,r=r[0][s];return n}function x(e,t,i){let a=b(e,t),o=i.get(a);if(o)for(let e of o)n.delete(e),n.add(e),n.size==1&&queueMicrotask(()=>{let e={},t;for(let i of n)if(t=r.get(i),!(!t||t[u])&&!(t[s]&&n.has(t[s]))){if(e[i]=(e[i]||0)+1,e[i]>250){console.warn(`Possible infinite loop for watcher `+i+`:`,`
|
||||
watch(`+t[p]?.toString()+(t[m]?`, `+t[m].toString():``)+`)`);continue}t.run()}n.clear()})}let S=new Proxy({},{get(e,t){return function(){return w(),O.call(void 0,t,...arguments)}}}),C=!1;function w(){if(C)return;C=!0;function e(t,n){for(let r of n)r[t]&&r[t](r),r.childNodes&&e(t,r.childNodes)}new MutationObserver(t=>{for(let n of t)e(`onmount`,n.addedNodes),e(`onunmount`,n.removedNodes)}).observe(document,{childList:!0,subtree:!0})}let T=Symbol(),E=Symbol(),D=Symbol();function O(e,t={},...n){let r=document.createElement(e);if(t)for(let e in t){let n=t[e],i=!1;e.length>5&&e.startsWith(`html-`)&&(i=!0,e=e.substring(5)),n===void 0?r.removeAttribute(e):typeof n!=`function`||e.length>2&&e.startsWith(`on`)?i?r.setAttribute(e,n):r[e]=n:(e==`rid`&&console.warn(`rid is provided as reactive function and will not have effect for `+r+`.
|
||||
Consider using a plain number, string or object/array reference instead.`),r[T]=r[T]||[],r[T].push(()=>{if(!r)return;let t=n(r,e);t===void 0?(r[e]!==void 0&&(r[e]=void 0),r.removeAttribute(e)):i?r.setAttribute(e,t):r[e]=t}))}let i=r.onmount;r.onmount=()=>{if(!r[D]){if(r[D]=!0,r[T]){r[E]=r[E]||[];for(let e of r[T])r[E].push(g(e))}i?.(r)}};let a=r.onunmount;return r.onunmount=()=>{if(r[D]){if(r[D]=!1,r[E])for(let e of r[E])e.unwatch();r[E]=null,a?.(r)}},k(r,n),r}function k(e,t){t=N(t);for(let n of t)if(typeof n==`function`)A(e,n);else{let t=P(n);Array.isArray(t)?k(e,t):t&&e.appendChild(t)}}function A(e,t){let n=document.createComment(``);e.appendChild(n);let r=[],i=new Map,a=e.moveBefore||e.insertBefore;e[T]=e[T]||[],e[T].push(()=>{if(!e)return;let o=N(t(e)),s=o.length,c=new Map;if(!r?.length){let t=document.createDocumentFragment();for(let e=0;e<s;e++){o[e]=P(o[e]),t.appendChild(o[e]);let n=o[e].rid;n!==void 0&&(c.has(n)?console.warn(`Duplicated rid:`,n,o[e]):c.set(n,e))}e.insertBefore(t,n),t=null,r=o,i=c;return}let l=[],u=[],d=new Set,f=[];for(let e=0;e<s;e++){o[e]=P(o[e]);let t=o[e].rid;if(t!==void 0){c.has(t)?console.warn(`Duplicated rid:`,t,o[e]):c.set(t,e);let n=i.get(t);if(n>=0){d.add(r[n]),o[e]=r[n],f.push(n);continue}}u.push({child:o[e],prev:o[e-1]})}let p=j(f);f.length!=p.length&&f.forEach((e,t)=>{p.has(e)||l.push({child:r[e],targetPos:t})});let m,h,g;for(let t of l)m=r.findIndex(e=>e===t.child),h=t.targetPos,m<t.targetPos&&(h=t.targetPos+1),g=r[h]||n,M(r,m,t.targetPos),a.call(e,t.child,g);for(let e of u)e.prev?e.prev.after(e.child):(r[0]||n).before(e.child);for(let e=0;e<r.length;e++)d.has(r[e])||r[e].remove?.();r=o,i=c,o=null,c=null,d=null,l=null,u=null})}function j(e){let t=[],n=[];for(let r=0;r<e.length;r++){let i=e[r],a=0,o=0,s=t.length;for(;a<s;)o=Math.floor((a+s)/2),e[t[o]]>=i?s=o:a=o+1;a>0&&(n[r]=t[a-1]),t[a]=r}let r=new Set,i=t[t.length-1];for(;i!==void 0;)r.add(e[i]),i=n[i];return r}function M(e,t,n){if(t==n)return e;let r=t>n?-1:1,i=e[t];for(let i=t;i!=n;i+=r)e[i]=e[i+r];e[n]=i}function N(e){return e==null?[]:Array.isArray(e)?e:[e]}function P(e){return typeof e==`string`||typeof e==`number`||typeof e==`boolean`?document.createTextNode(e):e?.__raw===void 0?e:e.__raw}function F(e,t={fallbackPath:`#/`,transition:!0}){let n=L(e),r,i=()=>{let e=window.location.hash,i=I(n,e);if(!i){if(t.fallbackPath!=e){window.location.hash=t.fallbackPath;return}console.warn(`missing route:`,e);return}let a=async()=>{try{await r?.(),r=await i.handler(i)}catch(e){console.warn(`route navigation failed:`,e)}};t.transition&&document.startViewTransition?document.startViewTransition(a):a()};return window.addEventListener(`hashchange`,i),i(),()=>{window.removeEventListener(`hashchange`,i)}}function I(e,t){for(let n of e){let e=t.match(n.regex);if(!e)continue;let r={},i=t.split(`?`)?.[1];if(i){let e=new URLSearchParams(i);for(let[t,n]of e.entries())Array.isArray(r[t])||(r[t]=r[t]?[r[t]]:[]),r[t].push(n)}return Object.assign({path:t,query:r,params:e.groups||{}},n)}}function L(e){let t=[];for(let n in e){let r=n.split(`/`);for(let e in r)r[e].length>2&&r[e].startsWith(`{`)&&r[e].endsWith(`}`)?r[e]=`(?<`+r[e].substring(1,r[e].length-1)+`>[^\\/#?]+)`:r[e]=RegExp.escape(r[e]);t.push({regex:RegExp(`^`+r.join(`\\/`)+`(?:[?#].*)?$`),pattern:n,handler:e[n]})}return t}e.router=F,e.store=v,e.t=S,e.watch=g})(this.window=this.window||{});
|
||||
Consider using a plain number, string or object/array reference instead.`),r[T]=r[T]||[],r[T].push(()=>{if(!r)return;let t=n(r,e);t===void 0?(r[e]!==void 0&&(r[e]=void 0),r.removeAttribute(e)):i?r.setAttribute(e,t):r[e]=t}))}let i=r.onmount;r.onmount=()=>{if(!r[D]){if(r[D]=!0,r[T]){r[E]=r[E]||[];for(let e of r[T])r[E].push(g(e))}i?.(r)}};let a=r.onunmount;return r.onunmount=()=>{if(r[D]){if(r[D]=!1,r[E])for(let e of r[E])e.unwatch();r[E]=null,a?.(r)}},k(r,n),r}function k(e,t){t=M(t);for(let n of t)if(typeof n==`function`)A(e,n);else{let t=N(n);Array.isArray(t)?k(e,t):t&&e.appendChild(t)}}function A(e,t){let n=document.createComment(``);e.appendChild(n);let r=[],i=new Map,a=e.moveBefore||e.insertBefore;e[T]=e[T]||[],e[T].push(()=>{if(!e)return;let o=M(t(e)),s=o.length,c=new Map;if(!r?.length){let t=document.createDocumentFragment();for(let e=0;e<s;e++){o[e]=N(o[e],!0),t.appendChild(o[e]);let n=o[e].rid;n!==void 0&&(c.has(n)?console.warn(`Duplicated rid:`,n,o[e]):c.set(n,e))}e.insertBefore(t,n),t=null,r=o,i=c;return}let l=[],u=[],d=new Set,f=[];for(let e=0;e<s;e++){o[e]=N(o[e],!0);let t=o[e].rid;if(t!==void 0){c.has(t)?console.warn(`Duplicated rid:`,t,o[e]):c.set(t,e);let n=i.get(t);if(n>=0){d.add(r[n]),o[e]=r[n],f.push(n);continue}}u.push({child:o[e],prev:o[e-1]})}let p=j(f);f.length!=p.length&&f.forEach((e,t)=>{p.has(e)||l.push({newPos:t,child:r[e],insertBefore:r[f[t+1]]||n})});for(let t=l.length-1;t>=0;t--)a.call(e,l[t].child,l[t].insertBefore);for(let e of u)e.prev?e.prev.after(e.child):(r[f[0]||0]||n).before(e.child);for(let e=0;e<r.length;e++)d.has(r[e])||r[e].remove?.();r=o,i=c,o=null,c=null,d=null,l=null,u=null})}function j(e){let t=[],n=[];for(let r=0;r<e.length;r++){let i=e[r],a=0,o=0,s=t.length;for(;a<s;)o=Math.floor((a+s)/2),e[t[o]]>=i?s=o:a=o+1;a>0&&(n[r]=t[a-1]),t[a]=r}let r=new Set,i=t[t.length-1];for(;i!==void 0;)r.add(e[i]),i=n[i];return r}function M(e){return e==null?[]:Array.isArray(e)?e:[e]}function N(e,t=!1){return t&&e==null&&(e=``),typeof e==`string`||typeof e==`number`||typeof e==`boolean`?document.createTextNode(e):e?.__raw===void 0?e:e.__raw}function P(e,t={fallbackPath:`#/`,transition:!0}){let n=I(e),r,i=()=>{let e=window.location.hash,i=F(n,e);if(!i){if(t.fallbackPath!=e){window.location.hash=t.fallbackPath;return}console.warn(`missing route:`,e);return}let a=async()=>{try{await r?.(),r=await i.handler(i)}catch(e){console.warn(`route navigation failed:`,e)}};t.transition&&document.startViewTransition?document.startViewTransition(a):a()};return window.addEventListener(`hashchange`,i),i(),()=>{window.removeEventListener(`hashchange`,i)}}function F(e,t){for(let n of e){let e=t.match(n.regex);if(!e)continue;let r={},i=t.split(`?`)?.[1];if(i){let e=new URLSearchParams(i);for(let[t,n]of e.entries())Array.isArray(r[t])||(r[t]=r[t]?[r[t]]:[]),r[t].push(n)}return Object.assign({path:t,query:r,params:e.groups||{}},n)}}function I(e){let t=[];for(let n in e){let r=n.split(`/`);for(let e in r)r[e].length>2&&r[e].startsWith(`{`)&&r[e].endsWith(`}`)?r[e]=`(?<`+r[e].substring(1,r[e].length-1)+`>[^\\/#?]+)`:r[e]=RegExp.escape(r[e]);t.push({regex:RegExp(`^`+r.join(`\\/`)+`(?:[?#].*)?$`),pattern:n,handler:e[n]})}return t}e.router=P,e.store=v,e.t=S,e.watch=g})(this.window=this.window||{});
|
||||
@@ -31,13 +31,13 @@ export function docsListAuthMethods(collection) {
|
||||
|
||||
try {
|
||||
data.authMethods = await app.pb.collection(collection.name).listAuthMethods();
|
||||
data.isLoading = false;
|
||||
} catch (err) {
|
||||
if (err.isAbort) {
|
||||
app.pb.checkApiError(err);
|
||||
if (!err?.isAbort) {
|
||||
app.checkApiError(err);
|
||||
data.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
data.isLoading = false;
|
||||
}
|
||||
|
||||
return t.div(
|
||||
|
||||
@@ -99,7 +99,7 @@ async function authWithPassword(data) {
|
||||
await app.pb.collection("_superusers").authWithPassword(data.identity, data.password);
|
||||
app.toasts.removeAll();
|
||||
app.store.errors = null;
|
||||
app.utils.toRememberedPath();
|
||||
window.location.hash = "#/";
|
||||
} catch (err) {
|
||||
if (err.status == 401) {
|
||||
data.mfaId = err.response.mfaId;
|
||||
@@ -304,7 +304,7 @@ async function authWithOTP(data) {
|
||||
});
|
||||
app.toasts.removeAll();
|
||||
app.store.errors = null;
|
||||
app.utils.toRememberedPath();
|
||||
window.location.hash = "#/";
|
||||
} catch (err) {
|
||||
app.checkApiError(err);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,9 @@ window.app.components = window.app.components || {};
|
||||
* @example
|
||||
* ```js
|
||||
* app.components.colorPicker({
|
||||
* value: () => data.color,
|
||||
* predefinedColors: ["#ff0000", "#123456"],
|
||||
* value: () => data.color,
|
||||
* onchange: (newColor) => data.color = newColor,
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
|
||||
@@ -259,3 +259,11 @@ function findLargestZIndexModal(excludeEl) {
|
||||
|
||||
return largest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current top-most open modal element.
|
||||
* @return {Element|undefined}
|
||||
*/
|
||||
window.app.modals.getTop = function() {
|
||||
return findLargestZIndexModal();
|
||||
};
|
||||
@@ -51,16 +51,16 @@ window.app.components.refreshButton = function(propsArg = {}) {
|
||||
props.onclick(e);
|
||||
}
|
||||
|
||||
btn.classList.add("rotate");
|
||||
btn.dataset.rotate = true;
|
||||
btn.addEventListener("animationend", () => {
|
||||
btn.classList.remove("rotate");
|
||||
btn.dataset.rotate = false;
|
||||
});
|
||||
|
||||
// fallback
|
||||
clearTimeout(refreshTimeoutId);
|
||||
refreshTimeoutId = setTimeout(() => {
|
||||
clearTimeout(refreshTimeoutId);
|
||||
btn.classList.remove("rotate");
|
||||
btn.dataset.rotate = false;
|
||||
}, 500);
|
||||
},
|
||||
},
|
||||
|
||||
+18
-2
@@ -152,7 +152,15 @@ window.app.components.select = function(propsArg = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
const noItemsFoundElem = t.div({ className: "txt-hint txt-center m-0 p-5", hidden: true }, props.noItemsFoundText);
|
||||
const noItemsFoundElem = t.div(
|
||||
{ className: "txt-hint txt-center m-0 p-5", hidden: true },
|
||||
(el) => {
|
||||
if (typeof props.noItemsFoundText == "function") {
|
||||
return props.noItemsFoundText(el);
|
||||
}
|
||||
return props.noItemsFoundText;
|
||||
},
|
||||
);
|
||||
|
||||
async function toggleNoItemsFoundElem() {
|
||||
if (!dropdown) {
|
||||
@@ -236,7 +244,15 @@ window.app.components.select = function(propsArg = {}) {
|
||||
},
|
||||
() => {
|
||||
if (!internalData.selected.length) {
|
||||
return t.span({ rid: "selected-placeholder", className: "placeholder" }, () => props.placeholder);
|
||||
return t.span(
|
||||
{ rid: "selected-placeholder", className: "placeholder" },
|
||||
(el) => {
|
||||
if (typeof props.placeholder == "function") {
|
||||
return props.placeholder(el);
|
||||
}
|
||||
return props.placeholder;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return internalData.selected.map((opt) => {
|
||||
|
||||
@@ -265,8 +265,13 @@ function collectionUpsertModal(rawCollection, modalSettings) {
|
||||
className: "modal collection-upsert-modal",
|
||||
inert: () => data.isSaving,
|
||||
onkeydown: (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.code == "KeyS") {
|
||||
if (
|
||||
(e.ctrlKey || e.metaKey)
|
||||
&& e.code == "KeyS"
|
||||
&& app.modals.getTop() === modal
|
||||
) {
|
||||
e.preventDefault();
|
||||
|
||||
// temp blur any active input to make sure that onchange/blur events are fired
|
||||
const input = document.activeElement;
|
||||
input?.blur();
|
||||
|
||||
@@ -35,10 +35,7 @@ export function collectionViewQueryTab(upsertData) {
|
||||
}
|
||||
|
||||
try {
|
||||
// @todo replace with SDK method
|
||||
const result = await app.pb.send("/api/collections/meta/dry-run-view", {
|
||||
method: "POST",
|
||||
body: { "query": query },
|
||||
const result = await app.pb.collections.dryRunViewQuery(query, {
|
||||
requestKey: TEST_REQUEST_KEY,
|
||||
});
|
||||
|
||||
|
||||
@@ -229,3 +229,6 @@ window.app.oauth2.oidc = function(providerInfo, namePrefix, data) {
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
window.app.oauth2.oidc2 = window.app.oauth2.oidc;
|
||||
window.app.oauth2.oidc3 = window.app.oauth2.oidc;
|
||||
@@ -14,6 +14,7 @@ export function pageCollections(route) {
|
||||
|
||||
const pageData = store({
|
||||
reset: null,
|
||||
suggestReset: false,
|
||||
activeRecordIdOrModel: route.query[RECORD_QUERY_KEY]?.[0] || "",
|
||||
sort: route.query[SORT_QUERY_KEY]?.[0] || "",
|
||||
filter: route.query[FILTER_QUERY_KEY]?.[0] || "",
|
||||
@@ -148,7 +149,7 @@ export function pageCollections(route) {
|
||||
];
|
||||
|
||||
const documentEvents = {
|
||||
"record:save": (e) => {
|
||||
"record:create": (e) => {
|
||||
if (e.detail.collectionId != app.store.activeCollection?.id) {
|
||||
return;
|
||||
}
|
||||
@@ -196,7 +197,7 @@ export function pageCollections(route) {
|
||||
t.div(
|
||||
{ className: "page-content full-height" },
|
||||
t.header(
|
||||
{ className: "page-header compact flex-nowrap" },
|
||||
{ className: "page-header flex-nowrap" },
|
||||
t.nav(
|
||||
{ className: "breadcrumbs" },
|
||||
t.div(null, "Collections"),
|
||||
@@ -238,6 +239,9 @@ export function pageCollections(route) {
|
||||
),
|
||||
app.components.refreshButton({
|
||||
onclick: () => refreshRecordsList(),
|
||||
className: () =>
|
||||
`btn transparent circle rotate-btn ${pageData.suggestReset ? "warning" : "secondary"}`,
|
||||
tooltip: () => `Refresh${pageData.suggestReset ? "\n(list changed)" : ""}`,
|
||||
}),
|
||||
),
|
||||
t.div(
|
||||
@@ -303,21 +307,32 @@ export function pageCollections(route) {
|
||||
value: () => pageData.filter,
|
||||
onsubmit: (newFilter) => (pageData.filter = newFilter),
|
||||
}),
|
||||
app.components.recordsList({
|
||||
className: "m-t-sm",
|
||||
reset: () => pageData.reset,
|
||||
hidden: () => !app.store.activeCollection?.id,
|
||||
collection: () => app.store.activeCollection,
|
||||
filter: () => pageData.filter,
|
||||
sort: () => pageData.sort,
|
||||
onselect: (record) => {
|
||||
pageData.activeRecordIdOrModel = record;
|
||||
},
|
||||
onchange: (newFilter, newSort) => {
|
||||
pageData.filter = newFilter;
|
||||
pageData.sort = newSort;
|
||||
},
|
||||
}),
|
||||
() => {
|
||||
// not using "hidden" attr to force clear the records list and to
|
||||
// minimize the flickering when navigating between collections
|
||||
if (!app.store.activeCollection?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
return app.components.recordsList({
|
||||
className: "m-t-sm",
|
||||
reset: () => pageData.reset,
|
||||
collection: () => app.store.activeCollection,
|
||||
filter: () => pageData.filter,
|
||||
sort: () => pageData.sort,
|
||||
onselect: (record) => {
|
||||
pageData.activeRecordIdOrModel = record;
|
||||
},
|
||||
onchange: (newFilter, newSort) => {
|
||||
pageData.filter = newFilter;
|
||||
pageData.sort = newSort;
|
||||
},
|
||||
suggestReset: () => pageData.suggestReset,
|
||||
onSuggestResetChange: (suggestReset) => {
|
||||
pageData.suggestReset = !!suggestReset;
|
||||
},
|
||||
});
|
||||
},
|
||||
t.footer(
|
||||
{ className: "page-footer" },
|
||||
t.span(
|
||||
|
||||
@@ -35,3 +35,4 @@
|
||||
@import "./collectionsOverview.css";
|
||||
@import "./erd.css";
|
||||
@import "./hideControls.css";
|
||||
@import "./sqlConsole.css";
|
||||
+2
-1
@@ -919,6 +919,7 @@ hr {
|
||||
.alert {
|
||||
display: block;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
align-content: center;
|
||||
min-height: var(--lgBtnHeight);
|
||||
padding: max(5px, calc(var(--inputPadding) - 5px)) max(10px, var(--inputPadding));
|
||||
@@ -975,7 +976,7 @@ hr {
|
||||
min-width: 0;
|
||||
min-height: 25px;
|
||||
font-weight: normal;
|
||||
word-break: normal;
|
||||
word-break: break-word;
|
||||
text-decoration: none;
|
||||
font-size: var(--smFontSize);
|
||||
line-height: calc(var(--smFontSize) + 2px);
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
left: 0;
|
||||
bottom: var(--smSpacing);
|
||||
right: 0;
|
||||
pointer-events: none;
|
||||
|
||||
.bulkbar {
|
||||
position: relative;
|
||||
@@ -62,6 +63,7 @@
|
||||
right: auto;
|
||||
margin: var(--smSpacing) auto;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
@starting-style {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
+5
-8
@@ -161,7 +161,6 @@ button {
|
||||
&.danger {
|
||||
color: var(--dangerColor);
|
||||
}
|
||||
&.accent,
|
||||
&.secondary,
|
||||
&.info,
|
||||
&.success,
|
||||
@@ -283,12 +282,14 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
&[data-rotate="true"],
|
||||
&.rotate {
|
||||
i {
|
||||
animation: halfRotate 300ms;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-loading="true"],
|
||||
&.loading {
|
||||
cursor: default;
|
||||
&::after {
|
||||
@@ -312,16 +313,11 @@ button {
|
||||
font-family: var(--iconFontFamily);
|
||||
will-change: transform;
|
||||
animation:
|
||||
scaleIn var(--animationSpeed),
|
||||
rotate 1.1s infinite linear;
|
||||
fadeIn var(--animationSpeed),
|
||||
rotate var(--loaderAnimationSpeed) infinite linear;
|
||||
}
|
||||
& > * {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
transition:
|
||||
color var(--animationSpeed),
|
||||
opacity var(--animationSpeed),
|
||||
transform var(--animationSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1130,6 +1126,7 @@ button {
|
||||
&:empty::before {
|
||||
content: attr(data-placeholder);
|
||||
color: var(--surfaceTxtDisabledColor);
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-10
@@ -174,16 +174,14 @@
|
||||
column-gap: var(--smSpacing);
|
||||
row-gap: calc(var(--smSpacing) - 5px);
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
margin: -5px 0 calc(var(--spacing) - 5px);
|
||||
min-height: var(--btnHeight);
|
||||
margin: -10px 0 calc(var(--spacing) - 10px);
|
||||
.searchbar {
|
||||
width: auto;
|
||||
flex-grow: 1;
|
||||
}
|
||||
&.compact {
|
||||
margin-top: -10px;
|
||||
margin-bottom: calc(var(--spacing) - 10px);
|
||||
}
|
||||
.page-header-secondary-btns,
|
||||
.page-header-primary-btns {
|
||||
display: inline-flex;
|
||||
@@ -206,10 +204,6 @@
|
||||
}
|
||||
@media (max-width: 950px) {
|
||||
gap: 5px;
|
||||
flex-grow: 1;
|
||||
.flex-nowrap & {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.btn {
|
||||
flex-grow: 1;
|
||||
flex-basis: 0;
|
||||
@@ -219,7 +213,6 @@
|
||||
}
|
||||
@media (max-width: 550px) {
|
||||
gap: 5px;
|
||||
flex-grow: 0;
|
||||
.api-preview-btn {
|
||||
display: none;
|
||||
}
|
||||
@@ -228,6 +221,7 @@
|
||||
width: auto;
|
||||
aspect-ratio: 1 / 1;
|
||||
border-radius: 50%;
|
||||
min-width: 0;
|
||||
i + .txt {
|
||||
display: none;
|
||||
}
|
||||
@@ -241,6 +235,7 @@
|
||||
align-items: center;
|
||||
width: auto;
|
||||
z-index: 10;
|
||||
gap: var(--smSpacing);
|
||||
color: var(--surfaceTxtHintColor);
|
||||
font-size: var(--smFontSize);
|
||||
line-height: var(--smLineHeight);
|
||||
|
||||
+11
-14
@@ -11,8 +11,9 @@
|
||||
line-height: 1;
|
||||
font-size: var(--fontSize);
|
||||
color: var(--surfaceTxtDisabledColor);
|
||||
background: var(--surfaceAlt1Color);
|
||||
background: var(--surfaceAlt2Color);
|
||||
border-radius: var(--borderRadius);
|
||||
transition: background var(--animationSpeed);
|
||||
&:empty {
|
||||
&::before {
|
||||
content: "\f452";
|
||||
@@ -23,6 +24,15 @@
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.handle:hover &,
|
||||
.handle:focus-visible & {
|
||||
background: var(--surfaceAlt3Color);
|
||||
}
|
||||
.handle.active &,
|
||||
.handle:active & {
|
||||
transition-duration: var(--activeAnimationSpeed);
|
||||
background: var(--surfaceAlt4Color);
|
||||
}
|
||||
}
|
||||
|
||||
.provider-card {
|
||||
@@ -66,22 +76,9 @@
|
||||
user-select: none;
|
||||
text-decoration: none;
|
||||
transition: background var(--animationSpeed);
|
||||
.provider-logo {
|
||||
transition: background var(--animationSpeed);
|
||||
}
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: var(--surfaceAlt1Color);
|
||||
.provider-logo {
|
||||
background: var(--surfaceAlt2Color);
|
||||
}
|
||||
}
|
||||
&.active,
|
||||
&:active {
|
||||
.provider-logo {
|
||||
transition-duration: var(--activeAnimationSpeed);
|
||||
background: var(--surfaceAlt3Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -197,8 +197,11 @@
|
||||
}
|
||||
|
||||
.field-type-bool {
|
||||
&.record-field-view .label {
|
||||
min-width: 48px;
|
||||
&.record-field-view {
|
||||
min-width: auto;
|
||||
.label {
|
||||
min-width: 48px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +233,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.more-marker {
|
||||
align-self: flex-end;
|
||||
color: var(--surfaceTxtHintColor);
|
||||
}
|
||||
|
||||
.field-type-select {
|
||||
.field-select-choices-input {
|
||||
> input {
|
||||
@@ -245,23 +253,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
.more-marker {
|
||||
align-self: flex-end;
|
||||
color: var(--surfaceTxtHintColor);
|
||||
}
|
||||
|
||||
.field-type-select,
|
||||
.field-type-file {
|
||||
&.record-field-view {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.field-type-editor {
|
||||
&.record-field-view {
|
||||
width: 100%;
|
||||
.record-summary & {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
&.record-field-input {
|
||||
/* fixed size to minimize flickering */
|
||||
@@ -289,6 +297,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
.field-type-autodate,
|
||||
.field-type-date {
|
||||
&.record-field-view {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden-field-blur {
|
||||
filter: blur(2.5px);
|
||||
transition: filter var(--animationSpeed);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
.field.sql-console-field {
|
||||
.input {
|
||||
resize: vertical;
|
||||
height: 250px;
|
||||
min-height: 150px;
|
||||
max-height: 400px;
|
||||
|
||||
@media (max-height: 650px) {
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sql-console-history-dropdown {
|
||||
width: 450px;
|
||||
&.no-items {
|
||||
width: 300px;
|
||||
}
|
||||
.dropdown-item {
|
||||
gap: 5px;
|
||||
padding: 5px;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.2;
|
||||
font-family: var(--monospaceFontFamily);
|
||||
.query {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-5
@@ -304,6 +304,10 @@ table {
|
||||
margin-right: calc(-1 * var(--spacing));
|
||||
container-type: inline-size scroll-state;
|
||||
|
||||
.label {
|
||||
word-break: normal; /* container has overflow so there is no need to unnecessery break characters */
|
||||
}
|
||||
|
||||
.load-more-btn,
|
||||
.sticky-content {
|
||||
position: sticky;
|
||||
@@ -317,8 +321,21 @@ table {
|
||||
min-width: 900px;
|
||||
border-radius: 0;
|
||||
}
|
||||
tr {
|
||||
animation: fadeIn var(--animationSpeed);
|
||||
table > :first-child > tr:first-child {
|
||||
> :first-child {
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
> :last-child {
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
table > :last-child > tr:last-child {
|
||||
> :first-child {
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
> :last-child {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
td,
|
||||
th {
|
||||
@@ -342,6 +359,7 @@ table {
|
||||
td,
|
||||
th {
|
||||
height: 59px;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,9 +422,6 @@ table {
|
||||
.col-copy {
|
||||
display: none;
|
||||
}
|
||||
.label {
|
||||
word-break: break-word;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
display: block;
|
||||
@@ -493,6 +508,9 @@ table {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
.label {
|
||||
word-break: break-word;
|
||||
}
|
||||
}
|
||||
.page-table-wrapper .responsive-table {
|
||||
border: 0;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
line-height: var(--smLineHeight);
|
||||
font-family: var(--baseFontFamily);
|
||||
scrollbar-width: thin;
|
||||
margin: 3px;
|
||||
padding: 3px 5px;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
|
||||
+6
-6
@@ -126,7 +126,7 @@
|
||||
--lgWrapperWidth: 1150px;
|
||||
|
||||
--loaderIcon: "\eec4";
|
||||
--loaderAnimationSpeed: 1.2s;
|
||||
--loaderAnimationSpeed: 1.1s;
|
||||
|
||||
--animationSpeed: 150ms;
|
||||
--activeAnimationSpeed: 70ms;
|
||||
@@ -138,7 +138,7 @@
|
||||
/* dark */
|
||||
[data-color-scheme="dark"],
|
||||
[data-color-scheme="dark"] .dropdown {
|
||||
--surfaceColor: #1f1f1f;
|
||||
--surfaceColor: #1c1c1c;
|
||||
--surfaceAlt1Color: color-mix(in srgb, var(--surfaceColor), white 2%);
|
||||
--surfaceAlt2Color: color-mix(in srgb, var(--surfaceColor), white 7%);
|
||||
--surfaceAlt3Color: color-mix(in srgb, var(--surfaceColor), white 12%);
|
||||
@@ -155,10 +155,10 @@
|
||||
--secondaryAlt1Color: var(--surfaceAlt3Color);
|
||||
--secondaryAlt2Color: var(--surfaceAlt4Color);
|
||||
|
||||
--primaryColor: #161616;
|
||||
--primaryColor: #121212;
|
||||
--primaryTxtColor: var(--surfaceTxtColor);
|
||||
--primaryAlt1Color: color-mix(in srgb, var(--primaryColor), white 1%);
|
||||
--primaryAlt2Color: color-mix(in srgb, var(--primaryColor), white 1.5%);
|
||||
--primaryAlt1Color: color-mix(in srgb, var(--primaryColor), black 12%);
|
||||
--primaryAlt2Color: color-mix(in srgb, var(--primaryColor), black 25%);
|
||||
--primaryTxtHintColor: color-mix(in srgb, var(--primaryTxtColor), transparent 35%);
|
||||
--primaryTxtDisabledColor: color-mix(in srgb, var(--primaryTxtColor), transparent 50%);
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
--inputBorderColor: var(--surfaceAlt4Color);
|
||||
scrollbar-color: var(--surfaceAlt5Color) transparent;
|
||||
|
||||
--modalOverlayColor: rgb(0,0,0, 0.45);
|
||||
--modalOverlayColor: rgb(18,18,18, 0.5);
|
||||
--tooltipSurfaceColor: rgb(22,22,22, 0.9);
|
||||
|
||||
--boxShadow: 0px 8px 5px -5px rgba(0,0,0, 0.3);
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
// get fieldIndex: int/-1,
|
||||
// get originalField: undefined
|
||||
// }
|
||||
export function settings(data) {
|
||||
export function settings(props) {
|
||||
const uniqueId = "f_" + app.utils.randomString();
|
||||
|
||||
return app.components.fieldSettings(data, {
|
||||
return app.components.fieldSettings(props, {
|
||||
content: () =>
|
||||
t.div(
|
||||
{ className: "grid sm" },
|
||||
@@ -20,9 +20,9 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "text",
|
||||
id: uniqueId + ".help",
|
||||
name: () => `fields.${data.fieldIndex}.help`,
|
||||
value: () => data.field.help || "",
|
||||
oninput: (e) => (data.field.help = e.target.value),
|
||||
name: () => `fields.${props.fieldIndex}.help`,
|
||||
value: () => props.field.help || "",
|
||||
oninput: (e) => (props.field.help = e.target.value),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -34,9 +34,9 @@ export function settings(data) {
|
||||
className: "sm",
|
||||
type: "checkbox",
|
||||
id: uniqueId + ".required",
|
||||
name: () => `fields.${data.fieldIndex}.required`,
|
||||
checked: () => !!data.field.required,
|
||||
onchange: (e) => (data.field.required = e.target.checked),
|
||||
name: () => `fields.${props.fieldIndex}.required`,
|
||||
checked: () => !!props.field.required,
|
||||
onchange: (e) => (props.field.required = e.target.checked),
|
||||
}),
|
||||
t.label(
|
||||
{ htmlFor: uniqueId + ".required" },
|
||||
|
||||
@@ -26,6 +26,15 @@ export function input(props) {
|
||||
required: () => props.field.required,
|
||||
value: () => app.utils.toDatetimeLocalInputValue(props.record[props.field.name]),
|
||||
onchange: (e) => {
|
||||
// don't clear until blur
|
||||
// https://github.com/pocketbase/pocketbase/issues/7726
|
||||
if (!e.target.value) {
|
||||
return;
|
||||
}
|
||||
props.record[props.field.name] = app.utils.toRFC3339Datetime(e.target.value);
|
||||
},
|
||||
onblur: (e) => {
|
||||
props.record[props.field.name] = null; // change to an invalid value to force reactive update
|
||||
props.record[props.field.name] = app.utils.toRFC3339Datetime(e.target.value);
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
// get fieldIndex: int/-1,
|
||||
// get originalField: undefined
|
||||
// }
|
||||
export function settings(data) {
|
||||
export function settings(props) {
|
||||
const uniqueId = "f_" + app.utils.randomString();
|
||||
|
||||
return app.components.fieldSettings(data, {
|
||||
return app.components.fieldSettings(props, {
|
||||
content: () =>
|
||||
t.div(
|
||||
{ className: "grid sm" },
|
||||
@@ -21,10 +21,10 @@ export function settings(data) {
|
||||
type: "datetime-local",
|
||||
id: uniqueId + ".min",
|
||||
step: 1,
|
||||
name: () => `fields.${data.fieldIndex}.min`,
|
||||
value: () => app.utils.toDatetimeLocalInputValue(data.field.min),
|
||||
name: () => `fields.${props.fieldIndex}.min`,
|
||||
value: () => app.utils.toDatetimeLocalInputValue(props.field.min),
|
||||
onchange: (e) => {
|
||||
data.field.min = app.utils.toRFC3339Datetime(e.target.value);
|
||||
props.field.min = app.utils.toRFC3339Datetime(e.target.value);
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -38,10 +38,10 @@ export function settings(data) {
|
||||
type: "datetime-local",
|
||||
id: uniqueId + ".max",
|
||||
step: 1,
|
||||
name: () => `fields.${data.fieldIndex}.max`,
|
||||
value: () => app.utils.toDatetimeLocalInputValue(data.field.max),
|
||||
name: () => `fields.${props.fieldIndex}.max`,
|
||||
value: () => app.utils.toDatetimeLocalInputValue(props.field.max),
|
||||
onchange: (e) => {
|
||||
data.field.max = app.utils.toRFC3339Datetime(e.target.value);
|
||||
props.field.max = app.utils.toRFC3339Datetime(e.target.value);
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -54,9 +54,9 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "text",
|
||||
id: uniqueId + ".help",
|
||||
name: () => `fields.${data.fieldIndex}.help`,
|
||||
value: () => data.field.help || "",
|
||||
oninput: (e) => (data.field.help = e.target.value),
|
||||
name: () => `fields.${props.fieldIndex}.help`,
|
||||
value: () => props.field.help || "",
|
||||
oninput: (e) => (props.field.help = e.target.value),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -68,9 +68,9 @@ export function settings(data) {
|
||||
className: "sm",
|
||||
type: "checkbox",
|
||||
id: uniqueId + ".required",
|
||||
name: () => `fields.${data.fieldIndex}.required`,
|
||||
checked: () => !!data.field.required,
|
||||
onchange: (e) => (data.field.required = e.target.checked),
|
||||
name: () => `fields.${props.fieldIndex}.required`,
|
||||
checked: () => !!props.field.required,
|
||||
onchange: (e) => (props.field.required = e.target.checked),
|
||||
}),
|
||||
t.label(
|
||||
{ htmlFor: uniqueId + ".required" },
|
||||
@@ -78,7 +78,7 @@ export function settings(data) {
|
||||
t.small({ className: "txt-hint" }, "(!='')"),
|
||||
t.i({
|
||||
className: "ri-information-line link-hint",
|
||||
ariaDescription: app.attrs.tooltip("Requires the field value to be nonempty string"),
|
||||
ariaDescription: app.attrs.tooltip("Requires the field value to be nonempty string."),
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
export function input(props) {
|
||||
const uniqueId = "editor_" + app.utils.randomString();
|
||||
|
||||
const data = store({
|
||||
const local = store({
|
||||
lazyEditor: "",
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ export function input(props) {
|
||||
className: "record-field-input field-type-editor large-modal",
|
||||
onmount: () => {
|
||||
lazyEditorTimeoutId = setTimeout(() => {
|
||||
data.lazyEditor = app.components.tinymce({
|
||||
local.lazyEditor = app.components.tinymce({
|
||||
id: uniqueId,
|
||||
name: () => props.field.name,
|
||||
required: () => props.field.required,
|
||||
@@ -41,7 +41,7 @@ export function input(props) {
|
||||
t.i({ className: app.fieldTypes.editor.icon, ariaHidden: true }),
|
||||
t.span({ className: "txt" }, () => props.field.name),
|
||||
),
|
||||
() => data.lazyEditor,
|
||||
() => local.lazyEditor,
|
||||
),
|
||||
() => {
|
||||
if (props.field.help) {
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
// get fieldIndex: int/-1,
|
||||
// get originalField: undefined
|
||||
// }
|
||||
export function settings(data) {
|
||||
export function settings(props) {
|
||||
const uniqueId = "f_" + app.utils.randomString();
|
||||
|
||||
const local = store({
|
||||
showInfo: false,
|
||||
});
|
||||
|
||||
return app.components.fieldSettings(data, {
|
||||
return app.components.fieldSettings(props, {
|
||||
content: () =>
|
||||
t.div(
|
||||
{ className: "grid sm" },
|
||||
@@ -28,14 +28,14 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "number",
|
||||
id: uniqueId + ".maxSize",
|
||||
name: () => `fields.${data.fieldIndex}.maxSize`,
|
||||
name: () => `fields.${props.fieldIndex}.maxSize`,
|
||||
min: 0,
|
||||
step: 1,
|
||||
max: Number.MAX_SAFE_INTEGER,
|
||||
placeholder: "Default to max ~5MB",
|
||||
value: () => data.field.maxSize || "",
|
||||
value: () => props.field.maxSize || "",
|
||||
oninput: (e) => {
|
||||
data.field.maxSize = parseInt(e.target.value, 10);
|
||||
props.field.maxSize = parseInt(e.target.value, 10);
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -48,9 +48,9 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "text",
|
||||
id: uniqueId + ".help",
|
||||
name: () => `fields.${data.fieldIndex}.help`,
|
||||
value: () => data.field.help || "",
|
||||
oninput: (e) => (data.field.help = e.target.value),
|
||||
name: () => `fields.${props.fieldIndex}.help`,
|
||||
value: () => props.field.help || "",
|
||||
oninput: (e) => (props.field.help = e.target.value),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -62,16 +62,16 @@ export function settings(data) {
|
||||
className: "sm",
|
||||
type: "checkbox",
|
||||
id: uniqueId + ".required",
|
||||
name: () => `fields.${data.fieldIndex}.required`,
|
||||
checked: () => !!data.field.required,
|
||||
onchange: (e) => (data.field.required = e.target.checked),
|
||||
name: () => `fields.${props.fieldIndex}.required`,
|
||||
checked: () => !!props.field.required,
|
||||
onchange: (e) => (props.field.required = e.target.checked),
|
||||
}),
|
||||
t.label(
|
||||
{ htmlFor: uniqueId + ".required" },
|
||||
t.span({ className: "txt" }, "Required"),
|
||||
t.i({
|
||||
className: "ri-information-line link-hint",
|
||||
ariaDescription: app.attrs.tooltip("Requires the field value to be nonempty string"),
|
||||
ariaDescription: app.attrs.tooltip("Requires the field value to be nonempty string."),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -81,9 +81,9 @@ export function settings(data) {
|
||||
className: "sm",
|
||||
type: "checkbox",
|
||||
id: uniqueId + ".convertURLs",
|
||||
name: () => `fields.${data.fieldIndex}.convertURLs`,
|
||||
checked: () => !!data.field.convertURLs,
|
||||
onchange: (e) => (data.field.convertURLs = e.target.checked),
|
||||
name: () => `fields.${props.fieldIndex}.convertURLs`,
|
||||
checked: () => !!props.field.convertURLs,
|
||||
onchange: (e) => (props.field.convertURLs = e.target.checked),
|
||||
}),
|
||||
t.label(
|
||||
{ htmlFor: uniqueId + ".convertURLs" },
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
// get fieldIndex: int/-1,
|
||||
// get originalField: undefined
|
||||
// }
|
||||
export function settings(data) {
|
||||
export function settings(props) {
|
||||
const uniqueId = "f_" + app.utils.randomString();
|
||||
|
||||
return app.components.fieldSettings(data, {
|
||||
return app.components.fieldSettings(props, {
|
||||
content: () =>
|
||||
t.div(
|
||||
{ className: "grid sm" },
|
||||
@@ -29,12 +29,12 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "text",
|
||||
id: uniqueId + ".exceptDomains",
|
||||
disabled: () => !app.utils.isEmpty(data.field.onlyDomains),
|
||||
name: () => `fields.${data.fieldIndex}.exceptDomains`,
|
||||
value: () => app.utils.joinNonEmpty(data.field.exceptDomains),
|
||||
disabled: () => !app.utils.isEmpty(props.field.onlyDomains),
|
||||
name: () => `fields.${props.fieldIndex}.exceptDomains`,
|
||||
value: () => app.utils.joinNonEmpty(props.field.exceptDomains),
|
||||
onchange: (
|
||||
e,
|
||||
) => (data.field.exceptDomains = app.utils.splitNonEmpty(e.target.value, ",")),
|
||||
) => (props.field.exceptDomains = app.utils.splitNonEmpty(e.target.value, ",")),
|
||||
}),
|
||||
),
|
||||
t.div({ className: "field-help" }, "Use comma as separator."),
|
||||
@@ -56,10 +56,10 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "text",
|
||||
id: uniqueId + ".onlyDomains",
|
||||
disabled: () => !app.utils.isEmpty(data.field.exceptDomains),
|
||||
name: () => `fields.${data.fieldIndex}.onlyDomains`,
|
||||
value: () => app.utils.joinNonEmpty(data.field.onlyDomains),
|
||||
onchange: (e) => (data.field.onlyDomains = app.utils.splitNonEmpty(e.target.value, ",")),
|
||||
disabled: () => !app.utils.isEmpty(props.field.exceptDomains),
|
||||
name: () => `fields.${props.fieldIndex}.onlyDomains`,
|
||||
value: () => app.utils.joinNonEmpty(props.field.onlyDomains),
|
||||
onchange: (e) => (props.field.onlyDomains = app.utils.splitNonEmpty(e.target.value, ",")),
|
||||
}),
|
||||
),
|
||||
t.div({ className: "field-help" }, "Use comma as separator."),
|
||||
@@ -72,9 +72,9 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "text",
|
||||
id: uniqueId + ".help",
|
||||
name: () => `fields.${data.fieldIndex}.help`,
|
||||
value: () => data.field.help || "",
|
||||
oninput: (e) => (data.field.help = e.target.value),
|
||||
name: () => `fields.${props.fieldIndex}.help`,
|
||||
value: () => props.field.help || "",
|
||||
oninput: (e) => (props.field.help = e.target.value),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -86,9 +86,9 @@ export function settings(data) {
|
||||
className: "sm",
|
||||
type: "checkbox",
|
||||
id: uniqueId + ".required",
|
||||
name: () => `fields.${data.fieldIndex}.required`,
|
||||
checked: () => !!data.field.required,
|
||||
onchange: (e) => (data.field.required = e.target.checked),
|
||||
name: () => `fields.${props.fieldIndex}.required`,
|
||||
checked: () => !!props.field.required,
|
||||
onchange: (e) => (props.field.required = e.target.checked),
|
||||
}),
|
||||
t.label(
|
||||
{ htmlFor: uniqueId + ".required" },
|
||||
@@ -96,7 +96,7 @@ export function settings(data) {
|
||||
t.small({ className: "txt-hint" }, "(!='')"),
|
||||
t.i({
|
||||
className: "ri-information-line link-hint",
|
||||
ariaDescription: app.attrs.tooltip("Requires the field value to be nonempty string"),
|
||||
ariaDescription: app.attrs.tooltip("Requires the field value to be nonempty string."),
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// get fieldIndex: int/-1,
|
||||
// get originalField: undefined
|
||||
// }
|
||||
export function settings(data) {
|
||||
export function settings(props) {
|
||||
const uniqueId = "f_" + app.utils.randomString();
|
||||
|
||||
const isMultipleOptions = [
|
||||
@@ -13,7 +13,7 @@ export function settings(data) {
|
||||
{ label: "Multiple", value: true },
|
||||
];
|
||||
|
||||
return app.components.fieldSettings(data, {
|
||||
return app.components.fieldSettings(props, {
|
||||
header: [
|
||||
t.div(
|
||||
{
|
||||
@@ -23,15 +23,15 @@ export function settings(data) {
|
||||
required: true,
|
||||
options: isMultipleOptions,
|
||||
value: () => {
|
||||
return data.field.maxSelect > 1;
|
||||
return props.field.maxSelect > 1;
|
||||
},
|
||||
onchange: (opts) => {
|
||||
if (opts?.[0]?.value) {
|
||||
if (!data.field.maxSelect || data.field.maxSelect < 2) {
|
||||
data.field.maxSelect = 10;
|
||||
if (!props.field.maxSelect || props.field.maxSelect < 2) {
|
||||
props.field.maxSelect = 10;
|
||||
}
|
||||
} else {
|
||||
data.field.maxSelect = 1;
|
||||
props.field.maxSelect = 1;
|
||||
}
|
||||
},
|
||||
}),
|
||||
@@ -68,9 +68,9 @@ export function settings(data) {
|
||||
),
|
||||
};
|
||||
}),
|
||||
name: () => `fields.${data.fieldIndex}.mimeTypes`,
|
||||
value: () => app.utils.toArray(data.field.mimeTypes),
|
||||
onchange: (opts) => (data.field.mimeTypes = opts.map((opt) => opt.value)),
|
||||
name: () => `fields.${props.fieldIndex}.mimeTypes`,
|
||||
value: () => app.utils.toArray(props.field.mimeTypes),
|
||||
onchange: (opts) => (props.field.mimeTypes = opts.map((opt) => opt.value)),
|
||||
}),
|
||||
),
|
||||
t.div(
|
||||
@@ -95,7 +95,7 @@ export function settings(data) {
|
||||
className: "dropdown-item",
|
||||
role: "menuitem",
|
||||
onclick: (e) => {
|
||||
data.field.mimeTypes = [
|
||||
props.field.mimeTypes = [
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"image/svg+xml",
|
||||
@@ -112,7 +112,7 @@ export function settings(data) {
|
||||
className: "dropdown-item",
|
||||
role: "menuitem",
|
||||
onclick: (e) => {
|
||||
data.field.mimeTypes = [
|
||||
props.field.mimeTypes = [
|
||||
"application/pdf",
|
||||
"application/msword",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
@@ -129,7 +129,7 @@ export function settings(data) {
|
||||
className: "dropdown-item",
|
||||
role: "menuitem",
|
||||
onclick: (e) => {
|
||||
data.field.mimeTypes = [
|
||||
props.field.mimeTypes = [
|
||||
"video/mp4",
|
||||
"video/mpeg",
|
||||
"video/x-msvideo",
|
||||
@@ -146,7 +146,7 @@ export function settings(data) {
|
||||
className: "dropdown-item",
|
||||
role: "menuitem",
|
||||
onclick: (e) => {
|
||||
data.field.mimeTypes = [
|
||||
props.field.mimeTypes = [
|
||||
"application/zip",
|
||||
"application/x-7z-compressed",
|
||||
"application/x-rar-compressed",
|
||||
@@ -160,7 +160,7 @@ export function settings(data) {
|
||||
),
|
||||
),
|
||||
t.div(
|
||||
{ className: () => (data.field.maxSelect > 1 ? "col-sm-6" : "col-sm-9") },
|
||||
{ className: () => (props.field.maxSelect > 1 ? "col-sm-6" : "col-sm-9") },
|
||||
t.div(
|
||||
{ className: "field" },
|
||||
t.label(
|
||||
@@ -179,9 +179,9 @@ export function settings(data) {
|
||||
type: "text",
|
||||
id: uniqueId + ".thumbs",
|
||||
placeholder: "e.g. 50x50, 480x720",
|
||||
name: () => `fields.${data.fieldIndex}.thumbs`,
|
||||
value: () => app.utils.joinNonEmpty(data.field.thumbs),
|
||||
onchange: (e) => (data.field.thumbs = app.utils.splitNonEmpty(e.target.value, ",")),
|
||||
name: () => `fields.${props.fieldIndex}.thumbs`,
|
||||
value: () => app.utils.joinNonEmpty(props.field.thumbs),
|
||||
onchange: (e) => (props.field.thumbs = app.utils.splitNonEmpty(e.target.value, ",")),
|
||||
}),
|
||||
),
|
||||
t.div(
|
||||
@@ -250,43 +250,39 @@ export function settings(data) {
|
||||
min: 0,
|
||||
max: Number.MAX_SAFE_INTEGER,
|
||||
placeholder: "~5MB default",
|
||||
name: () => `fields.${data.fieldIndex}.maxSize`,
|
||||
value: () => data.field.maxSize || "",
|
||||
oninput: (e) => (data.field.maxSize = parseInt(e.target.value, 10)),
|
||||
name: () => `fields.${props.fieldIndex}.maxSize`,
|
||||
value: () => props.field.maxSize || "",
|
||||
oninput: (e) => (props.field.maxSize = parseInt(e.target.value, 10)),
|
||||
}),
|
||||
),
|
||||
t.div({ className: "field-help" }, "In bytes."),
|
||||
),
|
||||
() => {
|
||||
if (data.field.maxSelect > 1) {
|
||||
return t.div(
|
||||
{ className: "col-sm-3" },
|
||||
t.div(
|
||||
{ className: "field" },
|
||||
t.label({ htmlFor: uniqueId + ".maxSelect" }, "Max select"),
|
||||
t.input({
|
||||
type: "number",
|
||||
id: uniqueId + ".maxSelect",
|
||||
placeholder: "Default to single",
|
||||
step: 1,
|
||||
min: 2,
|
||||
required: true,
|
||||
max: Number.MAX_SAFE_INTEGER,
|
||||
name: () => `fields.${data.fieldIndex}.maxSelect`,
|
||||
value: () => data.field.maxSelect || "",
|
||||
onchange: (e) => {
|
||||
const maxSelect = parseInt(e.target.value, 10);
|
||||
if (maxSelect > 1) {
|
||||
props.field.maxSelect = maxSelect;
|
||||
} else {
|
||||
props.field.maxSelect = 1;
|
||||
}
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
t.div(
|
||||
{ className: "col-sm-3", hidden: () => props.field.maxSelect << 0 < 2 },
|
||||
t.div(
|
||||
{ className: "field" },
|
||||
t.label({ htmlFor: uniqueId + ".maxSelect" }, "Max select"),
|
||||
t.input({
|
||||
type: "number",
|
||||
id: uniqueId + ".maxSelect",
|
||||
placeholder: "Default to single",
|
||||
step: 1,
|
||||
min: 2,
|
||||
required: true,
|
||||
max: Number.MAX_SAFE_INTEGER,
|
||||
name: () => `fields.${props.fieldIndex}.maxSelect`,
|
||||
value: () => props.field.maxSelect || "",
|
||||
onchange: (e) => {
|
||||
const maxSelect = parseInt(e.target.value, 10);
|
||||
if (maxSelect > 1) {
|
||||
props.field.maxSelect = maxSelect;
|
||||
} else {
|
||||
props.field.maxSelect = 1;
|
||||
}
|
||||
},
|
||||
}),
|
||||
),
|
||||
),
|
||||
t.div(
|
||||
{ className: "col-sm-12" },
|
||||
t.div(
|
||||
@@ -295,9 +291,9 @@ export function settings(data) {
|
||||
className: "switch",
|
||||
type: "checkbox",
|
||||
id: uniqueId + ".protected",
|
||||
name: () => `fields.${data.fieldIndex}.protected`,
|
||||
checked: () => !!data.field.protected,
|
||||
onchange: (e) => (data.field.protected = e.target.checked),
|
||||
name: () => `fields.${props.fieldIndex}.protected`,
|
||||
checked: () => !!props.field.protected,
|
||||
onchange: (e) => (props.field.protected = e.target.checked),
|
||||
}),
|
||||
t.label(
|
||||
{ htmlFor: uniqueId + ".protected" },
|
||||
@@ -324,9 +320,9 @@ export function settings(data) {
|
||||
t.input({
|
||||
type: "text",
|
||||
id: uniqueId + ".help",
|
||||
name: () => `fields.${data.fieldIndex}.help`,
|
||||
value: () => data.field.help || "",
|
||||
oninput: (e) => (data.field.help = e.target.value),
|
||||
name: () => `fields.${props.fieldIndex}.help`,
|
||||
value: () => props.field.help || "",
|
||||
oninput: (e) => (props.field.help = e.target.value),
|
||||
}),
|
||||
),
|
||||
),
|
||||
@@ -338,17 +334,19 @@ export function settings(data) {
|
||||
className: "sm",
|
||||
type: "checkbox",
|
||||
id: uniqueId + ".required",
|
||||
name: () => `fields.${data.fieldIndex}.required`,
|
||||
checked: () => !!data.field.required,
|
||||
onchange: (e) => (data.field.required = e.target.checked),
|
||||
name: () => `fields.${props.fieldIndex}.required`,
|
||||
checked: () => !!props.field.required,
|
||||
onchange: (e) => (props.field.required = e.target.checked),
|
||||
}),
|
||||
t.label(
|
||||
{ htmlFor: uniqueId + ".required" },
|
||||
t.span({ className: "txt" }, "Required"),
|
||||
t.small({ className: "txt-hint" }, "(!='')"),
|
||||
t.small({ className: "txt-hint" }, () => props.field.maxSelect > 1 ? "(!=[])" : "(!='')"),
|
||||
t.i({
|
||||
className: "ri-information-line link-hint",
|
||||
ariaDescription: app.attrs.tooltip("Requires the field value to be nonempty string"),
|
||||
ariaDescription: app.attrs.tooltip(() =>
|
||||
`Requires the field value to be nonempty ${props.field.maxSelect > 1 ? "array" : "string"}.`
|
||||
),
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
Loaded 100 of 120 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user