minimal sql console API and UI

This commit is contained in:
Gani Georgiev committed 2026-05-24 09:30:30 +03:00
1 parent db853850ab
commit b022e138d4
21 files changed
+984 -21

No files matched your search

+1
View File
@@ -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)
+191
View File
@@ -0,0 +1,191 @@
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, 3000)),
)
}
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"}
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
}
+202
View File
@@ -0,0 +1,202 @@
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: "long query",
Method: http.MethodPost,
URL: "/api/sql",
Body: strings.NewReader(`{"query":"` + strings.Repeat("a", 3001) + `"}`),
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: "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)
}
}
@@ -1,8 +1,9 @@
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
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
`),i=new Blob([r],{type:`text/csv;charset=utf-8;`}),a=window.URL.createObjectURL(i);_.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||=g.hash;let i;return i=RegExp(n?`^`+RegExp.escape(e)+`\\/?.*$`:`^`+RegExp.escape(e)+`\\/?(?:\\?.+)?$`),i.test(r)},getHashQueryParams(e=``){e||=g.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];_.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},rememberPath(){window.localStorage.setItem(h,window.location.hash)},toRememberedPath(e=`#/collections`){let n=window.localStorage.getItem(h);n&&window.localStorage.removeItem(h),window.location.hash=n||e},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 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
`),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.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">
@@ -59,12 +60,12 @@ This field is disabled if "Except domains" is set.`)})),t.input({type:`text`,id:
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
-----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
(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}),app.components.recordsList({className:`m-t-sm`,reset:()=>r.reset,hidden:()=>!app.store.activeCollection?.id,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 Mr(){return document.body.clientWidth>600&&document.body.clientHeight>=720?150:100}function Nr(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,Fr(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:Mr()})},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 Pr(e){let n=e?.data?.[0];n&&e.setScale(`x`,{min:n[0],max:n[n.length-1]})}function Fr(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:Mr(),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:[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
{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(console.log(n.bulkSelected),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?.m 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
@@ -80,4 +81,4 @@ Records update will also require an extra unlock step before save.`)})))),t.div(
@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
@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
@@ -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
+2 -2
View File
@@ -13,9 +13,9 @@
<!-- prism -->
<script src="./libs/prism/prism.js" data-manual></script>
<script type="module" crossorigin src="./assets/index-1u9sxa_M.js"></script>
<script type="module" crossorigin src="./assets/index-BRepvmcR.js"></script>
<link rel="modulepreload" crossorigin href="./assets/pocketbase.es-B_4DUNUU.js">
<link rel="stylesheet" crossorigin href="./assets/index-ePwHvxbV.css">
<link rel="stylesheet" crossorigin href="./assets/index-C5MjNoft.css">
</head>
<body>
</body>
+1
View File
@@ -1,3 +1,4 @@
(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+`:`,`
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],!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]=P(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({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,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 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||{});
//# sourceMappingURL=shablon.iife.js.map
+1
View File
@@ -1,3 +1,4 @@
(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+`:`,`
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],!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]=P(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({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,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 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||{});
//# sourceMappingURL=shablon.iife.js.map
+1 -1
View File
@@ -197,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"),
+1
View File
@@ -35,3 +35,4 @@
@import "./collectionsOverview.css";
@import "./erd.css";
@import "./hideControls.css";
@import "./sqlConsole.css";
+1
View File
@@ -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));
+1
View File
@@ -1131,6 +1131,7 @@ button {
&:empty::before {
content: attr(data-placeholder);
color: var(--surfaceTxtDisabledColor);
white-space: normal;
}
}
+5 -10
View File
@@ -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);
+32
View File
@@ -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;
}
}
}
+17
View File
@@ -317,6 +317,22 @@ table {
min-width: 900px;
border-radius: 0;
}
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;
}
}
tr {
animation: fadeIn var(--animationSpeed);
}
@@ -342,6 +358,7 @@ table {
td,
th {
height: 59px;
border-radius: 0;
}
}
-1
View File
@@ -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;
+2
View File
@@ -165,6 +165,8 @@ export function logsList(logsSettings) {
return; // nothing to download
}
console.log(data.bulkSelected);
if (selected.length == 1) {
return app.utils.downloadJSON(
selected[0],
+2
View File
@@ -5,6 +5,7 @@ import { pageApplicationSettings } from "@/settings/application/pageApplicationS
import { pageBackupsSettings } from "@/settings/backups/pageBackupsSettings";
import { pageCronsSettings } from "@/settings/crons/pageCronsSettings";
import { pageMailSettings } from "@/settings/mail/pageMailSettings";
import { pageSQLConsole } from "@/settings/sql/pageSQLConsole";
import { pageStorageSettings } from "@/settings/storage/pageStorageSettings";
import { pageExportCollections } from "@/settings/sync/pageExportCollections";
import { pageImportCollections } from "@/settings/sync/pageImportCollections";
@@ -170,3 +171,4 @@ app.routes.superuserOnly("#/settings/backups", pageBackupsSettings);
app.routes.superuserOnly("#/settings/crons", pageCronsSettings);
app.routes.superuserOnly("#/settings/export-collections", pageExportCollections);
app.routes.superuserOnly("#/settings/import-collections", pageImportCollections);
app.routes.superuserOnly("#/settings/sql", pageSQLConsole);
+486
View File
@@ -0,0 +1,486 @@
import { settingsSidebar } from "../settingsSidebar";
const SQL_HISTORY_STORAGE_KEY = "pbSQLConsoleHistory";
export function pageSQLConsole(route) {
app.store.title = "SQL console";
const uniqueId = "sql_console_" + app.utils.randomString();
const editorId = uniqueId + "editor";
const requestKey = uniqueId + "executeSQL";
const defaultMaxRows = 250;
const pageData = store({
askedForConfirmationAtLeastOnce: false,
isExecuting: false,
maxRows: defaultMaxRows,
query: "",
result: {},
sort: {},
errorMsg: "",
executedHistory: app.utils.getLocalHistory(SQL_HISTORY_STORAGE_KEY, []),
get sortedResultRows() {
// no client-side sort
if (typeof pageData.sort?.index == "undefined") {
const rows = pageData.result?.rows || [];
return pageData.maxRows >= rows.length ? rows : rows.slice(0, pageData.maxRows);
}
const isAsc = !!pageData.sort?.asc;
const sorted = pageData.result?.rows?.toSorted((rowA, rowB) => {
const valA = rowA[pageData.sort.index];
const valB = rowB[pageData.sort.index];
if (isAsc) {
if (valA == valB) {
return 0;
}
if (valA == null) {
return -1;
}
if (valB == null) {
return 1;
}
return valA.localeCompare(valB);
}
if (valA == valB) {
return 0;
}
if (valA == null) {
return 1;
}
if (valB == null) {
return -1;
}
return valB.localeCompare(valA);
}) || [];
return pageData.maxRows >= sorted.length ? sorted : sorted.slice(0, pageData.maxRows);
},
get totalRemainingRows() {
return (pageData.result?.rows?.length << 0) - pageData.sortedResultRows.length;
},
});
function needConfirmation(query) {
if (!query?.trim()) {
return false;
}
// if the hideControls option is enabled ask for confirmation
// at least once no matter of the query
if (
!pageData.askedForConfirmationAtLeastOnce
&& !!app.store.settings?.meta?.hideControls
) {
return true;
}
query = query?.replace(/[\s\;]/gm, " ").toUpperCase() + " ";
return !![
"INSERT ",
"CREATE ",
"UPDATE ",
"DELETE ",
"DROP ",
"DETACH ",
"PRAGMA ",
].find((p) => query.includes(p));
}
async function executeSQLWithConfirm() {
if (!needConfirmation(pageData.query)) {
return executeSQL();
}
pageData.askedForConfirmationAtLeastOnce = true;
return app.modals.confirm(
t.div(
{ className: "txt-center" },
t.h6(
null,
"Be careful and continue only if you really know what you are doing because, depending on the query, the operation could break your application and may not be reversible.",
),
),
() => executeSQL(),
null,
{ yesButton: "Execute", noButton: "Cancel" },
);
}
async function executeSQL() {
pageData.isExecuting = true;
pageData.maxRows = defaultMaxRows;
pageData.result = {};
pageData.sort = {};
pageData.errorMsg = "";
const query = pageData.query.trim();
if (!query) {
app.pb.cancelRequest(requestKey);
pageData.isExecuting = false;
return;
}
try {
// @todo add method to JS SDK
pageData.result = await app.pb.send("/api/sql", {
method: "POST",
body: { query },
requestKey: requestKey,
});
addToHistory(query);
pageData.isExecuting = false;
} catch (err) {
if (!err?.isAbort) {
pageData.isExecuting = false;
pageData.errorMsg = err?.response?.message || err?.message || "Failed to execute query.";
}
}
}
function removeFromHistory(query) {
function looseNormalize(str) {
return str.replace(/[\s\;]/gm, "").toUpperCase();
}
const normalized = looseNormalize(query);
for (let i = pageData.executedHistory.length - 1; i >= 0; i--) {
if (
pageData.executedHistory[i] == query
|| looseNormalize(pageData.executedHistory[i]) == normalized
) {
pageData.executedHistory.splice(i, 1);
}
}
}
function addToHistory(query) {
removeFromHistory(query);
pageData.executedHistory.unshift(pageData.query);
if (pageData.executedHistory.length > 10) {
pageData.executedHistory.splice(10);
}
}
function downloadCSV() {
if (!pageData.sortedResultRows.length) {
return;
}
const data = [pageData.result.columns.map((c) => c.name)].concat(pageData.sortedResultRows);
const name = "export_" + app.utils.toLocalDatetime(new Date()).replace(/[\-\:\. ]/g, "_") + ".csv";
app.utils.downloadCSV(data, name);
}
function toggleSort(index) {
if (pageData.sort?.index == index) {
pageData.sort = {
index: index,
asc: !pageData.sort.asc,
};
} else {
pageData.sort = {
index: index,
asc: true,
};
}
}
const watchers = [
watch(() => JSON.stringify(pageData.executedHistory), (newVal, oldVal) => {
if (typeof oldVal == "undefined") {
return;
}
window.localStorage.setItem(SQL_HISTORY_STORAGE_KEY, newVal);
}),
];
return t.div(
{
pbEvent: "pageSQLConsole",
className: "page",
onunmount: () => {
app.pb.cancelRequest(requestKey);
watchers.forEach((w) => w?.unwatch());
},
},
settingsSidebar(),
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" }, () => app.store.title),
),
t.div(
{ className: "page-header-secondary-btns" },
t.button(
{
type: "button",
className: "btn circle transparent secondary",
ariaDescription: app.attrs.tooltip("Recently executed queries", "right"),
"html-popovertarget": "sql-console-history-dropdown",
},
t.i({ className: "ri-history-line", ariaHidden: true }),
),
t.div(
{
id: "sql-console-history-dropdown",
className: () =>
`dropdown left sql-console-history-dropdown ${
!pageData.executedHistory.length ? "no-items" : ""
}`,
popover: "auto",
},
() => {
if (!pageData.executedHistory.length) {
return t.span({ className: "txt txt-hint p-5" }, "No recently executed queries.");
}
return pageData.executedHistory.map((item) => {
return t.button(
{
role: "button",
className: "dropdown-item",
onclick: (e) => {
e.target.closest(".dropdown").hidePopover();
pageData.query = item;
document.getElementById(editorId)?.click();
},
},
t.span(
{ className: "query" },
() => app.utils.truncate(item, 500),
),
t.small(
{
role: "button",
className: "remove-btn link-hint m-l-auto p-l-5 p-r-5",
title: "Clear",
onauxclick: (e) => {
e.stopPropagation();
return false;
},
onclick: (e) => {
e.stopPropagation();
removeFromHistory(item);
return false;
},
},
t.i({ className: "ri-close-line", ariaHidden: true }),
),
);
});
},
),
),
t.div(
{ className: "page-header-primary-btns" },
t.button(
{
type: "button",
className: () => `btn expanded-lg ${pageData.isExecuting ? "loading" : ""}`,
disabled: () => pageData.isExecuting,
onclick: () => executeSQLWithConfirm(),
},
t.i({ className: "ri-play-large-line", ariaHidden: true }),
t.span({ className: "txt" }, "Execute"),
),
),
),
t.div(
{ className: "field sql-console-field" },
app.components.codeEditor({
id: editorId,
language: "sql",
required: true,
name: "query",
placeholder: "e.g. EXPLAIN QUERY PLAN SELECT * from users WHERE verified=true",
value: () => pageData.query,
oninput: (val) => pageData.query = val,
onblur: (val) => pageData.query = val.trim(),
}),
),
t.div(
{ className: "flex field-help m-b-sm" },
t.button(
{
type: "button",
className: "link-hint m-l-auto",
"html-popovertarget": uniqueId + "caveats_dropdown",
},
() => "SQL console caveats",
),
t.div(
{
id: uniqueId + "caveats_dropdown",
className: "dropdown sm query-caveats-dropdown",
popover: "auto",
},
t.ul(
null,
t.li(null, "The returned rows are limited up to 1000."),
t.li(null, "The executed queries have a max timeout of 3 minutes."),
t.li(null, "The data is returned as byte strings without any additional formatting."),
t.li(null, "Multiple queries are supported but only the result of the last one is returned."),
),
),
),
// error alert
t.div(
{
hidden: () => pageData.isExecuting || !pageData.errorMsg,
className: "alert danger m-b-sm",
},
t.pre(null, () => pageData.errorMsg),
),
// success alert
t.div(
{
hidden: () =>
pageData.isExecuting || pageData.errorMsg || pageData.result?.columns?.length
|| app.utils.isEmpty(pageData.result),
className: "alert success m-b-sm",
},
t.p({ className: "txt-bold" }, "Query executed successfully!"),
t.p(null, "Affected rows: ", () => pageData.result?.affectedRows || 0),
),
// rows
t.div(
{
hidden: () => pageData.isExecuting || !pageData.result?.columns?.length,
className: "page-table-wrapper",
},
t.table(
{ className: "sql-console-table responsive-table optimize" },
t.thead(
{ className: "sticky" },
t.tr(null, () => {
return pageData.result?.columns?.map((col, i) => {
return t.th({
textContent: col.name,
className: () => {
let classes = "sort-handle";
if (pageData.sort?.index == i) {
classes += pageData.sort.asc ? " asc" : " desc";
}
return classes;
},
onclick: () => toggleSort(i),
});
});
}),
),
t.tbody(
null,
() => {
if (!pageData.sortedResultRows.length) {
return t.tr(
null,
t.td(
{ colSpan: pageData.result?.columns?.length || 1, className: "txt-center" },
t.span({ className: "txt-hint" }, "No rows found."),
),
);
}
return pageData.sortedResultRows.map((rowData) => {
return t.tr(
null,
() => {
return pageData.result?.columns?.map((col, j) => {
const val = rowData[j];
return t.td(
{ "html-data-name": col.name },
val == null ? "NULL" : app.utils.truncate(val, 2000),
);
});
},
);
});
},
// load more btn
t.tr(
{
hidden: () => (
pageData.isExecuting
|| !pageData.result?.rows?.length
|| pageData.result.rows.length <= pageData.sortedResultRows.length
),
},
t.td(
{ colSpan: 99 },
t.button(
{
type: "button",
className: "btn lg secondary load-more-btn",
onclick: () => {
pageData.maxRows = pageData.result?.rows?.length || defaultMaxRows;
},
},
t.span({
className: "txt",
textContent: () => `Load remaining (${pageData.totalRemainingRows})`,
}),
),
),
),
),
),
),
t.footer(
{ className: "page-footer" },
t.span(
{
className: () => `exec-time ${pageData.isExecuting ? "faded" : ""}`,
},
"Time: ",
() => (pageData.result?.execTime || 0) + "ms",
),
t.span(
{
hidden: () => !pageData.result?.columns?.length,
className: () => `total-count ${pageData.isExecuting ? "faded" : ""}`,
},
"Rows: ",
() => pageData.result?.rows?.length || 0,
() => {
if (!pageData.result?.rows?.length) {
return;
}
return [
" (",
t.span({
role: "button",
className: "link-hint",
textContent: "Export as CSV",
onclick: downloadCSV,
}),
")",
];
},
),
app.components.credits(),
),
),
);
}
+8 -1
View File
@@ -115,6 +115,13 @@ window.app.store = store({
label: "Import collections",
},
],
Debug: [
{
href: "#/settings/sql",
icon: "ri-terminal-box-line",
label: "SQL console",
},
],
},
predefinedAccentColors: [
@@ -205,7 +212,7 @@ window.app.store = store({
addOrUpdateCollection(collection) {
const index = app.store.collections.findIndex((c) => c.id == collection.id);
if (index >= 0) {
if (app.store.activeCollection.id == collection.id) {
if (app.store.activeCollection?.id == collection.id) {
app.store._activeCollectionIdOrName = collection.id;
}
+23
View File
@@ -844,6 +844,29 @@ const utils = {
utils.download(url, name);
},
/**
* Downloads a CSV file created from the provide array data.
*
* @param {Array} arr The JS array to serialize as CSV
* @param {string} name The result file name.
*/
downloadCSV(arr, name) {
name = name.endsWith(".csv") ? name : name + ".csv";
// seriaze data
let content = arr.map((row) => {
return row.map((v) => "\"" + ("" + v).replaceAll("\"", "\"\"") + "\"").join(",");
}).join("\n");
const blob = new Blob([content], {
type: "text/csv;charset=utf-8;",
});
const url = window.URL.createObjectURL(blob);
utils.download(url, name);
},
/**
* Returns a normalized API URL address that is used in the API example docs.
*