@nocobase/plugin-multi-portal 2.2.0-alpha.11 → 2.2.0-alpha.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/dist/client-v2/354.7be267ae5d61a878.js +10 -0
- package/dist/client-v2/556.5faf86d4f8faef40.js +10 -0
- package/dist/client-v2/778.c98e7ec542d931fe.js +10 -0
- package/dist/client-v2/RootLanding.d.ts +19 -0
- package/dist/client-v2/index.js +1 -1
- package/dist/client-v2/layoutRegistration.d.ts +2 -1
- package/dist/client-v2/models/MultiPortalMobilePageModels.d.ts +3 -11
- package/dist/client-v2/pages/MultiPortalsPage.d.ts +24 -0
- package/dist/client-v2/pages/PortalRoutesDrawer.d.ts +13 -0
- package/dist/client-v2/permissions/MultiPortalPermissionsTab.d.ts +1 -0
- package/dist/client-v2/routeRepositoryScope.d.ts +5 -2
- package/dist/constants.d.ts +11 -0
- package/dist/constants.js +48 -0
- package/dist/externalVersion.js +10 -9
- package/dist/locale/en-US.json +52 -1
- package/dist/locale/zh-CN.json +79 -28
- package/dist/node_modules/tar/package.json +1 -1
- package/dist/server/collections/desktopRoutes.js +1 -0
- package/dist/server/migrations/20260730120000-fix-fixed-layout-portal-type.d.ts +13 -0
- package/dist/server/migrations/20260730120000-fix-fixed-layout-portal-type.js +73 -0
- package/dist/server/plugin.d.ts +1 -1
- package/dist/server/plugin.js +1009 -271
- package/package.json +6 -7
- package/dist/client-v2/354.ead3e96143164cd5.js +0 -10
- package/dist/client-v2/778.23ba409a924ee34a.js +0 -10
- package/dist/client-v2/845.f2b0ce44ae4be707.js +0 -10
package/dist/server/plugin.js
CHANGED
|
@@ -41,6 +41,7 @@ __export(plugin_exports, {
|
|
|
41
41
|
});
|
|
42
42
|
module.exports = __toCommonJS(plugin_exports);
|
|
43
43
|
var import_server = require("@nocobase/server");
|
|
44
|
+
var import_database = require("@nocobase/database");
|
|
44
45
|
var import_utils = require("@nocobase/utils");
|
|
45
46
|
var import_child_process = require("child_process");
|
|
46
47
|
var import_fs = __toESM(require("fs"));
|
|
@@ -51,6 +52,7 @@ var import_promises = require("stream/promises");
|
|
|
51
52
|
var tar = __toESM(require("tar"));
|
|
52
53
|
var import_zlib = require("zlib");
|
|
53
54
|
var import_ensureDefaultRoleMultiPortalAccess = require("./ensureDefaultRoleMultiPortalAccess");
|
|
55
|
+
var import_constants = require("../constants");
|
|
54
56
|
const MULTI_PORTAL_RUNTIME_FIELDS = [
|
|
55
57
|
"uid",
|
|
56
58
|
"title",
|
|
@@ -79,8 +81,8 @@ const MAIN_APP_NAME = "main";
|
|
|
79
81
|
const UNION_ROLE_KEY = "__union__";
|
|
80
82
|
const MULTI_PORTAL_MANIFEST_NAMESPACE = "multi-portal";
|
|
81
83
|
const MULTI_PORTAL_MANIFEST_SYNC_MESSAGE_TYPE = "multi-portal:app-manifest-changed";
|
|
82
|
-
const DEFAULT_INIT_PORTAL_TYPE = "
|
|
83
|
-
const DEFAULT_INIT_PORTAL_NAME = "
|
|
84
|
+
const DEFAULT_INIT_PORTAL_TYPE = "ai";
|
|
85
|
+
const DEFAULT_INIT_PORTAL_NAME = "main";
|
|
84
86
|
const DEFAULT_INIT_PORTAL_TEMPLATE = "@nocobase/portal-template-default";
|
|
85
87
|
const PORTAL_CLIENT_PREFIX = "x";
|
|
86
88
|
const PORTAL_DEPLOY_UPLOAD_LIMIT = 200 * 1024 * 1024;
|
|
@@ -91,8 +93,6 @@ const PORTAL_TEMPLATE_NPM_PACK_TIMEOUT_MS = 3e4;
|
|
|
91
93
|
const DEFAULT_MULTI_PORTAL_UID = "__default_portal__";
|
|
92
94
|
const MULTI_PORTAL_SLUG_PATTERN = /^[a-z0-9_-]+$/;
|
|
93
95
|
const INIT_PORTAL_TYPES = ["no-code", "ai"];
|
|
94
|
-
const DEFAULT_MULTI_PORTAL_UIDS = [DEFAULT_MULTI_PORTAL_UID];
|
|
95
|
-
const DEFAULT_MULTI_PORTAL_UID_SET = new Set(DEFAULT_MULTI_PORTAL_UIDS);
|
|
96
96
|
const MULTI_PORTAL_MANAGEMENT_ACTIONS = [
|
|
97
97
|
"multiPortals:list",
|
|
98
98
|
"multiPortals:get",
|
|
@@ -103,14 +103,22 @@ const MULTI_PORTAL_MANAGEMENT_ACTIONS = [
|
|
|
103
103
|
"multiPortals:destroy",
|
|
104
104
|
"multiPortals:deploy",
|
|
105
105
|
"multiPortals:pullSource",
|
|
106
|
-
"multiPortals:pushSource"
|
|
106
|
+
"multiPortals:pushSource",
|
|
107
|
+
"desktopRoutes:list",
|
|
108
|
+
"desktopRoutes:get",
|
|
109
|
+
"desktopRoutes:create",
|
|
110
|
+
"desktopRoutes:update",
|
|
111
|
+
"desktopRoutes:move",
|
|
112
|
+
"desktopRoutes:destroy",
|
|
113
|
+
"desktopRoutes:updateOrCreate",
|
|
114
|
+
"registry:list",
|
|
115
|
+
"registry:get"
|
|
107
116
|
];
|
|
108
117
|
const ROLE_MULTI_PORTAL_PERMISSION_ACTIONS = [
|
|
109
118
|
"roles.multiPortals:*",
|
|
110
119
|
"rolesMultiPortalDesktopRoutes:*",
|
|
111
120
|
"rolesMultiPortalRoutePolicies:*"
|
|
112
121
|
];
|
|
113
|
-
const EXPLICIT_DESKTOP_ROUTE_UI_LAYOUTS = Symbol("explicitDesktopRouteUiLayouts");
|
|
114
122
|
const SKIP_CREATE_PORTAL_DIRECTORY = Symbol("skipCreatePortalDirectory");
|
|
115
123
|
function getRecordField(record, field) {
|
|
116
124
|
if (!record || typeof record !== "object") {
|
|
@@ -174,6 +182,38 @@ function getDefaultMultiPortalRecord() {
|
|
|
174
182
|
uiLayoutUid: "admin-layout-model"
|
|
175
183
|
};
|
|
176
184
|
}
|
|
185
|
+
function getFreshMultiPortalRecords() {
|
|
186
|
+
return [getDefaultMultiPortalRecord()];
|
|
187
|
+
}
|
|
188
|
+
function getFixedLayoutMultiPortalRecords() {
|
|
189
|
+
return [
|
|
190
|
+
{
|
|
191
|
+
uid: import_constants.DEFAULT_ADMIN_MULTI_PORTAL_UID,
|
|
192
|
+
title: "Desktop layout",
|
|
193
|
+
icon: "DesktopOutlined",
|
|
194
|
+
portalType: "no-code",
|
|
195
|
+
portalName: "admin",
|
|
196
|
+
routePath: "/admin",
|
|
197
|
+
authCheck: true,
|
|
198
|
+
enabled: true,
|
|
199
|
+
uiLayoutUid: "admin-layout-model"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
uid: import_constants.DEFAULT_MOBILE_MULTI_PORTAL_UID,
|
|
203
|
+
title: "Mobile layout",
|
|
204
|
+
icon: "MobileOutlined",
|
|
205
|
+
portalType: "no-code",
|
|
206
|
+
portalName: "mobile",
|
|
207
|
+
routePath: "/mobile",
|
|
208
|
+
authCheck: true,
|
|
209
|
+
enabled: true,
|
|
210
|
+
uiLayoutUid: "mobile-layout-model"
|
|
211
|
+
}
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
function getFixedLayoutMultiPortalRecord(uid) {
|
|
215
|
+
return getFixedLayoutMultiPortalRecords().find((portal) => portal.uid === uid);
|
|
216
|
+
}
|
|
177
217
|
function isValidPortalDeploySegment(value) {
|
|
178
218
|
return /^[A-Za-z0-9_-]+$/.test(value);
|
|
179
219
|
}
|
|
@@ -556,7 +596,8 @@ async function buildPortalStorageItem(portalDir, item) {
|
|
|
556
596
|
const logPath = getPortalStorageLogPath(item);
|
|
557
597
|
const buildEnv = getPortalStorageCommandEnv({
|
|
558
598
|
NOCOBASE_API_URL: getPortalStorageApiUrl(item.appName),
|
|
559
|
-
NOCOBASE_PORTAL_BASE: getPortalDeployBasePath(item.appName, item.portalName)
|
|
599
|
+
NOCOBASE_PORTAL_BASE: getPortalDeployBasePath(item.appName, item.portalName),
|
|
600
|
+
COREPACK_ENABLE_PROJECT_SPEC: "0"
|
|
560
601
|
});
|
|
561
602
|
await appendPortalStorageLog(logPath, `Building portal ${item.appName}/${item.portalName}.`);
|
|
562
603
|
await appendPortalStorageLog(
|
|
@@ -790,29 +831,6 @@ async function replacePortalSourceFromArchive(params) {
|
|
|
790
831
|
await import_fs.default.promises.rm(tarPath, { force: true });
|
|
791
832
|
}
|
|
792
833
|
}
|
|
793
|
-
function isDefaultMultiPortalUid(uid) {
|
|
794
|
-
return typeof uid === "string" && DEFAULT_MULTI_PORTAL_UID_SET.has(uid);
|
|
795
|
-
}
|
|
796
|
-
function getDefaultMultiPortalRecordForUid(uid) {
|
|
797
|
-
return isDefaultMultiPortalUid(uid) ? getDefaultMultiPortalRecord() : void 0;
|
|
798
|
-
}
|
|
799
|
-
function getDefaultMultiPortalUiLayoutUid(portal) {
|
|
800
|
-
return Object.prototype.hasOwnProperty.call(portal, "uiLayoutUid") ? portal.uiLayoutUid ?? null : null;
|
|
801
|
-
}
|
|
802
|
-
function withCustomMultiPortalFilter(filter = {}) {
|
|
803
|
-
const customFilter = { ...filter };
|
|
804
|
-
const uidFilter = customFilter.uid;
|
|
805
|
-
if (Array.isArray(uidFilter)) {
|
|
806
|
-
customFilter.uid = uidFilter.filter((uid) => !isDefaultMultiPortalUid(uid));
|
|
807
|
-
return customFilter;
|
|
808
|
-
}
|
|
809
|
-
if (isDefaultMultiPortalUid(uidFilter)) {
|
|
810
|
-
customFilter.uid = [];
|
|
811
|
-
return customFilter;
|
|
812
|
-
}
|
|
813
|
-
customFilter["uid.$notIn"] = DEFAULT_MULTI_PORTAL_UIDS;
|
|
814
|
-
return customFilter;
|
|
815
|
-
}
|
|
816
834
|
function uniqueDesktopRouteIds(routeIds) {
|
|
817
835
|
const seen = /* @__PURE__ */ new Set();
|
|
818
836
|
return routeIds.filter((routeId) => {
|
|
@@ -867,14 +885,25 @@ function getExplicitRequestedLayoutUid(layout) {
|
|
|
867
885
|
return uid;
|
|
868
886
|
}
|
|
869
887
|
}
|
|
888
|
+
function hasActionParam(params, key) {
|
|
889
|
+
return isRecordLike(params) && Object.prototype.hasOwnProperty.call(params, key);
|
|
890
|
+
}
|
|
870
891
|
function getRequestedMultiPortalUid(ctx) {
|
|
871
|
-
var _a, _b;
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
|
|
892
|
+
var _a, _b, _c;
|
|
893
|
+
const hasPortalScope = hasActionParam((_a = ctx.action) == null ? void 0 : _a.params, "portal");
|
|
894
|
+
const hasLayoutScope = hasActionParam((_b = ctx.action) == null ? void 0 : _b.params, "layout");
|
|
895
|
+
const portalUid = getExplicitRequestedLayoutUid((_c = ctx.action) == null ? void 0 : _c.params.portal);
|
|
896
|
+
if (hasPortalScope && hasLayoutScope) {
|
|
875
897
|
ctx.throw(400, "layout and portal cannot be used together");
|
|
876
898
|
return;
|
|
877
899
|
}
|
|
900
|
+
if (!hasPortalScope) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
if (!portalUid) {
|
|
904
|
+
ctx.throw(400, "Invalid portal scope");
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
878
907
|
return portalUid;
|
|
879
908
|
}
|
|
880
909
|
function getCurrentRoles(ctx) {
|
|
@@ -899,27 +928,88 @@ function getMultiPortalNameFromValues(ctx) {
|
|
|
899
928
|
return portalName;
|
|
900
929
|
}
|
|
901
930
|
}
|
|
902
|
-
function
|
|
931
|
+
function getMultiPortalWriteRecords(ctx) {
|
|
903
932
|
var _a;
|
|
904
933
|
const values = (_a = ctx.action) == null ? void 0 : _a.params.values;
|
|
905
|
-
|
|
906
|
-
|
|
934
|
+
const records = Array.isArray(values) ? values : [values];
|
|
935
|
+
return records.filter(
|
|
936
|
+
(record) => !!record && typeof record === "object" && !Array.isArray(record)
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
async function getMultiPortalWriteTargets(ctx, fields) {
|
|
940
|
+
var _a, _b, _c, _d, _e;
|
|
941
|
+
const records = getMultiPortalWriteRecords(ctx);
|
|
942
|
+
if (!records.length || ((_a = ctx.action) == null ? void 0 : _a.actionName) === "create") {
|
|
943
|
+
return records.map((values2) => ({ values: values2 }));
|
|
944
|
+
}
|
|
945
|
+
const repository = ctx.db.getRepository("multiPortals");
|
|
946
|
+
const actionName = (_b = ctx.action) == null ? void 0 : _b.actionName;
|
|
947
|
+
const requestedFilterKeys = (_c = ctx.action) == null ? void 0 : _c.params.filterKeys;
|
|
948
|
+
if ((actionName === "firstOrCreate" || actionName === "updateOrCreate") && Array.isArray(requestedFilterKeys) && requestedFilterKeys.every((filterKey) => typeof filterKey === "string")) {
|
|
949
|
+
const targets = [];
|
|
950
|
+
for (const values2 of records) {
|
|
951
|
+
const existing2 = await repository.findOne({
|
|
952
|
+
filter: import_database.Repository.valuesToFilter(values2, requestedFilterKeys),
|
|
953
|
+
fields
|
|
954
|
+
});
|
|
955
|
+
targets.push({ existing: existing2, values: values2 });
|
|
956
|
+
}
|
|
957
|
+
return targets;
|
|
958
|
+
}
|
|
959
|
+
if (records.length > 1) {
|
|
960
|
+
const targets = [];
|
|
961
|
+
for (const values2 of records) {
|
|
962
|
+
const uid2 = values2.uid;
|
|
963
|
+
const existing2 = typeof uid2 === "string" && uid2 ? await repository.findOne({
|
|
964
|
+
filterByTk: uid2,
|
|
965
|
+
fields
|
|
966
|
+
}) : null;
|
|
967
|
+
targets.push({ existing: existing2, values: values2 });
|
|
968
|
+
}
|
|
969
|
+
return targets;
|
|
970
|
+
}
|
|
971
|
+
const values = records[0];
|
|
972
|
+
const filterByTk = (_d = ctx.action) == null ? void 0 : _d.params.filterByTk;
|
|
973
|
+
if (filterByTk !== void 0 && filterByTk !== null) {
|
|
974
|
+
const existingRecords = await repository.find({
|
|
975
|
+
filterByTk,
|
|
976
|
+
fields
|
|
977
|
+
});
|
|
978
|
+
return existingRecords.length ? existingRecords.map((existing2) => ({ existing: existing2, values })) : [{ values }];
|
|
979
|
+
}
|
|
980
|
+
const filter = (_e = ctx.action) == null ? void 0 : _e.params.filter;
|
|
981
|
+
if (isRecordLike(filter) && !Array.isArray(filter)) {
|
|
982
|
+
const existingRecords = await repository.find({
|
|
983
|
+
filter,
|
|
984
|
+
fields
|
|
985
|
+
});
|
|
986
|
+
return existingRecords.length ? existingRecords.map((existing2) => ({ existing: existing2, values })) : [{ values }];
|
|
907
987
|
}
|
|
908
|
-
|
|
988
|
+
const uid = values.uid;
|
|
989
|
+
const existing = typeof uid === "string" && uid ? await repository.findOne({
|
|
990
|
+
filterByTk: uid,
|
|
991
|
+
fields
|
|
992
|
+
}) : null;
|
|
993
|
+
return [{ existing, values }];
|
|
909
994
|
}
|
|
910
995
|
function isTruthyActionBoolean(value) {
|
|
911
996
|
return value === true || value === 1 || value === "1" || value === "true";
|
|
912
997
|
}
|
|
913
998
|
async function captureSkipCreatePortalDirectory(ctx, next) {
|
|
914
|
-
const
|
|
915
|
-
|
|
999
|
+
const records = getMultiPortalWriteRecords(ctx);
|
|
1000
|
+
const recordsWithOption = records.filter(
|
|
1001
|
+
(record) => Object.prototype.hasOwnProperty.call(record, "skipCreatePortalDirectory")
|
|
1002
|
+
);
|
|
1003
|
+
if (!recordsWithOption.length) {
|
|
916
1004
|
await next();
|
|
917
1005
|
return;
|
|
918
1006
|
}
|
|
919
|
-
ctx[SKIP_CREATE_PORTAL_DIRECTORY] =
|
|
920
|
-
|
|
1007
|
+
ctx[SKIP_CREATE_PORTAL_DIRECTORY] = recordsWithOption.some(
|
|
1008
|
+
(record) => isTruthyActionBoolean(record.skipCreatePortalDirectory)
|
|
921
1009
|
);
|
|
922
|
-
|
|
1010
|
+
for (const record of recordsWithOption) {
|
|
1011
|
+
delete record.skipCreatePortalDirectory;
|
|
1012
|
+
}
|
|
923
1013
|
await next();
|
|
924
1014
|
}
|
|
925
1015
|
function shouldSkipCreatePortalDirectory(options) {
|
|
@@ -927,156 +1017,180 @@ function shouldSkipCreatePortalDirectory(options) {
|
|
|
927
1017
|
return ((_a = options == null ? void 0 : options.context) == null ? void 0 : _a[SKIP_CREATE_PORTAL_DIRECTORY]) === true;
|
|
928
1018
|
}
|
|
929
1019
|
async function normalizeMultiPortalSlugValues(ctx, next) {
|
|
930
|
-
const values
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1020
|
+
for (const values of getMultiPortalWriteRecords(ctx)) {
|
|
1021
|
+
const portalName = values.portalName;
|
|
1022
|
+
if (portalName === void 0 || portalName === null || typeof portalName !== "string") {
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
const slug = portalName.trim();
|
|
1026
|
+
if (!slug) {
|
|
1027
|
+
continue;
|
|
1028
|
+
}
|
|
1029
|
+
if (!MULTI_PORTAL_SLUG_PATTERN.test(slug)) {
|
|
1030
|
+
ctx.throw(400, "Portal name can only contain lowercase letters, numbers, hyphens, and underscores");
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
values.portalName = slug;
|
|
1034
|
+
values.routePath = `/${slug}`;
|
|
939
1035
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1036
|
+
await next();
|
|
1037
|
+
}
|
|
1038
|
+
function isUniqueConstraintViolation(error) {
|
|
1039
|
+
return error instanceof import_database.UniqueConstraintError || isRecordLike(error) && error.name === "SequelizeUniqueConstraintError";
|
|
1040
|
+
}
|
|
1041
|
+
async function createDefaultMultiPortalBestEffort(db, portal) {
|
|
1042
|
+
const repository = db.getRepository("multiPortals");
|
|
1043
|
+
const [uidConflict, portalNameConflict] = await Promise.all([
|
|
1044
|
+
repository.findOne({
|
|
1045
|
+
filterByTk: portal.uid,
|
|
1046
|
+
fields: ["uid"]
|
|
1047
|
+
}),
|
|
1048
|
+
repository.findOne({
|
|
1049
|
+
filter: { portalName: portal.portalName },
|
|
1050
|
+
fields: ["uid"]
|
|
1051
|
+
})
|
|
1052
|
+
]);
|
|
1053
|
+
if (uidConflict || portalNameConflict) {
|
|
1054
|
+
return false;
|
|
944
1055
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
return;
|
|
1056
|
+
try {
|
|
1057
|
+
await repository.create({ values: portal });
|
|
1058
|
+
return true;
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
if (isUniqueConstraintViolation(error)) {
|
|
1061
|
+
return false;
|
|
1062
|
+
}
|
|
1063
|
+
throw error;
|
|
948
1064
|
}
|
|
949
|
-
values.portalName = slug;
|
|
950
|
-
values.routePath = `/${slug}`;
|
|
951
|
-
await next();
|
|
952
1065
|
}
|
|
953
|
-
async function
|
|
1066
|
+
async function repairFixedLayoutMultiPortalRecords(db) {
|
|
954
1067
|
const repository = db.getRepository("multiPortals");
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
transaction: options == null ? void 0 : options.transaction
|
|
960
|
-
});
|
|
961
|
-
if (!existing) {
|
|
962
|
-
await repository.create({
|
|
963
|
-
values: defaultPortal,
|
|
964
|
-
transaction: options == null ? void 0 : options.transaction
|
|
1068
|
+
for (const portal of getFixedLayoutMultiPortalRecords()) {
|
|
1069
|
+
const existing = await repository.findOne({
|
|
1070
|
+
filterByTk: portal.uid,
|
|
1071
|
+
fields: ["uid", "portalType", "portalName", "routePath", "uiLayoutUid"]
|
|
965
1072
|
});
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
uid: defaultPortal.uid,
|
|
970
|
-
portalType: defaultPortal.portalType ?? null,
|
|
971
|
-
portalName: defaultPortal.portalName,
|
|
972
|
-
routePath: defaultPortal.routePath,
|
|
973
|
-
authCheck: defaultPortal.authCheck,
|
|
974
|
-
uiLayoutUid: getDefaultMultiPortalUiLayoutUid(defaultPortal)
|
|
975
|
-
};
|
|
976
|
-
const shouldRepair = Object.entries(protectedValues).some(([field, value]) => existing.get(field) !== value);
|
|
977
|
-
if (shouldRepair) {
|
|
1073
|
+
if ((existing == null ? void 0 : existing.get("portalName")) !== portal.portalName || existing.get("routePath") !== portal.routePath || existing.get("uiLayoutUid") !== portal.uiLayoutUid || existing.get("portalType") === portal.portalType) {
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
978
1076
|
await repository.update({
|
|
979
|
-
filterByTk:
|
|
980
|
-
values:
|
|
981
|
-
|
|
1077
|
+
filterByTk: portal.uid,
|
|
1078
|
+
values: {
|
|
1079
|
+
portalType: portal.portalType
|
|
1080
|
+
}
|
|
982
1081
|
});
|
|
983
1082
|
}
|
|
984
1083
|
}
|
|
985
|
-
async function
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
if (!defaultPortal) {
|
|
989
|
-
await next();
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
const params = (_b = ctx.action) == null ? void 0 : _b.params;
|
|
993
|
-
const rawValues = isRecordLike(params == null ? void 0 : params.values) ? params.values : {};
|
|
994
|
-
const values = {
|
|
995
|
-
uid: defaultPortal.uid,
|
|
996
|
-
portalType: defaultPortal.portalType ?? null,
|
|
997
|
-
portalName: defaultPortal.portalName,
|
|
998
|
-
routePath: defaultPortal.routePath,
|
|
999
|
-
authCheck: defaultPortal.authCheck,
|
|
1000
|
-
uiLayoutUid: getDefaultMultiPortalUiLayoutUid(defaultPortal)
|
|
1001
|
-
};
|
|
1002
|
-
if (Object.prototype.hasOwnProperty.call(rawValues, "title")) {
|
|
1003
|
-
values.title = rawValues.title;
|
|
1084
|
+
async function seedFreshMultiPortals(db) {
|
|
1085
|
+
for (const portal of getFreshMultiPortalRecords()) {
|
|
1086
|
+
await createDefaultMultiPortalBestEffort(db, portal);
|
|
1004
1087
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1088
|
+
}
|
|
1089
|
+
async function seedHistoricalMultiPortals(db) {
|
|
1090
|
+
if (getInitPortalType() === "ai") {
|
|
1091
|
+
await createDefaultMultiPortalBestEffort(db, getDefaultMultiPortalRecord());
|
|
1007
1092
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1093
|
+
for (const portal of getFixedLayoutMultiPortalRecords()) {
|
|
1094
|
+
await createDefaultMultiPortalBestEffort(db, portal);
|
|
1010
1095
|
}
|
|
1011
|
-
(
|
|
1012
|
-
{
|
|
1013
|
-
values
|
|
1014
|
-
},
|
|
1015
|
-
{
|
|
1016
|
-
values: "overwrite"
|
|
1017
|
-
}
|
|
1018
|
-
);
|
|
1019
|
-
await next();
|
|
1096
|
+
await repairFixedLayoutMultiPortalRecords(db);
|
|
1020
1097
|
}
|
|
1021
1098
|
async function preventUiLayoutRouteNameConflict(ctx, next) {
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1099
|
+
const targets = await getMultiPortalWriteTargets(ctx, ["uid", "portalName", "uiLayoutUid"]);
|
|
1100
|
+
for (const { existing, values } of targets) {
|
|
1101
|
+
const existingUid = trimString(existing == null ? void 0 : existing.get("uid"));
|
|
1102
|
+
const uid = trimString(values.uid ?? existingUid);
|
|
1103
|
+
const portalName = trimString(values.portalName ?? (existing == null ? void 0 : existing.get("portalName")));
|
|
1104
|
+
const uiLayoutUid = trimString(values.uiLayoutUid ?? (existing == null ? void 0 : existing.get("uiLayoutUid")));
|
|
1105
|
+
const fixedPortal = getFixedLayoutMultiPortalRecord(existingUid) || getFixedLayoutMultiPortalRecord(uid);
|
|
1106
|
+
const canonicalPortal = (fixedPortal == null ? void 0 : fixedPortal.uid) === uid && (!existingUid || fixedPortal.uid === existingUid) && fixedPortal.portalName === portalName && fixedPortal.uiLayoutUid === uiLayoutUid;
|
|
1107
|
+
if (fixedPortal && !canonicalPortal) {
|
|
1108
|
+
ctx.throw(400, "Reserved Portal UID must match its canonical route and UI layout");
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
if (!portalName) {
|
|
1112
|
+
continue;
|
|
1113
|
+
}
|
|
1114
|
+
if (portalName === "admin" || portalName === "mobile") {
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
const existingInitPortal = (existing == null ? void 0 : existing.get("uid")) === DEFAULT_MULTI_PORTAL_UID && existing.get("portalName") === portalName;
|
|
1118
|
+
const uiLayout = await ctx.db.getRepository("uiLayouts").findOne({
|
|
1119
|
+
filter: {
|
|
1120
|
+
routeName: portalName
|
|
1121
|
+
},
|
|
1122
|
+
fields: ["uid"]
|
|
1123
|
+
});
|
|
1124
|
+
if (uiLayout && !existingInitPortal) {
|
|
1125
|
+
ctx.throw(400, "Portal route name conflicts with an existing UI layout");
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1031
1128
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1129
|
+
await next();
|
|
1130
|
+
}
|
|
1131
|
+
async function preventMultiPortalBackingLayoutChange(ctx, next) {
|
|
1132
|
+
const targets = await getMultiPortalWriteTargets(ctx, ["uiLayoutUid"]);
|
|
1133
|
+
for (const { existing, values } of targets) {
|
|
1134
|
+
if (!Object.prototype.hasOwnProperty.call(values, "uiLayoutUid")) {
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
if (existing && existing.get("uiLayoutUid") !== values.uiLayoutUid) {
|
|
1138
|
+
ctx.throw(400, "Portal backing UI layout cannot be changed");
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1041
1141
|
}
|
|
1042
1142
|
await next();
|
|
1043
1143
|
}
|
|
1044
|
-
async function findRequestedMultiPortal(ctx) {
|
|
1045
|
-
var _a;
|
|
1144
|
+
async function findRequestedMultiPortal(ctx, transaction) {
|
|
1046
1145
|
const portalUid = getRequestedMultiPortalUid(ctx);
|
|
1047
1146
|
if (portalUid === void 0) {
|
|
1048
1147
|
return {
|
|
1049
1148
|
requested: false
|
|
1050
1149
|
};
|
|
1051
1150
|
}
|
|
1052
|
-
const isExplicitPortalRequest = getExplicitRequestedLayoutUid((_a = ctx.action) == null ? void 0 : _a.params.portal) !== void 0;
|
|
1053
|
-
if (!isExplicitPortalRequest) {
|
|
1054
|
-
const uiLayout = await ctx.db.getRepository("uiLayouts").findOne({
|
|
1055
|
-
filter: {
|
|
1056
|
-
uid: portalUid
|
|
1057
|
-
},
|
|
1058
|
-
fields: ["uid"]
|
|
1059
|
-
});
|
|
1060
|
-
if (uiLayout) {
|
|
1061
|
-
return {
|
|
1062
|
-
requested: false
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
1151
|
const portal = await ctx.db.getRepository("multiPortals").findOne({
|
|
1067
1152
|
filter: {
|
|
1068
1153
|
uid: portalUid
|
|
1069
1154
|
},
|
|
1070
|
-
fields: ["uid", "uiLayoutUid", "enabled"]
|
|
1155
|
+
fields: ["uid", "portalType", "uiLayoutUid", "enabled"],
|
|
1156
|
+
...transaction ? { lock: transaction.LOCK.UPDATE } : {},
|
|
1157
|
+
transaction
|
|
1071
1158
|
});
|
|
1072
|
-
if (
|
|
1073
|
-
return {
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
};
|
|
1159
|
+
if (!portal || portal.get("enabled") !== true) {
|
|
1160
|
+
return { requested: true };
|
|
1161
|
+
}
|
|
1162
|
+
if (portal.get("portalType") !== "no-code") {
|
|
1163
|
+
ctx.throw(400, `Portal '${portalUid}' does not support desktop routes`);
|
|
1164
|
+
}
|
|
1165
|
+
const uiLayoutUid = portal.get("uiLayoutUid");
|
|
1166
|
+
if (typeof uiLayoutUid !== "string" || !uiLayoutUid) {
|
|
1167
|
+
ctx.throw(400, `Portal '${portalUid}' has no backing UI layout`);
|
|
1077
1168
|
}
|
|
1169
|
+
const uiLayout = await ctx.db.getRepository("uiLayouts").findOne({
|
|
1170
|
+
filter: {
|
|
1171
|
+
uid: uiLayoutUid,
|
|
1172
|
+
enabled: true
|
|
1173
|
+
},
|
|
1174
|
+
fields: ["uid"],
|
|
1175
|
+
...transaction ? { lock: transaction.LOCK.UPDATE } : {},
|
|
1176
|
+
transaction
|
|
1177
|
+
});
|
|
1178
|
+
if (!uiLayout) {
|
|
1179
|
+
ctx.throw(400, `Portal '${portalUid}' has no enabled backing UI layout`);
|
|
1180
|
+
}
|
|
1181
|
+
const usesLayoutPermissions = (0, import_constants.isDefaultLayoutMultiPortalUid)(portalUid);
|
|
1078
1182
|
return {
|
|
1079
|
-
|
|
1183
|
+
portal,
|
|
1184
|
+
requested: true,
|
|
1185
|
+
scope: {
|
|
1186
|
+
portalUid,
|
|
1187
|
+
uiLayoutUid,
|
|
1188
|
+
relation: usesLayoutPermissions ? "uiLayouts" : "multiPortals",
|
|
1189
|
+
uid: usesLayoutPermissions ? uiLayoutUid : portalUid,
|
|
1190
|
+
filter: usesLayoutPermissions ? {
|
|
1191
|
+
"uiLayouts.uid": uiLayoutUid
|
|
1192
|
+
} : getDesktopRoutePortalFilter(portalUid)
|
|
1193
|
+
}
|
|
1080
1194
|
};
|
|
1081
1195
|
}
|
|
1082
1196
|
function getDesktopRoutePortalFilter(multiPortalUid) {
|
|
@@ -1087,49 +1201,90 @@ function getDesktopRoutePortalFilter(multiPortalUid) {
|
|
|
1087
1201
|
function isDesktopRouteCreateValue(value) {
|
|
1088
1202
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1089
1203
|
}
|
|
1090
|
-
function
|
|
1091
|
-
|
|
1204
|
+
function withDesktopRouteEffectiveOwner(value, scope) {
|
|
1205
|
+
if (Array.isArray(value)) {
|
|
1206
|
+
return value.map((item) => withDesktopRouteEffectiveOwner(item, scope));
|
|
1207
|
+
}
|
|
1208
|
+
if (!isDesktopRouteCreateValue(value)) {
|
|
1209
|
+
return value;
|
|
1210
|
+
}
|
|
1211
|
+
const { uiLayouts: _uiLayouts, multiPortals: _multiPortals, children, ...route } = value;
|
|
1212
|
+
return {
|
|
1213
|
+
...route,
|
|
1214
|
+
...scope.relation === "uiLayouts" ? { uiLayouts: [scope.uid] } : { multiPortals: [scope.uid] },
|
|
1215
|
+
...Array.isArray(children) ? { children: withDesktopRouteEffectiveOwner(children, scope) } : {}
|
|
1216
|
+
};
|
|
1092
1217
|
}
|
|
1093
|
-
function
|
|
1218
|
+
function collectExplicitDesktopRouteParentIds(value) {
|
|
1094
1219
|
if (Array.isArray(value)) {
|
|
1095
|
-
return value.
|
|
1220
|
+
return uniqueDesktopRouteIds(value.flatMap((item) => collectExplicitDesktopRouteParentIds(item)));
|
|
1096
1221
|
}
|
|
1097
1222
|
if (!isDesktopRouteCreateValue(value)) {
|
|
1098
|
-
return;
|
|
1223
|
+
return [];
|
|
1099
1224
|
}
|
|
1100
|
-
const
|
|
1101
|
-
if (
|
|
1102
|
-
|
|
1225
|
+
const parentIds = [];
|
|
1226
|
+
if (typeof value.parentId === "string" || typeof value.parentId === "number") {
|
|
1227
|
+
parentIds.push(value.parentId);
|
|
1103
1228
|
}
|
|
1104
1229
|
if (Array.isArray(value.children)) {
|
|
1105
|
-
|
|
1230
|
+
parentIds.push(...value.children.flatMap((child) => collectExplicitDesktopRouteParentIds(child)));
|
|
1106
1231
|
}
|
|
1107
|
-
return
|
|
1232
|
+
return uniqueDesktopRouteIds(parentIds);
|
|
1108
1233
|
}
|
|
1109
|
-
function
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
return
|
|
1234
|
+
async function assertDesktopRouteParentsBelongToScope(ctx, scope, values = ((_a) => (_a = ctx.action) == null ? void 0 : _a.params.values)(), transaction) {
|
|
1235
|
+
const parentIds = collectExplicitDesktopRouteParentIds(values);
|
|
1236
|
+
if (!parentIds.length) {
|
|
1237
|
+
return;
|
|
1113
1238
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1239
|
+
const parents = await ctx.db.getRepository("desktopRoutes").find({
|
|
1240
|
+
fields: ["id"],
|
|
1241
|
+
filter: {
|
|
1242
|
+
id: parentIds,
|
|
1243
|
+
...scope.filter
|
|
1244
|
+
},
|
|
1245
|
+
...transaction ? { lock: transaction.LOCK.UPDATE } : {},
|
|
1246
|
+
transaction
|
|
1247
|
+
});
|
|
1248
|
+
if (new Set(normalizeDesktopRouteTargetIds(parents)).size !== new Set(parentIds.map(String)).size) {
|
|
1249
|
+
ctx.throw(400, "Parent route does not belong to the requested portal scope");
|
|
1116
1250
|
}
|
|
1117
|
-
const explicitRoute = isDesktopRouteCreateValue(explicitValue) ? explicitValue : void 0;
|
|
1118
|
-
const existingMultiPortals = Array.isArray(value.multiPortals) ? value.multiPortals.filter((uid) => typeof uid === "string" && !!uid) : [];
|
|
1119
|
-
const { uiLayouts: _uiLayouts, children: _children, ...route } = value;
|
|
1120
|
-
return {
|
|
1121
|
-
...route,
|
|
1122
|
-
...hasDesktopRouteField(explicitRoute, "uiLayouts") ? { uiLayouts: explicitRoute == null ? void 0 : explicitRoute.uiLayouts } : {},
|
|
1123
|
-
multiPortals: Array.from(/* @__PURE__ */ new Set([...existingMultiPortals, multiPortalUid])),
|
|
1124
|
-
...Array.isArray(value.children) ? { children: withDesktopRouteMultiPortal(value.children, multiPortalUid, explicitRoute == null ? void 0 : explicitRoute.children) } : {}
|
|
1125
|
-
};
|
|
1126
1251
|
}
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1252
|
+
function getDesktopRouteUpdateOrCreateFilters(ctx, values = ((_b) => (_b = ctx.action) == null ? void 0 : _b.params.values)(), requestedFilterKeys = ((_c) => (_c = ctx.action) == null ? void 0 : _c.params.filterKeys)()) {
|
|
1253
|
+
const filterKeys = Array.isArray(requestedFilterKeys) ? requestedFilterKeys.filter((key) => typeof key === "string" && !!key) : [];
|
|
1254
|
+
if (!filterKeys.length) {
|
|
1255
|
+
return [];
|
|
1256
|
+
}
|
|
1257
|
+
const records = Array.isArray(values) ? values : [values];
|
|
1258
|
+
return records.flatMap((value) => {
|
|
1259
|
+
if (!isDesktopRouteCreateValue(value) || filterKeys.some((key) => !Object.prototype.hasOwnProperty.call(value, key))) {
|
|
1260
|
+
return [];
|
|
1261
|
+
}
|
|
1262
|
+
return [Object.fromEntries(filterKeys.map((key) => [key, value[key]]))];
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
async function assertDesktopRouteUpsertMatchesBelongToScope(ctx, scope, values = ((_d) => (_d = ctx.action) == null ? void 0 : _d.params.values)(), filterKeys = ((_e) => (_e = ctx.action) == null ? void 0 : _e.params.filterKeys)(), transaction) {
|
|
1266
|
+
for (const filter of getDesktopRouteUpdateOrCreateFilters(ctx, values, filterKeys)) {
|
|
1267
|
+
const allMatches = await ctx.db.getRepository("desktopRoutes").find({
|
|
1268
|
+
fields: ["id"],
|
|
1269
|
+
filter,
|
|
1270
|
+
...transaction ? { lock: transaction.LOCK.UPDATE } : {},
|
|
1271
|
+
transaction
|
|
1272
|
+
});
|
|
1273
|
+
const scopedMatches = await ctx.db.getRepository("desktopRoutes").find({
|
|
1274
|
+
fields: ["id"],
|
|
1275
|
+
filter: {
|
|
1276
|
+
...filter,
|
|
1277
|
+
...scope.filter
|
|
1278
|
+
},
|
|
1279
|
+
...transaction ? { lock: transaction.LOCK.UPDATE } : {},
|
|
1280
|
+
transaction
|
|
1281
|
+
});
|
|
1282
|
+
const allMatchIds = normalizeDesktopRouteTargetIds(allMatches);
|
|
1283
|
+
const scopedMatchIds = normalizeDesktopRouteTargetIds(scopedMatches);
|
|
1284
|
+
if (allMatchIds.join(",") !== scopedMatchIds.join(",")) {
|
|
1285
|
+
ctx.throw(400, "Desktop route does not belong to the requested portal scope");
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1133
1288
|
}
|
|
1134
1289
|
async function canAccessMultiPortal(ctx, multiPortalUid) {
|
|
1135
1290
|
const currentRoles = getCurrentRoles(ctx);
|
|
@@ -1379,25 +1534,17 @@ async function prepareAccessibleDesktopRoutesForMultiPortal(ctx) {
|
|
|
1379
1534
|
if (!portalRequest.requested) {
|
|
1380
1535
|
return;
|
|
1381
1536
|
}
|
|
1382
|
-
const
|
|
1383
|
-
if (!
|
|
1384
|
-
return null;
|
|
1385
|
-
}
|
|
1386
|
-
const portalUid = portal.get("uid");
|
|
1387
|
-
const uiLayoutUid = portal.get("uiLayoutUid");
|
|
1388
|
-
if (typeof portalUid !== "string" || !portalUid || typeof uiLayoutUid !== "string" || !uiLayoutUid) {
|
|
1537
|
+
const scope = portalRequest.scope;
|
|
1538
|
+
if (!scope) {
|
|
1389
1539
|
return null;
|
|
1390
1540
|
}
|
|
1391
|
-
if (!await canAccessMultiPortal(ctx, portalUid)) {
|
|
1541
|
+
if (scope.relation === "multiPortals" && !await canAccessMultiPortal(ctx, scope.portalUid)) {
|
|
1392
1542
|
return null;
|
|
1393
1543
|
}
|
|
1394
1544
|
if ((_a = ctx.action) == null ? void 0 : _a.params) {
|
|
1395
|
-
ctx.action.params.layout = uiLayoutUid;
|
|
1545
|
+
ctx.action.params.layout = scope.uiLayoutUid;
|
|
1396
1546
|
}
|
|
1397
|
-
return
|
|
1398
|
-
portalUid,
|
|
1399
|
-
uiLayoutUid
|
|
1400
|
-
};
|
|
1547
|
+
return scope;
|
|
1401
1548
|
}
|
|
1402
1549
|
async function addMultiPortalListAccessibleGuard(ctx, next) {
|
|
1403
1550
|
const portalContext = await prepareAccessibleDesktopRoutesForMultiPortal(ctx);
|
|
@@ -1406,10 +1553,13 @@ async function addMultiPortalListAccessibleGuard(ctx, next) {
|
|
|
1406
1553
|
return;
|
|
1407
1554
|
}
|
|
1408
1555
|
await next();
|
|
1409
|
-
if (portalContext) {
|
|
1556
|
+
if ((portalContext == null ? void 0 : portalContext.relation) === "multiPortals") {
|
|
1410
1557
|
await replaceListAccessibleRoutesWithPortalScopedRoutes(ctx, portalContext);
|
|
1411
1558
|
return;
|
|
1412
1559
|
}
|
|
1560
|
+
if ((portalContext == null ? void 0 : portalContext.relation) === "uiLayouts") {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1413
1563
|
await removeMultiPortalOwnedRoutesFromListResponse(ctx);
|
|
1414
1564
|
}
|
|
1415
1565
|
async function addMultiPortalGetAccessibleGuard(ctx, next) {
|
|
@@ -1420,26 +1570,36 @@ async function addMultiPortalGetAccessibleGuard(ctx, next) {
|
|
|
1420
1570
|
return;
|
|
1421
1571
|
}
|
|
1422
1572
|
await next();
|
|
1423
|
-
if (portalContext) {
|
|
1573
|
+
if ((portalContext == null ? void 0 : portalContext.relation) === "multiPortals") {
|
|
1424
1574
|
await replaceGetAccessibleRouteWithPortalScopedRoute(ctx, portalContext);
|
|
1425
1575
|
return;
|
|
1426
1576
|
}
|
|
1577
|
+
if ((portalContext == null ? void 0 : portalContext.relation) === "uiLayouts") {
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1427
1580
|
await removeMultiPortalOwnedRouteFromGetResponse(ctx);
|
|
1428
1581
|
}
|
|
1429
1582
|
async function mapMultiPortalLayoutToUiLayoutForRolePermissionTargets(ctx, next) {
|
|
1583
|
+
var _a;
|
|
1430
1584
|
const portalRequest = await findRequestedMultiPortal(ctx);
|
|
1431
1585
|
if (portalRequest.requested && !portalRequest.portal) {
|
|
1432
1586
|
ctx.status = 200;
|
|
1433
1587
|
ctx.body = [];
|
|
1434
1588
|
return;
|
|
1435
1589
|
}
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1590
|
+
const scope = portalRequest.scope;
|
|
1591
|
+
if ((scope == null ? void 0 : scope.relation) === "uiLayouts") {
|
|
1592
|
+
if ((_a = ctx.action) == null ? void 0 : _a.params) {
|
|
1593
|
+
ctx.action.params.layout = scope.uiLayoutUid;
|
|
1594
|
+
}
|
|
1595
|
+
await next();
|
|
1596
|
+
return;
|
|
1597
|
+
}
|
|
1598
|
+
if ((scope == null ? void 0 : scope.relation) === "multiPortals") {
|
|
1439
1599
|
const routes = await ctx.db.getRepository("desktopRoutes").find({
|
|
1440
1600
|
tree: true,
|
|
1441
1601
|
sort: "sort",
|
|
1442
|
-
filter:
|
|
1602
|
+
filter: scope.filter,
|
|
1443
1603
|
fields: [...DESKTOP_ROUTE_ROLE_PERMISSION_TARGET_FIELDS]
|
|
1444
1604
|
});
|
|
1445
1605
|
ctx.status = 200;
|
|
@@ -1450,38 +1610,585 @@ async function mapMultiPortalLayoutToUiLayoutForRolePermissionTargets(ctx, next)
|
|
|
1450
1610
|
await removeMultiPortalOwnedRoutesFromListResponse(ctx);
|
|
1451
1611
|
}
|
|
1452
1612
|
async function addDesktopRouteCreateMultiPortal(ctx, next) {
|
|
1453
|
-
var _a, _b;
|
|
1613
|
+
var _a, _b, _c;
|
|
1454
1614
|
const portalRequest = await findRequestedMultiPortal(ctx);
|
|
1455
1615
|
if (portalRequest.requested && !portalRequest.portal) {
|
|
1456
1616
|
ctx.throw(400, "Invalid portal scope");
|
|
1457
1617
|
return;
|
|
1458
1618
|
}
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1619
|
+
const scope = portalRequest.scope;
|
|
1620
|
+
if (scope) {
|
|
1621
|
+
await assertDesktopRouteParentsBelongToScope(ctx, scope);
|
|
1622
|
+
if (((_a = ctx.action) == null ? void 0 : _a.actionName) === "updateOrCreate") {
|
|
1623
|
+
await assertDesktopRouteUpsertMatchesBelongToScope(ctx, scope);
|
|
1624
|
+
}
|
|
1625
|
+
(_c = ctx.action) == null ? void 0 : _c.mergeParams(
|
|
1464
1626
|
{
|
|
1465
|
-
values:
|
|
1627
|
+
values: withDesktopRouteEffectiveOwner((_b = ctx.action) == null ? void 0 : _b.params.values, scope)
|
|
1466
1628
|
},
|
|
1467
1629
|
{
|
|
1468
1630
|
values: "overwrite"
|
|
1469
1631
|
}
|
|
1470
1632
|
);
|
|
1471
1633
|
await next();
|
|
1472
|
-
const desktopRouteIds = collectDesktopRouteIds(ctx.body);
|
|
1473
|
-
await removeLayoutRoutePermissionsFromPortalOnlyRoutes(ctx, desktopRouteIds);
|
|
1474
|
-
await grantDefaultRouteAccessToNewMultiPortalRoutes(ctx, portalUid, desktopRouteIds);
|
|
1475
1634
|
return;
|
|
1476
1635
|
}
|
|
1477
1636
|
await next();
|
|
1478
1637
|
}
|
|
1638
|
+
async function addDesktopRouteEffectiveScopeFilter(ctx, next) {
|
|
1639
|
+
var _a;
|
|
1640
|
+
const portalRequest = await findRequestedMultiPortal(ctx);
|
|
1641
|
+
if (!portalRequest.requested) {
|
|
1642
|
+
await next();
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
if (!portalRequest.scope) {
|
|
1646
|
+
ctx.throw(400, "Invalid portal scope");
|
|
1647
|
+
return;
|
|
1648
|
+
}
|
|
1649
|
+
(_a = ctx.action) == null ? void 0 : _a.mergeParams({
|
|
1650
|
+
filter: portalRequest.scope.filter
|
|
1651
|
+
});
|
|
1652
|
+
await next();
|
|
1653
|
+
}
|
|
1654
|
+
function withoutDesktopRouteOwnerFields(value) {
|
|
1655
|
+
if (Array.isArray(value)) {
|
|
1656
|
+
return value.map((item) => withoutDesktopRouteOwnerFields(item));
|
|
1657
|
+
}
|
|
1658
|
+
if (!isDesktopRouteCreateValue(value)) {
|
|
1659
|
+
return value;
|
|
1660
|
+
}
|
|
1661
|
+
const { uiLayouts: _uiLayouts, multiPortals: _multiPortals, children, ...route } = value;
|
|
1662
|
+
return {
|
|
1663
|
+
...route,
|
|
1664
|
+
...Array.isArray(children) ? { children: withoutDesktopRouteOwnerFields(children) } : {}
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
function normalizeDesktopRouteTargetIds(records) {
|
|
1668
|
+
return records.map((record) => record.get("id")).filter((id) => typeof id === "string" || typeof id === "number").map(String).sort();
|
|
1669
|
+
}
|
|
1670
|
+
async function findDesktopRouteMutationTargets(ctx, scopeFilter, transaction, selector) {
|
|
1671
|
+
var _a;
|
|
1672
|
+
const params = selector ?? ((_a = ctx.action) == null ? void 0 : _a.params) ?? {};
|
|
1673
|
+
const values = params.values;
|
|
1674
|
+
const batchUpdateIds = Array.isArray(values) ? values.flatMap((value) => {
|
|
1675
|
+
if (!isDesktopRouteCreateValue(value)) {
|
|
1676
|
+
return [];
|
|
1677
|
+
}
|
|
1678
|
+
const routeId = value.id;
|
|
1679
|
+
return typeof routeId === "string" || typeof routeId === "number" ? [routeId] : [];
|
|
1680
|
+
}) : [];
|
|
1681
|
+
const hasBatchUpdateTargets = batchUpdateIds.length > 0;
|
|
1682
|
+
const filterByTk = hasBatchUpdateTargets ? batchUpdateIds : params.filterByTk;
|
|
1683
|
+
const filter = !hasBatchUpdateTargets && isRecordLike(params.filter) ? params.filter : void 0;
|
|
1684
|
+
if ((filterByTk === void 0 || filterByTk === null) && !filter) {
|
|
1685
|
+
return [];
|
|
1686
|
+
}
|
|
1687
|
+
return ctx.db.getRepository("desktopRoutes").find({
|
|
1688
|
+
fields: ["id"],
|
|
1689
|
+
...filterByTk !== void 0 && filterByTk !== null ? { filterByTk } : {},
|
|
1690
|
+
filter: {
|
|
1691
|
+
...filter ?? {},
|
|
1692
|
+
...scopeFilter ?? {}
|
|
1693
|
+
},
|
|
1694
|
+
...transaction ? { lock: transaction.LOCK.UPDATE } : {},
|
|
1695
|
+
transaction
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
async function assertDesktopRouteMutationTargetsBelongToScope(ctx, scope, transaction, selector) {
|
|
1699
|
+
const allTargets = await findDesktopRouteMutationTargets(ctx, void 0, transaction, selector);
|
|
1700
|
+
const scopedTargets = await findDesktopRouteMutationTargets(ctx, scope.filter, transaction, selector);
|
|
1701
|
+
const allTargetIds = normalizeDesktopRouteTargetIds(allTargets);
|
|
1702
|
+
const scopedTargetIds = normalizeDesktopRouteTargetIds(scopedTargets);
|
|
1703
|
+
if (!allTargetIds.length || allTargetIds.join(",") !== scopedTargetIds.join(",")) {
|
|
1704
|
+
ctx.throw(400, "Desktop route does not belong to the requested portal scope");
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
async function guardDesktopRouteUpdateScope(ctx, next) {
|
|
1708
|
+
var _a, _b;
|
|
1709
|
+
const portalRequest = await findRequestedMultiPortal(ctx);
|
|
1710
|
+
if (!portalRequest.requested) {
|
|
1711
|
+
await next();
|
|
1712
|
+
return;
|
|
1713
|
+
}
|
|
1714
|
+
const scope = portalRequest.scope;
|
|
1715
|
+
if (!scope) {
|
|
1716
|
+
ctx.throw(400, "Invalid portal scope");
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
await assertDesktopRouteMutationTargetsBelongToScope(ctx, scope);
|
|
1720
|
+
await assertDesktopRouteParentsBelongToScope(ctx, scope);
|
|
1721
|
+
(_b = ctx.action) == null ? void 0 : _b.mergeParams(
|
|
1722
|
+
{
|
|
1723
|
+
values: withoutDesktopRouteOwnerFields((_a = ctx.action) == null ? void 0 : _a.params.values)
|
|
1724
|
+
},
|
|
1725
|
+
{
|
|
1726
|
+
values: "overwrite"
|
|
1727
|
+
}
|
|
1728
|
+
);
|
|
1729
|
+
await next();
|
|
1730
|
+
}
|
|
1731
|
+
function getDesktopRoutePortalContext(context) {
|
|
1732
|
+
var _a;
|
|
1733
|
+
if (!isRecordLike(context)) {
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
const ctx = context;
|
|
1737
|
+
if (((_a = ctx.action) == null ? void 0 : _a.resourceName) !== "desktopRoutes" || !hasActionParam(ctx.action.params, "portal")) {
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
return ctx;
|
|
1741
|
+
}
|
|
1742
|
+
async function resolveRequiredDesktopRouteScope(ctx, transaction) {
|
|
1743
|
+
const portalRequest = await findRequestedMultiPortal(ctx, transaction);
|
|
1744
|
+
if (!portalRequest.scope) {
|
|
1745
|
+
ctx.throw(400, "Invalid portal scope");
|
|
1746
|
+
}
|
|
1747
|
+
return portalRequest.scope;
|
|
1748
|
+
}
|
|
1749
|
+
function withDesktopRouteScopeFilter(filter, scope) {
|
|
1750
|
+
return {
|
|
1751
|
+
...isRecordLike(filter) ? filter : {},
|
|
1752
|
+
...scope.filter
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
class MultiPortalDesktopRouteRepository extends import_database.Repository {
|
|
1756
|
+
async withAuthoritativeScopeTransaction(existingTransaction, callback) {
|
|
1757
|
+
if (existingTransaction) {
|
|
1758
|
+
return callback(existingTransaction);
|
|
1759
|
+
}
|
|
1760
|
+
return this.database.sequelize.transaction(callback);
|
|
1761
|
+
}
|
|
1762
|
+
async create(options) {
|
|
1763
|
+
const ctx = getDesktopRoutePortalContext(options.context);
|
|
1764
|
+
if (!ctx) {
|
|
1765
|
+
return super.create(options);
|
|
1766
|
+
}
|
|
1767
|
+
return this.withAuthoritativeScopeTransaction(options.transaction, async (transaction) => {
|
|
1768
|
+
const scope = await resolveRequiredDesktopRouteScope(ctx, transaction);
|
|
1769
|
+
const values = withDesktopRouteEffectiveOwner(options.values, scope);
|
|
1770
|
+
await assertDesktopRouteParentsBelongToScope(ctx, scope, values, transaction);
|
|
1771
|
+
return super.create({
|
|
1772
|
+
...options,
|
|
1773
|
+
values,
|
|
1774
|
+
transaction
|
|
1775
|
+
});
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
async update(options) {
|
|
1779
|
+
const ctx = getDesktopRoutePortalContext(options.context);
|
|
1780
|
+
if (!ctx) {
|
|
1781
|
+
return super.update(options);
|
|
1782
|
+
}
|
|
1783
|
+
return this.withAuthoritativeScopeTransaction(options.transaction, async (transaction) => {
|
|
1784
|
+
const scope = await resolveRequiredDesktopRouteScope(ctx, transaction);
|
|
1785
|
+
await assertDesktopRouteMutationTargetsBelongToScope(ctx, scope, transaction, options);
|
|
1786
|
+
const values = withoutDesktopRouteOwnerFields(options.values);
|
|
1787
|
+
await assertDesktopRouteParentsBelongToScope(ctx, scope, values, transaction);
|
|
1788
|
+
return super.update({
|
|
1789
|
+
...options,
|
|
1790
|
+
filter: withDesktopRouteScopeFilter(options.filter, scope),
|
|
1791
|
+
values,
|
|
1792
|
+
transaction
|
|
1793
|
+
});
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
async updateOrCreate(options) {
|
|
1797
|
+
const ctx = getDesktopRoutePortalContext(options.context);
|
|
1798
|
+
if (!ctx) {
|
|
1799
|
+
return super.updateOrCreate(options);
|
|
1800
|
+
}
|
|
1801
|
+
return this.withAuthoritativeScopeTransaction(options.transaction, async (transaction) => {
|
|
1802
|
+
const scope = await resolveRequiredDesktopRouteScope(ctx, transaction);
|
|
1803
|
+
const values = withDesktopRouteEffectiveOwner(options.values, scope);
|
|
1804
|
+
await assertDesktopRouteParentsBelongToScope(ctx, scope, values, transaction);
|
|
1805
|
+
await assertDesktopRouteUpsertMatchesBelongToScope(ctx, scope, values, options.filterKeys, transaction);
|
|
1806
|
+
return super.updateOrCreate({
|
|
1807
|
+
...options,
|
|
1808
|
+
values,
|
|
1809
|
+
transaction
|
|
1810
|
+
});
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
async function guardDesktopRouteMoveScope(ctx, next) {
|
|
1815
|
+
var _a;
|
|
1816
|
+
const portalRequest = await findRequestedMultiPortal(ctx);
|
|
1817
|
+
if (!portalRequest.requested) {
|
|
1818
|
+
await next();
|
|
1819
|
+
return;
|
|
1820
|
+
}
|
|
1821
|
+
const params = ((_a = ctx.action) == null ? void 0 : _a.params) ?? {};
|
|
1822
|
+
const sourceId = params.sourceId;
|
|
1823
|
+
const targetId = params.targetId;
|
|
1824
|
+
const hasTargetId = targetId !== void 0 && targetId !== null;
|
|
1825
|
+
if (typeof sourceId !== "string" && typeof sourceId !== "number" || hasTargetId && typeof targetId !== "string" && typeof targetId !== "number" || params.sortField !== void 0 && params.sortField !== "sort") {
|
|
1826
|
+
ctx.throw(400, "Invalid desktop route move scope");
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
await ctx.db.sequelize.transaction(async (transaction) => {
|
|
1830
|
+
const scope = await resolveRequiredDesktopRouteScope(ctx, transaction);
|
|
1831
|
+
const routeIds = uniqueDesktopRouteIds(hasTargetId ? [sourceId, targetId] : [sourceId]);
|
|
1832
|
+
const allRoutes = await ctx.db.getRepository("desktopRoutes").find({
|
|
1833
|
+
fields: ["id", "parentId", "sort"],
|
|
1834
|
+
filter: {
|
|
1835
|
+
id: routeIds
|
|
1836
|
+
},
|
|
1837
|
+
lock: transaction.LOCK.UPDATE,
|
|
1838
|
+
transaction
|
|
1839
|
+
});
|
|
1840
|
+
const scopedRoutes = await ctx.db.getRepository("desktopRoutes").find({
|
|
1841
|
+
fields: ["id", "parentId", "sort"],
|
|
1842
|
+
filter: {
|
|
1843
|
+
id: routeIds,
|
|
1844
|
+
...scope.filter
|
|
1845
|
+
},
|
|
1846
|
+
lock: transaction.LOCK.UPDATE,
|
|
1847
|
+
transaction
|
|
1848
|
+
});
|
|
1849
|
+
if (normalizeDesktopRouteTargetIds(allRoutes).join(",") !== normalizeDesktopRouteTargetIds(scopedRoutes).join(",") || scopedRoutes.length !== routeIds.length) {
|
|
1850
|
+
ctx.throw(400, "Move target does not belong to the requested portal scope");
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
const routesById = new Map(
|
|
1854
|
+
scopedRoutes.map((route) => [String(route.get("id")), route])
|
|
1855
|
+
);
|
|
1856
|
+
let source = routesById.get(String(sourceId));
|
|
1857
|
+
const target = hasTargetId ? routesById.get(String(targetId)) : void 0;
|
|
1858
|
+
if (!source || hasTargetId && !target) {
|
|
1859
|
+
ctx.throw(400, "Move target does not belong to the requested portal scope");
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
const targetScope = isRecordLike(params.targetScope) ? params.targetScope : void 0;
|
|
1863
|
+
const requestedParentId = targetScope == null ? void 0 : targetScope.parentId;
|
|
1864
|
+
const parentIds = uniqueDesktopRouteIds(
|
|
1865
|
+
[source.get("parentId"), target == null ? void 0 : target.get("parentId"), requestedParentId].filter(
|
|
1866
|
+
(id) => typeof id === "string" || typeof id === "number"
|
|
1867
|
+
)
|
|
1868
|
+
);
|
|
1869
|
+
if (parentIds.length) {
|
|
1870
|
+
const parents = await ctx.db.getRepository("desktopRoutes").find({
|
|
1871
|
+
fields: ["id"],
|
|
1872
|
+
filter: {
|
|
1873
|
+
id: parentIds,
|
|
1874
|
+
...scope.filter
|
|
1875
|
+
},
|
|
1876
|
+
lock: transaction.LOCK.UPDATE,
|
|
1877
|
+
transaction
|
|
1878
|
+
});
|
|
1879
|
+
if (normalizeDesktopRouteTargetIds(parents).join(",") !== parentIds.map(String).sort().join(",")) {
|
|
1880
|
+
ctx.throw(400, "Move parent does not belong to the requested portal scope");
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
const updateSource = async (values, silent) => {
|
|
1885
|
+
const [updatedSource] = await ctx.db.getRepository("desktopRoutes").update({
|
|
1886
|
+
filterByTk: sourceId,
|
|
1887
|
+
values,
|
|
1888
|
+
silent,
|
|
1889
|
+
transaction
|
|
1890
|
+
});
|
|
1891
|
+
if (!updatedSource) {
|
|
1892
|
+
ctx.throw(400, "Move source does not belong to the requested portal scope");
|
|
1893
|
+
}
|
|
1894
|
+
source = updatedSource;
|
|
1895
|
+
};
|
|
1896
|
+
if (target) {
|
|
1897
|
+
const targetParentId = target.get("parentId");
|
|
1898
|
+
if (source.get("parentId") !== targetParentId) {
|
|
1899
|
+
await updateSource({ parentId: targetParentId }, false);
|
|
1900
|
+
}
|
|
1901
|
+
const sourceSort = source.get("sort");
|
|
1902
|
+
let targetSort = target.get("sort");
|
|
1903
|
+
if (typeof sourceSort !== "number" || typeof targetSort !== "number") {
|
|
1904
|
+
ctx.throw(400, "Invalid desktop route sort value");
|
|
1905
|
+
return;
|
|
1906
|
+
}
|
|
1907
|
+
if (params.method === "insertAfter") {
|
|
1908
|
+
targetSort += 1;
|
|
1909
|
+
}
|
|
1910
|
+
const movesForward = targetSort > sourceSort;
|
|
1911
|
+
const affectedRoutes = await ctx.db.getRepository("desktopRoutes").find({
|
|
1912
|
+
fields: ["id"],
|
|
1913
|
+
filter: {
|
|
1914
|
+
...scope.filter,
|
|
1915
|
+
parentId: targetParentId ?? null,
|
|
1916
|
+
sort: movesForward ? {
|
|
1917
|
+
$gt: sourceSort,
|
|
1918
|
+
$lte: targetSort
|
|
1919
|
+
} : {
|
|
1920
|
+
$lt: sourceSort,
|
|
1921
|
+
$gte: targetSort
|
|
1922
|
+
}
|
|
1923
|
+
},
|
|
1924
|
+
lock: transaction.LOCK.UPDATE,
|
|
1925
|
+
transaction
|
|
1926
|
+
});
|
|
1927
|
+
const affectedRouteIds = affectedRoutes.map((route) => route.get("id"));
|
|
1928
|
+
if (affectedRouteIds.length) {
|
|
1929
|
+
await ctx.db.getCollection("desktopRoutes").model.increment("sort", {
|
|
1930
|
+
where: {
|
|
1931
|
+
id: {
|
|
1932
|
+
[import_database.Op.in]: affectedRouteIds
|
|
1933
|
+
}
|
|
1934
|
+
},
|
|
1935
|
+
by: movesForward ? -1 : 1,
|
|
1936
|
+
silent: true,
|
|
1937
|
+
transaction
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
await updateSource({ sort: targetSort }, true);
|
|
1941
|
+
}
|
|
1942
|
+
if (requestedParentId && source.get("parentId") !== requestedParentId) {
|
|
1943
|
+
await updateSource({ parentId: requestedParentId }, false);
|
|
1944
|
+
if (params.method === "prepend") {
|
|
1945
|
+
await updateSource({ sort: 0 }, true);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
if (params.sticky) {
|
|
1949
|
+
await updateSource({ sort: 0 }, true);
|
|
1950
|
+
}
|
|
1951
|
+
});
|
|
1952
|
+
ctx.body = "ok";
|
|
1953
|
+
}
|
|
1954
|
+
function normalizeDesktopRouteFilterByTk(filterByTk) {
|
|
1955
|
+
const values = Array.isArray(filterByTk) ? filterByTk : [filterByTk];
|
|
1956
|
+
return uniqueDesktopRouteIds(
|
|
1957
|
+
values.filter((value) => typeof value === "string" || typeof value === "number")
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
async function getDesktopRouteDestroyOwnerScope(ctx, transaction) {
|
|
1961
|
+
var _a, _b, _c, _d;
|
|
1962
|
+
const hasPortalScope = hasActionParam((_a = ctx.action) == null ? void 0 : _a.params, "portal");
|
|
1963
|
+
const hasLayoutScope = hasActionParam((_b = ctx.action) == null ? void 0 : _b.params, "layout");
|
|
1964
|
+
const portalUid = getExplicitRequestedLayoutUid((_c = ctx.action) == null ? void 0 : _c.params.portal);
|
|
1965
|
+
const layoutUid = getExplicitRequestedLayoutUid((_d = ctx.action) == null ? void 0 : _d.params.layout);
|
|
1966
|
+
if (hasPortalScope && hasLayoutScope) {
|
|
1967
|
+
ctx.throw(400, "layout and portal cannot be used together");
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
if (hasPortalScope) {
|
|
1971
|
+
if (!portalUid) {
|
|
1972
|
+
ctx.throw(400, "Invalid portal scope");
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
const portalRequest = await findRequestedMultiPortal(ctx, transaction);
|
|
1976
|
+
if (!portalRequest.scope) {
|
|
1977
|
+
ctx.throw(400, "Invalid portal scope");
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
return portalRequest.scope;
|
|
1981
|
+
}
|
|
1982
|
+
if (!hasLayoutScope) {
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
if (!layoutUid) {
|
|
1986
|
+
ctx.throw(400, "Invalid layout scope");
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
const layout = await ctx.db.getRepository("uiLayouts").findOne({
|
|
1990
|
+
fields: ["uid"],
|
|
1991
|
+
filter: {
|
|
1992
|
+
uid: layoutUid
|
|
1993
|
+
},
|
|
1994
|
+
transaction
|
|
1995
|
+
});
|
|
1996
|
+
if (!layout) {
|
|
1997
|
+
ctx.throw(400, "Invalid layout scope");
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
return {
|
|
2001
|
+
filter: {
|
|
2002
|
+
"uiLayouts.uid": layoutUid
|
|
2003
|
+
},
|
|
2004
|
+
relation: "uiLayouts",
|
|
2005
|
+
uid: layoutUid
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
async function findDesktopRouteDestroyRoots(ctx, transaction, scopeFilter) {
|
|
2009
|
+
var _a, _b, _c;
|
|
2010
|
+
const filterByTk = normalizeDesktopRouteFilterByTk((_a = ctx.action) == null ? void 0 : _a.params.filterByTk);
|
|
2011
|
+
const actionFilter = isRecordLike((_b = ctx.action) == null ? void 0 : _b.params.filter) ? (_c = ctx.action) == null ? void 0 : _c.params.filter : void 0;
|
|
2012
|
+
if (!filterByTk.length && !actionFilter) {
|
|
2013
|
+
return [];
|
|
2014
|
+
}
|
|
2015
|
+
const records = await ctx.db.getRepository("desktopRoutes").find({
|
|
2016
|
+
fields: ["id"],
|
|
2017
|
+
filter: {
|
|
2018
|
+
...actionFilter ?? {},
|
|
2019
|
+
...scopeFilter ?? {},
|
|
2020
|
+
...filterByTk.length ? { id: filterByTk } : {}
|
|
2021
|
+
},
|
|
2022
|
+
lock: transaction.LOCK.UPDATE,
|
|
2023
|
+
transaction
|
|
2024
|
+
});
|
|
2025
|
+
return collectDesktopRouteIds(records);
|
|
2026
|
+
}
|
|
2027
|
+
async function findDesktopRouteSubtreeIds(ctx, rootId, transaction) {
|
|
2028
|
+
const rootRoutes = await ctx.db.getRepository("desktopRoutes").find({
|
|
2029
|
+
fields: ["id"],
|
|
2030
|
+
filter: {
|
|
2031
|
+
id: rootId
|
|
2032
|
+
},
|
|
2033
|
+
lock: transaction.LOCK.UPDATE,
|
|
2034
|
+
transaction
|
|
2035
|
+
});
|
|
2036
|
+
const routeIds = collectDesktopRouteIds(rootRoutes);
|
|
2037
|
+
let parentIds = [...routeIds];
|
|
2038
|
+
while (parentIds.length) {
|
|
2039
|
+
const children = await ctx.db.getRepository("desktopRoutes").find({
|
|
2040
|
+
fields: ["id"],
|
|
2041
|
+
filter: {
|
|
2042
|
+
parentId: parentIds
|
|
2043
|
+
},
|
|
2044
|
+
lock: transaction.LOCK.UPDATE,
|
|
2045
|
+
sort: ["id"],
|
|
2046
|
+
transaction
|
|
2047
|
+
});
|
|
2048
|
+
const childIds = collectDesktopRouteIds(children).filter((routeId) => !routeIds.includes(routeId));
|
|
2049
|
+
routeIds.push(...childIds);
|
|
2050
|
+
parentIds = childIds;
|
|
2051
|
+
}
|
|
2052
|
+
return routeIds;
|
|
2053
|
+
}
|
|
2054
|
+
function getDesktopRouteOwnerUids(route, relation) {
|
|
2055
|
+
const owners = getRecordField(route, relation);
|
|
2056
|
+
if (!Array.isArray(owners)) {
|
|
2057
|
+
return [];
|
|
2058
|
+
}
|
|
2059
|
+
return owners.map((owner) => getRecordField(owner, "uid")).filter((uid) => typeof uid === "string" && !!uid);
|
|
2060
|
+
}
|
|
2061
|
+
function desktopRouteHasOwnerOutsideScope(route, scope) {
|
|
2062
|
+
const multiPortalUids = getDesktopRouteOwnerUids(route, "multiPortals");
|
|
2063
|
+
const uiLayoutUids = getDesktopRouteOwnerUids(route, "uiLayouts");
|
|
2064
|
+
if (scope.relation === "multiPortals") {
|
|
2065
|
+
return multiPortalUids.some((uid) => uid !== scope.uid) || uiLayoutUids.length > 0;
|
|
2066
|
+
}
|
|
2067
|
+
return uiLayoutUids.some((uid) => uid !== scope.uid) || multiPortalUids.length > 0;
|
|
2068
|
+
}
|
|
2069
|
+
async function detachDesktopRouteTreeOwner(ctx, routeIds, scope, transaction) {
|
|
2070
|
+
if (scope.relation === "multiPortals") {
|
|
2071
|
+
await ctx.db.getRepository("rolesMultiPortalDesktopRoutes").destroy({
|
|
2072
|
+
filter: {
|
|
2073
|
+
multiPortalUid: scope.uid,
|
|
2074
|
+
desktopRouteId: routeIds
|
|
2075
|
+
},
|
|
2076
|
+
transaction
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
for (const routeId of routeIds) {
|
|
2080
|
+
await ctx.db.getRepository(`desktopRoutes.${scope.relation}`, routeId).remove({
|
|
2081
|
+
tk: scope.uid,
|
|
2082
|
+
transaction
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
async function detachOwnerAndFindDestroyRoots(ctx, routeIds, scope, transaction) {
|
|
2087
|
+
var _a;
|
|
2088
|
+
const routes = await ctx.db.getRepository("desktopRoutes").find({
|
|
2089
|
+
fields: ["id", "parentId"],
|
|
2090
|
+
appends: ["multiPortals", "uiLayouts"],
|
|
2091
|
+
filter: {
|
|
2092
|
+
id: routeIds
|
|
2093
|
+
},
|
|
2094
|
+
lock: transaction.LOCK.UPDATE,
|
|
2095
|
+
transaction
|
|
2096
|
+
});
|
|
2097
|
+
const routesById = new Map(routes.map((route) => [String(route.get("id")), route]));
|
|
2098
|
+
const ownerlessRouteIds = new Set(
|
|
2099
|
+
routes.filter((route) => !desktopRouteHasOwnerOutsideScope(route, scope)).map((route) => String(route.get("id")))
|
|
2100
|
+
);
|
|
2101
|
+
await detachDesktopRouteTreeOwner(ctx, routeIds, scope, transaction);
|
|
2102
|
+
for (const route of routes) {
|
|
2103
|
+
if (ownerlessRouteIds.has(String(route.get("id")))) {
|
|
2104
|
+
continue;
|
|
2105
|
+
}
|
|
2106
|
+
let parentId = route.get("parentId");
|
|
2107
|
+
const visitedParentIds = /* @__PURE__ */ new Set();
|
|
2108
|
+
while (parentId !== null && parentId !== void 0 && ownerlessRouteIds.has(String(parentId))) {
|
|
2109
|
+
const normalizedParentId = String(parentId);
|
|
2110
|
+
if (visitedParentIds.has(normalizedParentId)) {
|
|
2111
|
+
ctx.throw(400, "Invalid desktop route tree");
|
|
2112
|
+
return [];
|
|
2113
|
+
}
|
|
2114
|
+
visitedParentIds.add(normalizedParentId);
|
|
2115
|
+
parentId = ((_a = routesById.get(normalizedParentId)) == null ? void 0 : _a.get("parentId")) ?? null;
|
|
2116
|
+
}
|
|
2117
|
+
if (route.get("parentId") !== parentId) {
|
|
2118
|
+
await ctx.db.getRepository("desktopRoutes").update({
|
|
2119
|
+
filterByTk: route.get("id"),
|
|
2120
|
+
values: {
|
|
2121
|
+
parentId
|
|
2122
|
+
},
|
|
2123
|
+
transaction
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
return routes.filter((route) => {
|
|
2128
|
+
if (!ownerlessRouteIds.has(String(route.get("id")))) {
|
|
2129
|
+
return false;
|
|
2130
|
+
}
|
|
2131
|
+
const parentId = route.get("parentId");
|
|
2132
|
+
return parentId === null || parentId === void 0 || !ownerlessRouteIds.has(String(parentId));
|
|
2133
|
+
}).map((route) => route.get("id"));
|
|
2134
|
+
}
|
|
2135
|
+
async function destroyScopedDesktopRoutes(ctx, next) {
|
|
2136
|
+
var _a, _b;
|
|
2137
|
+
if (!hasActionParam((_a = ctx.action) == null ? void 0 : _a.params, "portal") && !hasActionParam((_b = ctx.action) == null ? void 0 : _b.params, "layout")) {
|
|
2138
|
+
await next();
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
const result = await ctx.db.sequelize.transaction(async (transaction) => {
|
|
2142
|
+
const scope = await getDesktopRouteDestroyOwnerScope(ctx, transaction);
|
|
2143
|
+
if (!scope) {
|
|
2144
|
+
return true;
|
|
2145
|
+
}
|
|
2146
|
+
const allRootIds = (await findDesktopRouteDestroyRoots(ctx, transaction)).sort(
|
|
2147
|
+
(left, right) => String(left).localeCompare(String(right))
|
|
2148
|
+
);
|
|
2149
|
+
const rootIds = (await findDesktopRouteDestroyRoots(ctx, transaction, scope.filter)).sort(
|
|
2150
|
+
(left, right) => String(left).localeCompare(String(right))
|
|
2151
|
+
);
|
|
2152
|
+
if (allRootIds.map(String).join(",") !== rootIds.map(String).join(",")) {
|
|
2153
|
+
ctx.throw(400, "Desktop route does not belong to the requested portal scope");
|
|
2154
|
+
return true;
|
|
2155
|
+
}
|
|
2156
|
+
const routeTrees = [];
|
|
2157
|
+
for (const rootId of rootIds) {
|
|
2158
|
+
routeTrees.push({
|
|
2159
|
+
rootId,
|
|
2160
|
+
routeIds: await findDesktopRouteSubtreeIds(ctx, rootId, transaction)
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2163
|
+
const rootRouteTrees = routeTrees.filter(
|
|
2164
|
+
(routeTree, index) => !routeTrees.some(
|
|
2165
|
+
(candidate, candidateIndex) => candidateIndex !== index && candidate.routeIds.includes(routeTree.rootId)
|
|
2166
|
+
)
|
|
2167
|
+
);
|
|
2168
|
+
const physicalDestroyRootIds = [];
|
|
2169
|
+
for (const routeTree of rootRouteTrees) {
|
|
2170
|
+
physicalDestroyRootIds.push(
|
|
2171
|
+
...await detachOwnerAndFindDestroyRoots(ctx, routeTree.routeIds, scope, transaction)
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
if (!physicalDestroyRootIds.length) {
|
|
2175
|
+
return true;
|
|
2176
|
+
}
|
|
2177
|
+
return ctx.db.getRepository("desktopRoutes").destroy({
|
|
2178
|
+
context: ctx,
|
|
2179
|
+
filterByTk: physicalDestroyRootIds.length === 1 ? physicalDestroyRootIds[0] : physicalDestroyRootIds,
|
|
2180
|
+
transaction
|
|
2181
|
+
});
|
|
2182
|
+
});
|
|
2183
|
+
ctx.status = 200;
|
|
2184
|
+
ctx.body = result;
|
|
2185
|
+
}
|
|
1479
2186
|
async function listEnabledMultiPortals(ctx, next) {
|
|
1480
2187
|
const records = await ctx.db.getRepository("multiPortals").find({
|
|
1481
|
-
filter:
|
|
2188
|
+
filter: {
|
|
1482
2189
|
enabled: true,
|
|
1483
2190
|
"uiLayout.enabled": true
|
|
1484
|
-
}
|
|
2191
|
+
},
|
|
1485
2192
|
fields: [...MULTI_PORTAL_RUNTIME_QUERY_FIELDS],
|
|
1486
2193
|
appends: ["uiLayout"],
|
|
1487
2194
|
sort: ["uid"]
|
|
@@ -1491,40 +2198,33 @@ async function listEnabledMultiPortals(ctx, next) {
|
|
|
1491
2198
|
}
|
|
1492
2199
|
async function listAccessibleMultiPortals(ctx, next) {
|
|
1493
2200
|
const accessiblePortalUids = await listCurrentRoleAccessibleMultiPortalUids(ctx);
|
|
1494
|
-
if (Array.isArray(accessiblePortalUids) && accessiblePortalUids.length === 0) {
|
|
1495
|
-
ctx.body = [];
|
|
1496
|
-
await next();
|
|
1497
|
-
return;
|
|
1498
|
-
}
|
|
1499
|
-
const filter = {
|
|
1500
|
-
enabled: true,
|
|
1501
|
-
"uiLayout.enabled": true
|
|
1502
|
-
};
|
|
1503
|
-
if (Array.isArray(accessiblePortalUids)) {
|
|
1504
|
-
filter.uid = accessiblePortalUids;
|
|
1505
|
-
}
|
|
1506
2201
|
const records = await ctx.db.getRepository("multiPortals").find({
|
|
1507
|
-
filter:
|
|
2202
|
+
filter: {
|
|
2203
|
+
enabled: true,
|
|
2204
|
+
"uiLayout.enabled": true
|
|
2205
|
+
},
|
|
1508
2206
|
fields: [...MULTI_PORTAL_ACCESSIBLE_QUERY_FIELDS],
|
|
1509
2207
|
appends: ["uiLayout"],
|
|
1510
2208
|
sort: ["uid"]
|
|
1511
2209
|
});
|
|
1512
|
-
|
|
2210
|
+
const accessiblePortalUidSet = Array.isArray(accessiblePortalUids) ? new Set(accessiblePortalUids) : void 0;
|
|
2211
|
+
const accessibleRecords = records.filter(
|
|
2212
|
+
(record) => (0, import_constants.isDefaultLayoutMultiPortalUid)(record.get("uid")) || !accessiblePortalUidSet || accessiblePortalUidSet.has(String(record.get("uid")))
|
|
2213
|
+
);
|
|
2214
|
+
ctx.body = accessibleRecords.map((record) => pickMultiPortalAccessibleFields(record));
|
|
1513
2215
|
await next();
|
|
1514
2216
|
}
|
|
1515
|
-
async function grantDefaultRouteAccessToNewMultiPortalRoutes(ctx, multiPortalUid, desktopRouteIds) {
|
|
2217
|
+
async function grantDefaultRouteAccessToNewMultiPortalRoutes(ctx, multiPortalUid, desktopRouteIds, transaction) {
|
|
1516
2218
|
if (!desktopRouteIds.length) {
|
|
1517
2219
|
return;
|
|
1518
2220
|
}
|
|
1519
|
-
if (!ctx.db.getCollection("rolesMultiPortalRoutePolicies") || !ctx.db.getCollection("rolesMultiPortalDesktopRoutes")) {
|
|
1520
|
-
return;
|
|
1521
|
-
}
|
|
1522
2221
|
const routePolicies = await ctx.db.getRepository("rolesMultiPortalRoutePolicies").find({
|
|
1523
2222
|
fields: ["roleName"],
|
|
1524
2223
|
filter: {
|
|
1525
2224
|
multiPortalUid,
|
|
1526
2225
|
allowNewMenu: true
|
|
1527
|
-
}
|
|
2226
|
+
},
|
|
2227
|
+
transaction
|
|
1528
2228
|
});
|
|
1529
2229
|
const roleNames = routePolicies.map((routePolicy) => routePolicy.get("roleName")).filter((roleName) => typeof roleName === "string" && !!roleName);
|
|
1530
2230
|
if (!roleNames.length) {
|
|
@@ -1540,26 +2240,52 @@ async function grantDefaultRouteAccessToNewMultiPortalRoutes(ctx, multiPortalUid
|
|
|
1540
2240
|
multiPortalUid,
|
|
1541
2241
|
desktopRouteId
|
|
1542
2242
|
},
|
|
1543
|
-
context: ctx
|
|
2243
|
+
context: ctx,
|
|
2244
|
+
transaction
|
|
1544
2245
|
});
|
|
1545
2246
|
}
|
|
1546
2247
|
}
|
|
1547
2248
|
}
|
|
1548
|
-
async function
|
|
1549
|
-
if (!desktopRouteIds.length
|
|
1550
|
-
return;
|
|
1551
|
-
}
|
|
1552
|
-
const portalOnlyRouteIds = await getPortalOnlyDesktopRouteIds(ctx, desktopRouteIds);
|
|
1553
|
-
if (portalOnlyRouteIds.size === 0) {
|
|
2249
|
+
async function removeLayoutRoutePermissionsFromPortalRoutes(ctx, desktopRouteIds, transaction) {
|
|
2250
|
+
if (!desktopRouteIds.length) {
|
|
1554
2251
|
return;
|
|
1555
2252
|
}
|
|
1556
2253
|
await ctx.db.getRepository("rolesDesktopRoutes").destroy({
|
|
1557
2254
|
filter: {
|
|
1558
|
-
desktopRouteId:
|
|
2255
|
+
desktopRouteId: desktopRouteIds
|
|
1559
2256
|
},
|
|
1560
|
-
context: ctx
|
|
2257
|
+
context: ctx,
|
|
2258
|
+
transaction
|
|
1561
2259
|
});
|
|
1562
2260
|
}
|
|
2261
|
+
async function reconcileCreatedDesktopRoutePermissions(desktopRoute, options) {
|
|
2262
|
+
const ctx = options == null ? void 0 : options.context;
|
|
2263
|
+
if (!ctx) {
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
const portalRequest = await findRequestedMultiPortal(ctx, options.transaction);
|
|
2267
|
+
if (!portalRequest.requested) {
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
const scope = portalRequest.scope;
|
|
2271
|
+
if (!scope) {
|
|
2272
|
+
ctx.throw(400, "Invalid portal scope");
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
if (scope.relation !== "multiPortals") {
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
const transaction = options.transaction;
|
|
2279
|
+
if (!transaction) {
|
|
2280
|
+
throw new Error("Portal desktop route creation requires a transaction");
|
|
2281
|
+
}
|
|
2282
|
+
const desktopRouteId = desktopRoute.get("id");
|
|
2283
|
+
if (typeof desktopRouteId !== "string" && typeof desktopRouteId !== "number") {
|
|
2284
|
+
throw new Error("Created Portal desktop route has no identifier");
|
|
2285
|
+
}
|
|
2286
|
+
await removeLayoutRoutePermissionsFromPortalRoutes(ctx, [desktopRouteId], transaction);
|
|
2287
|
+
await grantDefaultRouteAccessToNewMultiPortalRoutes(ctx, scope.portalUid, [desktopRouteId], transaction);
|
|
2288
|
+
}
|
|
1563
2289
|
async function grantDefaultAccessToNewMultiPortal(db, multiPortal, options) {
|
|
1564
2290
|
if (multiPortal.get("enabled") !== true) {
|
|
1565
2291
|
return;
|
|
@@ -1568,7 +2294,7 @@ async function grantDefaultAccessToNewMultiPortal(db, multiPortal, options) {
|
|
|
1568
2294
|
if (typeof multiPortalUid !== "string" || !multiPortalUid) {
|
|
1569
2295
|
return;
|
|
1570
2296
|
}
|
|
1571
|
-
if (
|
|
2297
|
+
if ((0, import_constants.isDefaultLayoutMultiPortalUid)(multiPortalUid)) {
|
|
1572
2298
|
return;
|
|
1573
2299
|
}
|
|
1574
2300
|
if (!db.getCollection("roles") || !db.getCollection("rolesMultiPortals")) {
|
|
@@ -1602,7 +2328,7 @@ async function grantDefaultAccessToNewMultiPortal(db, multiPortal, options) {
|
|
|
1602
2328
|
transaction: options == null ? void 0 : options.transaction
|
|
1603
2329
|
});
|
|
1604
2330
|
}
|
|
1605
|
-
if (!db.getCollection("rolesMultiPortalRoutePolicies")) {
|
|
2331
|
+
if (multiPortal.get("portalType") === "ai" || !db.getCollection("rolesMultiPortalRoutePolicies")) {
|
|
1606
2332
|
return;
|
|
1607
2333
|
}
|
|
1608
2334
|
const routePolicyRepository = db.getRepository("rolesMultiPortalRoutePolicies");
|
|
@@ -1913,10 +2639,10 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
1913
2639
|
}
|
|
1914
2640
|
async listAppPortalManifest(options) {
|
|
1915
2641
|
const records = await this.db.getRepository("multiPortals").find({
|
|
1916
|
-
filter:
|
|
2642
|
+
filter: {
|
|
1917
2643
|
enabled: true,
|
|
1918
2644
|
"uiLayout.enabled": true
|
|
1919
|
-
}
|
|
2645
|
+
},
|
|
1920
2646
|
fields: [...MULTI_PORTAL_ACCESSIBLE_QUERY_FIELDS],
|
|
1921
2647
|
appends: ["uiLayout"],
|
|
1922
2648
|
sort: ["uid"],
|
|
@@ -1952,7 +2678,7 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
1952
2678
|
const portalType = getRecordField(multiPortal, "portalType");
|
|
1953
2679
|
const routePath = getRecordField(multiPortal, "routePath");
|
|
1954
2680
|
const enabled = getRecordField(multiPortal, "enabled");
|
|
1955
|
-
if (typeof uid !== "string" || !uid ||
|
|
2681
|
+
if (typeof uid !== "string" || !uid || typeof title !== "string" || typeof routePath !== "string" || !routePath || enabled !== true) {
|
|
1956
2682
|
return null;
|
|
1957
2683
|
}
|
|
1958
2684
|
const uiLayoutUid = getRecordField(multiPortal, "uiLayoutUid");
|
|
@@ -2024,9 +2750,9 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
2024
2750
|
return;
|
|
2025
2751
|
}
|
|
2026
2752
|
const records = await this.db.getRepository("multiPortals").find({
|
|
2027
|
-
filter:
|
|
2753
|
+
filter: {
|
|
2028
2754
|
uiLayoutUid
|
|
2029
|
-
}
|
|
2755
|
+
},
|
|
2030
2756
|
fields: [...MULTI_PORTAL_ACCESSIBLE_QUERY_FIELDS],
|
|
2031
2757
|
transaction: options == null ? void 0 : options.transaction
|
|
2032
2758
|
});
|
|
@@ -2085,6 +2811,9 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
2085
2811
|
}
|
|
2086
2812
|
}
|
|
2087
2813
|
async beforeLoad() {
|
|
2814
|
+
this.app.db.registerRepositories({ MultiPortalDesktopRouteRepository });
|
|
2815
|
+
this.app.resourceManager.registerPreActionHandler("desktopRoutes:list", addDesktopRouteEffectiveScopeFilter);
|
|
2816
|
+
this.app.resourceManager.registerPreActionHandler("desktopRoutes:get", addDesktopRouteEffectiveScopeFilter);
|
|
2088
2817
|
this.app.resourceManager.registerPreActionHandler(
|
|
2089
2818
|
"desktopRoutes:listAccessible",
|
|
2090
2819
|
addMultiPortalListAccessibleGuard
|
|
@@ -2097,22 +2826,23 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
2097
2826
|
this.app.resourceManager.registerPreActionHandler("multiPortals:create", captureSkipCreatePortalDirectory);
|
|
2098
2827
|
this.app.resourceManager.registerPreActionHandler("multiPortals:create", normalizeMultiPortalSlugValues);
|
|
2099
2828
|
this.app.resourceManager.registerPreActionHandler("multiPortals:create", preventUiLayoutRouteNameConflict);
|
|
2100
|
-
this.app.resourceManager.registerPreActionHandler("multiPortals:update",
|
|
2829
|
+
this.app.resourceManager.registerPreActionHandler("multiPortals:update", preventMultiPortalBackingLayoutChange);
|
|
2101
2830
|
this.app.resourceManager.registerPreActionHandler("multiPortals:update", normalizeMultiPortalSlugValues);
|
|
2102
2831
|
this.app.resourceManager.registerPreActionHandler("multiPortals:update", preventUiLayoutRouteNameConflict);
|
|
2832
|
+
this.app.resourceManager.registerPreActionHandler(
|
|
2833
|
+
"multiPortals:firstOrCreate",
|
|
2834
|
+
preventMultiPortalBackingLayoutChange
|
|
2835
|
+
);
|
|
2103
2836
|
this.app.resourceManager.registerPreActionHandler("multiPortals:firstOrCreate", captureSkipCreatePortalDirectory);
|
|
2104
2837
|
this.app.resourceManager.registerPreActionHandler("multiPortals:firstOrCreate", normalizeMultiPortalSlugValues);
|
|
2105
2838
|
this.app.resourceManager.registerPreActionHandler("multiPortals:firstOrCreate", preventUiLayoutRouteNameConflict);
|
|
2106
|
-
this.app.resourceManager.registerPreActionHandler("desktopRoutes:create", captureExplicitDesktopRouteUiLayouts, {
|
|
2107
|
-
before: UI_LAYOUT_DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG
|
|
2108
|
-
});
|
|
2109
2839
|
this.app.resourceManager.registerPreActionHandler(
|
|
2110
|
-
"
|
|
2111
|
-
|
|
2112
|
-
{
|
|
2113
|
-
before: UI_LAYOUT_DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG
|
|
2114
|
-
}
|
|
2840
|
+
"multiPortals:updateOrCreate",
|
|
2841
|
+
preventMultiPortalBackingLayoutChange
|
|
2115
2842
|
);
|
|
2843
|
+
this.app.resourceManager.registerPreActionHandler("multiPortals:updateOrCreate", captureSkipCreatePortalDirectory);
|
|
2844
|
+
this.app.resourceManager.registerPreActionHandler("multiPortals:updateOrCreate", normalizeMultiPortalSlugValues);
|
|
2845
|
+
this.app.resourceManager.registerPreActionHandler("multiPortals:updateOrCreate", preventUiLayoutRouteNameConflict);
|
|
2116
2846
|
this.app.resourceManager.registerPreActionHandler("desktopRoutes:create", addDesktopRouteCreateMultiPortal, {
|
|
2117
2847
|
after: UI_LAYOUT_DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG
|
|
2118
2848
|
});
|
|
@@ -2123,6 +2853,9 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
2123
2853
|
after: UI_LAYOUT_DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG
|
|
2124
2854
|
}
|
|
2125
2855
|
);
|
|
2856
|
+
this.app.resourceManager.registerPreActionHandler("desktopRoutes:update", guardDesktopRouteUpdateScope);
|
|
2857
|
+
this.app.resourceManager.registerPreActionHandler("desktopRoutes:move", guardDesktopRouteMoveScope);
|
|
2858
|
+
this.app.resourceManager.registerPreActionHandler("desktopRoutes:destroy", destroyScopedDesktopRoutes);
|
|
2126
2859
|
}
|
|
2127
2860
|
async load() {
|
|
2128
2861
|
this.app.acl.registerSnippet({
|
|
@@ -2163,6 +2896,7 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
2163
2896
|
this.app.db.on("roles.beforeCreate", (role) => {
|
|
2164
2897
|
(0, import_ensureDefaultRoleMultiPortalAccess.applyDefaultRoleMultiPortalAccess)(role);
|
|
2165
2898
|
});
|
|
2899
|
+
this.app.db.on("desktopRoutes.afterCreate", reconcileCreatedDesktopRoutePermissions);
|
|
2166
2900
|
this.app.db.on("multiPortals.afterCreate", async (multiPortal, options) => {
|
|
2167
2901
|
await grantDefaultAccessToNewMultiPortal(this.app.db, multiPortal, options);
|
|
2168
2902
|
await this.publishAppManifestItem(multiPortal, options);
|
|
@@ -2187,14 +2921,18 @@ class PluginMultiPortalServer extends import_server.Plugin {
|
|
|
2187
2921
|
});
|
|
2188
2922
|
}
|
|
2189
2923
|
async install() {
|
|
2190
|
-
await
|
|
2191
|
-
|
|
2924
|
+
const version = await this.app.version.get();
|
|
2925
|
+
if (!version) {
|
|
2926
|
+
await (0, import_ensureDefaultRoleMultiPortalAccess.ensureDefaultRoleMultiPortalAccess)(this.db);
|
|
2927
|
+
await seedFreshMultiPortals(this.db);
|
|
2928
|
+
} else {
|
|
2929
|
+
await seedHistoricalMultiPortals(this.db);
|
|
2930
|
+
}
|
|
2192
2931
|
await this.reconcilePortalStorage();
|
|
2193
2932
|
await this.reconcileAppManifest();
|
|
2194
2933
|
}
|
|
2195
2934
|
async afterEnable() {
|
|
2196
|
-
await
|
|
2197
|
-
await (0, import_ensureDefaultRoleMultiPortalAccess.ensureDefaultRoleMultiPortalAccess)(this.db);
|
|
2935
|
+
await repairFixedLayoutMultiPortalRecords(this.db);
|
|
2198
2936
|
await this.reconcilePortalStorage();
|
|
2199
2937
|
await this.reconcileAppManifest();
|
|
2200
2938
|
}
|