merge newui branch
This commit is contained in:
@@ -0,0 +1,347 @@
|
||||
import { toDeleteProp } from "@/base/fieldSettings";
|
||||
|
||||
window.app = window.app || {};
|
||||
window.app.modals = window.app.modals || {};
|
||||
|
||||
window.app.modals.openCollectionChangesConfirmation = async function(
|
||||
oldCollection,
|
||||
newCollection,
|
||||
yesCallback,
|
||||
noCallback,
|
||||
) {
|
||||
const data = store({
|
||||
isLoadingConflictingOIDCProviders: false,
|
||||
conflictingOIDCProviders: [],
|
||||
// ---
|
||||
get isCollectionRenamed() {
|
||||
return oldCollection?.name != newCollection?.name;
|
||||
},
|
||||
get isNewCollectionAuth() {
|
||||
return newCollection?.type === "auth";
|
||||
},
|
||||
get isNewCollectionView() {
|
||||
return newCollection?.type === "view";
|
||||
},
|
||||
get renamedFields() {
|
||||
if (data.isNewCollectionView) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return newCollection?.fields?.filter?.((f) => {
|
||||
let oldField;
|
||||
if (f.id && !f[toDeleteProp]) {
|
||||
oldField = oldCollection.fields?.find?.((old) => old.id == f.id);
|
||||
}
|
||||
|
||||
return oldField && oldField.name != f.name;
|
||||
}) || [];
|
||||
},
|
||||
get deletedFields() {
|
||||
if (data.isNewCollectionView) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return newCollection?.fields?.filter?.((f) => {
|
||||
return f.id && f[toDeleteProp];
|
||||
}) || [];
|
||||
},
|
||||
get multipleToSingleFields() {
|
||||
if (data.isNewCollectionView) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return newCollection?.fields?.filter?.((newField) => {
|
||||
const oldField = oldCollection?.fields?.find?.((f) => f.id == newField.id);
|
||||
if (!oldField || typeof oldField.maxSelect == "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
// normalize
|
||||
const oldMaxSelect = oldField.maxSelect || 1;
|
||||
const newMaxSelect = newField.maxSelect || 1;
|
||||
|
||||
return oldMaxSelect > 1 && newMaxSelect == 1;
|
||||
}) || [];
|
||||
},
|
||||
get changedRules() {
|
||||
// for now enable only for "production"
|
||||
if (window.location.protocol != "https:") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = [];
|
||||
|
||||
const ruleProps = ["listRule", "viewRule"];
|
||||
if (!data.isNewCollectionView) {
|
||||
ruleProps.push("createRule", "updateRule", "deleteRule");
|
||||
}
|
||||
if (data.isNewCollectionAuth) {
|
||||
ruleProps.push("manageRule", "authRule");
|
||||
}
|
||||
|
||||
let oldRule, newRule;
|
||||
for (let prop of ruleProps) {
|
||||
oldRule = oldCollection?.[prop];
|
||||
newRule = newCollection?.[prop];
|
||||
if (oldRule === newRule) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push({ prop, oldRule, newRule });
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
get needConfirmation() {
|
||||
return !app.utils.isEmpty(oldCollection?.id) && (
|
||||
data.isCollectionRenamed
|
||||
|| data.renamedFields.length
|
||||
|| data.deletedFields.length
|
||||
|| data.multipleToSingleFields.length
|
||||
|| data.changedRules.length
|
||||
|| data.conflictingOIDCProviders.length
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const knownOIDCProviders = ["oidc", "oidc2", "oidc3"];
|
||||
|
||||
async function detectConflictingOIDCProviders() {
|
||||
if (app.utils.isEmpty(oldCollection?.id) || !data.isNewCollectionAuth) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.isLoadingConflictingOIDCProviders = true;
|
||||
|
||||
try {
|
||||
data.conflictingOIDCProviders = [];
|
||||
|
||||
for (const name of knownOIDCProviders) {
|
||||
const oldProvider = oldCollection?.oauth2?.providers?.find?.((p) => p.name == name);
|
||||
const newProvider = newCollection?.oauth2?.providers?.find?.((p) => p.name == name);
|
||||
|
||||
if (!oldProvider || !newProvider) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const oldHost = new URL(oldProvider.authURL).host;
|
||||
const newHost = new URL(newProvider.authURL).host;
|
||||
if (oldHost == newHost) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if there are existing externalAuths
|
||||
const haveExternalAuths = await app.pb.collection("_externalAuths").getFirstListItem(
|
||||
app.pb.filter("collectionRef={:collectionId} && provider={:provider}", {
|
||||
collectionId: newCollection?.id,
|
||||
provider: name,
|
||||
}),
|
||||
{
|
||||
requestKey: null,
|
||||
},
|
||||
);
|
||||
if (haveExternalAuths) {
|
||||
data.conflictingOIDCProviders.push({ name, oldHost, newHost });
|
||||
}
|
||||
}
|
||||
|
||||
data.isLoadingConflictingOIDCProviders = false;
|
||||
} catch (err) {
|
||||
if (err.isAbort) {
|
||||
data.isLoadingConflictingOIDCProviders = false;
|
||||
app.checkApiError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await detectConflictingOIDCProviders();
|
||||
|
||||
if (!data.needConfirmation) {
|
||||
return yesCallback();
|
||||
}
|
||||
|
||||
app.modals.confirm(
|
||||
t.div(
|
||||
{ className: "dangerous-collection-changes-list" },
|
||||
t.h5({ className: "block txt-center m-b-base" }, "Do you really want to save the collection changes?"),
|
||||
// general collection warning
|
||||
() => {
|
||||
if (!data.isCollectionRenamed && !data.deletedFields.length && !data.renamedFields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t.div(
|
||||
{ className: "alert warning m-b-base" },
|
||||
t.p(
|
||||
null,
|
||||
"If the collection participate in another collection rule, filter or view query, you'll have to update it manually!",
|
||||
),
|
||||
() => {
|
||||
if (data.deletedFields.length) {
|
||||
return t.p(
|
||||
null,
|
||||
"All data associated with the removed fields will be permanently deleted!",
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
// renamed collection
|
||||
() => {
|
||||
if (!data.isCollectionRenamed) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t.ul(
|
||||
{ className: "collection-changes-list changes-renamed-collection" },
|
||||
t.li(
|
||||
{ className: "list-item" },
|
||||
"Renamed collection ",
|
||||
t.strong({ className: "label warning" }, oldCollection?.name),
|
||||
t.i({ className: "ri-arrow-right-line txt-sm" }),
|
||||
t.strong({ className: "label success" }, newCollection?.name || "N/A"),
|
||||
),
|
||||
);
|
||||
},
|
||||
// renamed fields
|
||||
() => {
|
||||
if (!data.renamedFields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t.ul(
|
||||
{ className: "collection-changes-list changes-renamed-fields" },
|
||||
() => {
|
||||
return data.renamedFields.map((newField) => {
|
||||
const oldField = oldCollection?.fields?.find?.((f) => f.id == newField.id);
|
||||
return t.li(
|
||||
{ className: "list-item" },
|
||||
"Renamed field ",
|
||||
t.strong({ className: "label warning" }, oldField?.name),
|
||||
t.i({ className: "ri-arrow-right-line txt-sm" }),
|
||||
t.strong({ className: "label success" }, newField.name || "N/A"),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
// deleted fields
|
||||
() => {
|
||||
if (!data.deletedFields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t.ul(
|
||||
{ className: "collection-changes-list changes-deleted-fields" },
|
||||
() => {
|
||||
return data.deletedFields.map((field) => {
|
||||
return t.li(
|
||||
{ className: "list-item" },
|
||||
"Deleted field ",
|
||||
t.strong({ className: "label danger" }, field.name || "N/A"),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
// multiple->single fields
|
||||
() => {
|
||||
if (!data.multipleToSingleFields.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t.ul(
|
||||
{ className: "collection-changes-list changes-multiple-to-single-fields" },
|
||||
() => {
|
||||
return data.multipleToSingleFields.map((field) => {
|
||||
return t.li(
|
||||
{ className: "list-item" },
|
||||
"Multiple to single value conversion of field ",
|
||||
t.strong({ className: "label warning" }, field.name || field.id),
|
||||
t.em({ className: "txt-sm" }, " (will keep only the last array item)"),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
// API rule changes
|
||||
() => {
|
||||
if (!data.changedRules.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t.ul(
|
||||
{ className: "collection-changes-list changes-api-rules" },
|
||||
() => {
|
||||
return data.changedRules.map((ruleChange) => {
|
||||
return t.li(
|
||||
{ className: "list-item" },
|
||||
t.div(
|
||||
{ className: "content" },
|
||||
t.span({ className: "txt" }, "Changed API rule for "),
|
||||
t.code(null, ruleChange.prop),
|
||||
),
|
||||
t.small({ className: "txt-bold" }, "Old:"),
|
||||
t.div(
|
||||
{ className: "rule-content old-rule" },
|
||||
ruleChange.oldRule === null
|
||||
? "null (superusers only)"
|
||||
: (ruleChange.oldRule || "\"\""),
|
||||
),
|
||||
t.small({ className: "txt-bold" }, "New:"),
|
||||
t.div(
|
||||
{ className: "rule-content new-rule" },
|
||||
ruleChange.newRule === null
|
||||
? "null (superusers only)"
|
||||
: (ruleChange.newRule || "\"\""),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
// Conflicting OIDC changes
|
||||
() => {
|
||||
if (!data.conflictingOIDCProviders.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return t.ul(
|
||||
{ className: "collection-changes-list changes-api-rules" },
|
||||
() => {
|
||||
return data.conflictingOIDCProviders.map((oidc) => {
|
||||
return t.li(
|
||||
{ className: "list-item" },
|
||||
"Changed OIDC ",
|
||||
oidc.name,
|
||||
" host ",
|
||||
t.strong({ className: "label warning" }, oidc.oldHost),
|
||||
t.i({ className: "ri-arrow-right-line txt-sm" }),
|
||||
t.strong({ className: "label success" }, oidc.newHost),
|
||||
t.br(),
|
||||
t.span(
|
||||
{ className: "txt-hint" },
|
||||
"If the old and new OIDC configuration is not for the same provider consider deleting",
|
||||
" all old _externalAuths records associated to the current collection and provider,",
|
||||
" otherwise it may result in account linking errors.",
|
||||
),
|
||||
" ",
|
||||
t.a({
|
||||
rel: "noopenener noreferrer",
|
||||
target: "_blank",
|
||||
href: () => {
|
||||
return `#/collections?collection=_externalAuths&filter=collectionRef%3D%22${newCollection?.id}%22+%26%26+provider%3D%22${oidc.name}%22`;
|
||||
},
|
||||
textContent: "Review existing _externalAuths records",
|
||||
}),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
yesCallback,
|
||||
noCallback,
|
||||
{ className: "collection-changes-confirm-modal" },
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user