mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-05-19 06:11:43 -04:00
various minor ui fixes
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -2,21 +2,21 @@
|
||||
|
||||
- Added backups list scroll container ([#7655](https://github.com/pocketbase/pocketbase/issues/7655)).
|
||||
|
||||
- Optimized record upsert and preview modals loading to minimize layout jumps.
|
||||
- Optimized record upsert and preview modals data loading to minimize layout jumps.
|
||||
|
||||
- Fixed SMTP IPv6 network address format ([#7659](https://github.com/pocketbase/pocketbase/issues/7659)).
|
||||
|
||||
- Fixed autocomplete selection not properly updating the underlying input value ([#7664](https://github.com/pocketbase/pocketbase/issues/7664)).
|
||||
|
||||
- Added dummy bcrypt password check for the failure auth path to minimize enumaration timing attacks.
|
||||
- Added `ghupdate.BaseURL` config option ([#7665](https://github.com/pocketbase/pocketbase/issues/7665)).
|
||||
|
||||
- Added dummy bcrypt password check for the failure auth path to minimize enumaration timing attacks when registrations are disabled.
|
||||
|
||||
- Adjusted Bitbucket, GitHub and Gitea/Forgejo OAuth2 providers to better reflect recent API updates and doc references.
|
||||
_The providers also now always send a sepatate emails list internal request since it contains more information about the fetched email than the userinfo endpoint in order to minimize eventual linking security issues caused by custom onpremise setups (e.g. Gitea/Forgejo allows skipping the emails verification if an ENV variable is configured)._
|
||||
_The providers also now always send a sepatate emails list request since it has more information about the fetched email than the userinfo endpoint in order to minimize eventual linking security issues caused by custom onpremise setups (e.g. Gitea/Forgejo allows skipping the email verification if an ENV variable is configured)._
|
||||
|
||||
- ⚠️ Fixed a pre-hijacking OAuth2 linking vulnerability ([#7662](https://github.com/pocketbase/pocketbase/discussions/7662); thanks @Alardiians for reporting it privately).
|
||||
|
||||
- Added `ghupdate.BaseURL` config option ([#7665](https://github.com/pocketbase/pocketbase/issues/7665)).
|
||||
|
||||
- Bumped Go and npm dependencies.
|
||||
|
||||
|
||||
|
||||
81
ui/dist/assets/index-DbuYk4bQ.js
vendored
81
ui/dist/assets/index-DbuYk4bQ.js
vendored
File diff suppressed because one or more lines are too long
81
ui/dist/assets/index-DijNBRV3.js
vendored
Normal file
81
ui/dist/assets/index-DijNBRV3.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
ui/dist/index.html
vendored
2
ui/dist/index.html
vendored
@@ -13,7 +13,7 @@
|
||||
|
||||
<!-- prism -->
|
||||
<script src="./libs/prism/prism.js" data-manual></script>
|
||||
<script type="module" crossorigin src="./assets/index-DbuYk4bQ.js"></script>
|
||||
<script type="module" crossorigin src="./assets/index-DijNBRV3.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/pocketbase.es-B_4DUNUU.js">
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-ouas71Vg.css">
|
||||
</head>
|
||||
|
||||
@@ -124,10 +124,10 @@ window.app.components.tinymce = function(propsArg = {}) {
|
||||
|
||||
clearTimeout(changeTimeoutId);
|
||||
|
||||
// workaround for https://github.com/tinymce/tinymce/issues/9377
|
||||
editorRef.dom?.unbind(document);
|
||||
|
||||
catchError(() => {
|
||||
// workaround for https://github.com/tinymce/tinymce/issues/9377
|
||||
editorRef.dom?.unbind(document);
|
||||
|
||||
window.tinymce?.remove(editorRef);
|
||||
});
|
||||
editorRef = null;
|
||||
|
||||
@@ -7,9 +7,32 @@
|
||||
export function input(props) {
|
||||
const uniqueId = "editor_" + app.utils.randomString();
|
||||
|
||||
const data = store({
|
||||
lazyEditor: "",
|
||||
});
|
||||
|
||||
let lazyEditorTimeoutId;
|
||||
|
||||
return t.div(
|
||||
{
|
||||
className: "record-field-input field-type-editor large-modal",
|
||||
onmount: () => {
|
||||
lazyEditorTimeoutId = setTimeout(() => {
|
||||
data.lazyEditor = app.components.tinymce({
|
||||
id: uniqueId,
|
||||
name: () => props.field.name,
|
||||
required: () => props.field.required,
|
||||
convertURLs: () => props.field.convertURLs,
|
||||
value: () => props.record[props.field.name] || "",
|
||||
onchange: (val) => {
|
||||
props.record[props.field.name] = val;
|
||||
},
|
||||
});
|
||||
}, 0);
|
||||
},
|
||||
onunmount: () => {
|
||||
clearTimeout(lazyEditorTimeoutId);
|
||||
},
|
||||
},
|
||||
t.div(
|
||||
{ className: "field" },
|
||||
@@ -18,18 +41,7 @@ export function input(props) {
|
||||
t.i({ className: app.fieldTypes.editor.icon, ariaHidden: true }),
|
||||
t.span({ className: "txt" }, () => props.field.name),
|
||||
),
|
||||
() => {
|
||||
return app.components.tinymce({
|
||||
id: uniqueId,
|
||||
name: () => props.field.name,
|
||||
required: () => props.field.required,
|
||||
convertURLs: () => props.field.convertURLs,
|
||||
value: () => props.record[props.field.name] || "",
|
||||
onchange: (val) => {
|
||||
props.record[props.field.name] = val;
|
||||
},
|
||||
});
|
||||
},
|
||||
() => data.lazyEditor,
|
||||
),
|
||||
() => {
|
||||
if (props.field.help) {
|
||||
|
||||
@@ -304,7 +304,7 @@ export function settings(data) {
|
||||
t.span({ className: "txt" }, "Protected"),
|
||||
t.small(
|
||||
{ className: "txt-hint" },
|
||||
"Files will require View API rule permissions and file token (",
|
||||
"File download requests will need to satisfy the View API rule (",
|
||||
t.a({
|
||||
href: import.meta.env.PB_PROTECTED_FILE_DOCS,
|
||||
target: "_blank",
|
||||
|
||||
@@ -43,28 +43,27 @@ window.app.modals.openRecordUpsert = function(collection, record = null, modalSe
|
||||
app.modals.open(modal);
|
||||
};
|
||||
|
||||
const defaultRedactFields = ["expand"];
|
||||
|
||||
function redacted(record, redactFields = defaultRedactFields) {
|
||||
// create redacted clone only if necessery
|
||||
if (redactFields.find((f) => typeof record[f] !== "undefined")) {
|
||||
record = Object.assign({}, record);
|
||||
for (let f of redactFields) {
|
||||
delete record[f];
|
||||
}
|
||||
// redact common sensitive fields
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter
|
||||
function redactedReplacer(key, val) {
|
||||
switch (key) {
|
||||
case "expand":
|
||||
case "password":
|
||||
case "passwordConfirm":
|
||||
case "tokenKey":
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return record;
|
||||
return val;
|
||||
}
|
||||
|
||||
function downloadJSON(record) {
|
||||
record = redacted(record);
|
||||
app.utils.downloadJSON(record, record.collectionName + "_" + record.id + ".json");
|
||||
const pojo = JSON.parse(JSON.stringify(record, redactedReplacer));
|
||||
app.utils.downloadJSON(pojo, record.collectionName + "_" + record.id + ".json");
|
||||
}
|
||||
|
||||
function copyJSON(record) {
|
||||
record = redacted(record);
|
||||
app.utils.copyToClipboard(JSON.stringify(record, null, 2));
|
||||
app.utils.copyToClipboard(JSON.stringify(record, redactedReplacer, 2));
|
||||
app.toasts.success("Record copied to clipboard!");
|
||||
}
|
||||
|
||||
@@ -73,7 +72,7 @@ function serializeRecord(record) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return JSON.stringify(redacted(record));
|
||||
return JSON.stringify(record, redactedReplacer);
|
||||
}
|
||||
|
||||
const TAB_MAIN = "main";
|
||||
@@ -253,7 +252,11 @@ function recordUpsertModal(collection, rawRecord, modalSettings) {
|
||||
Object.assign(data.record, JSON.parse(JSON.stringify(record)));
|
||||
|
||||
data.isLoading = false;
|
||||
initDraftWatcher();
|
||||
|
||||
// schedule a macro task to allow fields to populate their reactive values
|
||||
setTimeout(() => {
|
||||
initDraftWatcher();
|
||||
}, 0);
|
||||
} catch (err) {
|
||||
if (!err?.isAbort) {
|
||||
app.checkApiError(err);
|
||||
@@ -263,24 +266,32 @@ function recordUpsertModal(collection, rawRecord, modalSettings) {
|
||||
}
|
||||
}
|
||||
|
||||
function deleteInternalKeys(record) {
|
||||
for (let key in record) {
|
||||
if (key.startsWith("@@")) {
|
||||
delete record[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function exportPayload() {
|
||||
const payload = {};
|
||||
|
||||
// shallow copy of the record fields
|
||||
for (const prop in data.record) {
|
||||
for (const key in data.record) {
|
||||
// skip expand and internal dynamic enumerable props
|
||||
if (prop == "expand" || prop.startsWith("@@")) {
|
||||
if (key == "expand" || key.startsWith("@@")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let val = data.record[prop]?.__raw || data.record[prop];
|
||||
let val = data.record[key]?.__raw || data.record[key];
|
||||
|
||||
// normalize undefined values
|
||||
if (typeof val == "undefined") {
|
||||
val = null;
|
||||
}
|
||||
|
||||
payload[prop] = val;
|
||||
payload[key] = val;
|
||||
}
|
||||
|
||||
// apply fields save normalization funcs
|
||||
@@ -331,6 +342,8 @@ function recordUpsertModal(collection, rawRecord, modalSettings) {
|
||||
// extend, not overwrite, to prevent reseting the reference passed down to the inputs
|
||||
Object.assign(data.originalRecord, structuredClone(record));
|
||||
Object.assign(data.record, structuredClone(record));
|
||||
deleteInternalKeys(data.originalRecord);
|
||||
deleteInternalKeys(data.record);
|
||||
}
|
||||
|
||||
modalSettings.onsave?.(record, isNew);
|
||||
|
||||
Reference in New Issue
Block a user