@nocobase/plugin-ui-layout 2.2.0-beta.6

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.
Files changed (55) hide show
  1. package/LICENSE.txt +107 -0
  2. package/README.md +17 -0
  3. package/client-v2.d.ts +2 -0
  4. package/client-v2.js +1 -0
  5. package/client.d.ts +2 -0
  6. package/client.js +1 -0
  7. package/dist/client/index.d.ts +9 -0
  8. package/dist/client/index.js +10 -0
  9. package/dist/client/plugin.d.ts +12 -0
  10. package/dist/client-v2/647.3a2f92424b5ce814.js +10 -0
  11. package/dist/client-v2/983.f95caf8d9687987d.js +10 -0
  12. package/dist/client-v2/index.d.ts +11 -0
  13. package/dist/client-v2/index.js +10 -0
  14. package/dist/client-v2/layoutRegistration.d.ts +28 -0
  15. package/dist/client-v2/locale.d.ts +9 -0
  16. package/dist/client-v2/mobileOpenViewAction.d.ts +206 -0
  17. package/dist/client-v2/mobilePageModelResolution.d.ts +9 -0
  18. package/dist/client-v2/mobileRouteRepository.d.ts +44 -0
  19. package/dist/client-v2/models/MobileLayoutModel.d.ts +102 -0
  20. package/dist/client-v2/models/MobileMenuComponents.d.ts +10 -0
  21. package/dist/client-v2/models/MobileMenuModels.d.ts +97 -0
  22. package/dist/client-v2/models/MobileMenuUtils.d.ts +30 -0
  23. package/dist/client-v2/models/MobilePageModels.d.ts +26 -0
  24. package/dist/client-v2/models/mobileComponents.d.ts +18 -0
  25. package/dist/client-v2/models/mobileFlowCompat.d.ts +61 -0
  26. package/dist/client-v2/models/mobileThemeToken.d.ts +25 -0
  27. package/dist/client-v2/pages/RoutesPage.d.ts +22 -0
  28. package/dist/client-v2/pages/UiLayoutsPage.d.ts +80 -0
  29. package/dist/client-v2/permissions/LayoutAwareDesktopRoutesPermissionsTab.d.ts +22 -0
  30. package/dist/client-v2/permissions/layoutAwareDesktopRoutesPermissions.d.ts +22 -0
  31. package/dist/client-v2/plugin.d.ts +14 -0
  32. package/dist/constants.d.ts +29 -0
  33. package/dist/constants.js +64 -0
  34. package/dist/externalVersion.js +33 -0
  35. package/dist/index.d.ts +10 -0
  36. package/dist/index.js +48 -0
  37. package/dist/locale/en-US.json +163 -0
  38. package/dist/locale/zh-CN.json +163 -0
  39. package/dist/server/collections/desktopRoutes.d.ts +14 -0
  40. package/dist/server/collections/desktopRoutes.js +48 -0
  41. package/dist/server/collections/uiLayouts.d.ts +10 -0
  42. package/dist/server/collections/uiLayouts.js +85 -0
  43. package/dist/server/ensureDefaultUiLayout.d.ts +10 -0
  44. package/dist/server/ensureDefaultUiLayout.js +129 -0
  45. package/dist/server/index.d.ts +9 -0
  46. package/dist/server/index.js +42 -0
  47. package/dist/server/migrations/20260601090000-ensure-default-admin-layout.d.ts +13 -0
  48. package/dist/server/migrations/20260601090000-ensure-default-admin-layout.js +39 -0
  49. package/dist/server/migrations/20260615090000-backfill-admin-layout-desktop-routes.d.ts +15 -0
  50. package/dist/server/migrations/20260615090000-backfill-admin-layout-desktop-routes.js +94 -0
  51. package/dist/server/plugin.d.ts +19 -0
  52. package/dist/server/plugin.js +662 -0
  53. package/package.json +37 -0
  54. package/server.d.ts +2 -0
  55. package/server.js +1 -0
@@ -0,0 +1,662 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var plugin_exports = {};
28
+ __export(plugin_exports, {
29
+ PluginUiLayoutServer: () => PluginUiLayoutServer,
30
+ default: () => plugin_default
31
+ });
32
+ module.exports = __toCommonJS(plugin_exports);
33
+ var import_server = require("@nocobase/server");
34
+ var import_constants = require("../constants");
35
+ var import_ensureDefaultUiLayout = require("./ensureDefaultUiLayout");
36
+ const EMPTY_DESKTOP_ROUTE_FILTER = {
37
+ id: {
38
+ $eq: null
39
+ }
40
+ };
41
+ const UI_LAYOUT_RUNTIME_FIELDS = [
42
+ "uid",
43
+ "title",
44
+ "layoutType",
45
+ "routeName",
46
+ "routePath",
47
+ "authCheck",
48
+ "enabled"
49
+ ];
50
+ const UI_LAYOUT_ROLE_PERMISSION_TARGET_FIELDS = ["uid", "title", "layoutType", "routeName", "enabled"];
51
+ const DESKTOP_ROUTE_ROLE_PERMISSION_TARGET_FIELDS = ["id", "title", "hidden", "parentId", "options"];
52
+ const ROUTES_MANAGEMENT_ACTIONS = ["desktopRoutes:list"];
53
+ const ROLE_LAYOUT_PERMISSION_TARGET_ACTIONS = [
54
+ "uiLayouts:listRolePermissionTargets",
55
+ "desktopRoutes:listRolePermissionTargets"
56
+ ];
57
+ const DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG = "plugin-ui-layout:desktop-route-write-layout";
58
+ const DEFAULT_ADMIN_UI_LAYOUT_PROTECTED_FIELDS = [
59
+ "uid",
60
+ "layoutType",
61
+ "routeName",
62
+ "routePath",
63
+ "authCheck",
64
+ "enabled"
65
+ ];
66
+ function getRequestedLayoutUid(layout) {
67
+ return getExplicitRequestedLayoutUid(layout) ?? import_constants.DEFAULT_ADMIN_UI_LAYOUT.uid;
68
+ }
69
+ function getExplicitRequestedLayoutUid(layout) {
70
+ const uid = Array.isArray(layout) ? layout[0] : layout;
71
+ if (typeof uid === "string" && uid.trim()) {
72
+ return uid;
73
+ }
74
+ }
75
+ function hasActionParam(params, key) {
76
+ return !!params && typeof params === "object" && Object.prototype.hasOwnProperty.call(params, key);
77
+ }
78
+ function getDesktopRouteLayoutFilterByUid(layoutUid) {
79
+ return {
80
+ "uiLayouts.uid": layoutUid
81
+ };
82
+ }
83
+ function isDesktopRouteCreateValue(value) {
84
+ return !!value && typeof value === "object" && !Array.isArray(value);
85
+ }
86
+ function isUiLayoutActionParams(value) {
87
+ return isDesktopRouteCreateValue(value);
88
+ }
89
+ function isDefaultAdminUiLayoutRecord(record) {
90
+ return record.get("uid") === import_constants.DEFAULT_ADMIN_UI_LAYOUT.uid;
91
+ }
92
+ function hasUnsafeDefaultAdminUiLayoutValues(values) {
93
+ if (!isDesktopRouteCreateValue(values)) {
94
+ return false;
95
+ }
96
+ return DEFAULT_ADMIN_UI_LAYOUT_PROTECTED_FIELDS.some(
97
+ (field) => Object.prototype.hasOwnProperty.call(values, field) && values[field] !== import_constants.DEFAULT_ADMIN_UI_LAYOUT[field]
98
+ );
99
+ }
100
+ async function findUiLayoutActionTargets(ctx) {
101
+ var _a;
102
+ const params = (_a = ctx.action) == null ? void 0 : _a.params;
103
+ if (!isUiLayoutActionParams(params)) {
104
+ return [];
105
+ }
106
+ const repository = ctx.db.getRepository("uiLayouts");
107
+ const filterByTk = params.filterByTk ?? params.filterByTks;
108
+ if (filterByTk !== void 0 && filterByTk !== null) {
109
+ return repository.find({
110
+ filterByTk
111
+ });
112
+ }
113
+ if (params.filter && Object.keys(params.filter).length) {
114
+ return repository.find({
115
+ filter: params.filter
116
+ });
117
+ }
118
+ return [];
119
+ }
120
+ function hasUiLayout(uiLayout, layoutUid) {
121
+ if (uiLayout === layoutUid) {
122
+ return true;
123
+ }
124
+ return isDesktopRouteCreateValue(uiLayout) && uiLayout.uid === layoutUid;
125
+ }
126
+ function mergeUiLayoutValue(uiLayouts, layoutUid) {
127
+ if (Array.isArray(uiLayouts)) {
128
+ if (uiLayouts.some((uiLayout) => hasUiLayout(uiLayout, layoutUid))) {
129
+ return uiLayouts;
130
+ }
131
+ return [...uiLayouts, layoutUid];
132
+ }
133
+ if (hasUiLayout(uiLayouts, layoutUid)) {
134
+ return uiLayouts;
135
+ }
136
+ return uiLayouts == null ? [layoutUid] : [uiLayouts, layoutUid];
137
+ }
138
+ function withDesktopRouteUiLayout(value, layoutUid) {
139
+ if (Array.isArray(value)) {
140
+ return value.map((item) => withDesktopRouteUiLayout(item, layoutUid));
141
+ }
142
+ if (!isDesktopRouteCreateValue(value)) {
143
+ return value;
144
+ }
145
+ return {
146
+ ...value,
147
+ uiLayouts: mergeUiLayoutValue(value.uiLayouts, layoutUid),
148
+ ...Array.isArray(value.children) ? { children: withDesktopRouteUiLayout(value.children, layoutUid) } : {}
149
+ };
150
+ }
151
+ async function getDesktopRouteLayoutContext(ctx) {
152
+ var _a;
153
+ const requestedLayout = (_a = ctx.action) == null ? void 0 : _a.params.layout;
154
+ const layoutUid = requestedLayout === void 0 ? import_constants.DEFAULT_ADMIN_UI_LAYOUT.uid : getExplicitRequestedLayoutUid(requestedLayout);
155
+ if (!layoutUid) {
156
+ return {
157
+ filter: EMPTY_DESKTOP_ROUTE_FILTER,
158
+ valid: false
159
+ };
160
+ }
161
+ const uiLayout = await ctx.db.getRepository("uiLayouts").findOne({
162
+ filter: {
163
+ uid: layoutUid,
164
+ enabled: true
165
+ }
166
+ });
167
+ if (!uiLayout) {
168
+ return {
169
+ filter: EMPTY_DESKTOP_ROUTE_FILTER,
170
+ layoutUid,
171
+ valid: false
172
+ };
173
+ }
174
+ return {
175
+ filter: getDesktopRouteLayoutFilterByUid(layoutUid),
176
+ layoutUid,
177
+ valid: true
178
+ };
179
+ }
180
+ function getRouteValue(route, key) {
181
+ if (!route || typeof route !== "object") {
182
+ return;
183
+ }
184
+ const maybeModel = route;
185
+ if (typeof maybeModel.get === "function") {
186
+ return maybeModel.get(key);
187
+ }
188
+ return route[key];
189
+ }
190
+ function getRouteId(route) {
191
+ const id = getRouteValue(route, "id");
192
+ return id === null || id === void 0 ? void 0 : String(id);
193
+ }
194
+ function getRouteParentId(route) {
195
+ const parentId = getRouteValue(route, "parentId");
196
+ return parentId === null || parentId === void 0 ? void 0 : String(parentId);
197
+ }
198
+ function setRouteChildren(route, children) {
199
+ if (!route || typeof route !== "object") {
200
+ return;
201
+ }
202
+ const maybeModel = route;
203
+ if (typeof maybeModel.setDataValue === "function") {
204
+ maybeModel.setDataValue("children", children);
205
+ } else {
206
+ maybeModel.children = children;
207
+ }
208
+ if (!maybeModel._options) {
209
+ return;
210
+ }
211
+ if (!maybeModel._options.includeNames) {
212
+ maybeModel._options.includeNames = ["children"];
213
+ return;
214
+ }
215
+ if (!maybeModel._options.includeNames.includes("children")) {
216
+ maybeModel._options.includeNames.push("children");
217
+ }
218
+ }
219
+ function collectRouteIds(routes, ids) {
220
+ for (const route of routes) {
221
+ const id = getRouteId(route);
222
+ if (id) {
223
+ ids.add(id);
224
+ }
225
+ const children = getRouteValue(route, "children");
226
+ if (Array.isArray(children)) {
227
+ collectRouteIds(children, ids);
228
+ }
229
+ }
230
+ }
231
+ function removeNestedRootRoutes(routes) {
232
+ if (!Array.isArray(routes)) {
233
+ return [];
234
+ }
235
+ const routeIds = /* @__PURE__ */ new Set();
236
+ collectRouteIds(routes, routeIds);
237
+ return routes.filter((route) => {
238
+ const parentId = getRouteParentId(route);
239
+ return !parentId || !routeIds.has(parentId);
240
+ });
241
+ }
242
+ async function removeRouteIdsWithUnauthorizedAncestors(ctx, routeIds) {
243
+ if (routeIds.size === 0) {
244
+ return;
245
+ }
246
+ const parentIdByRouteId = /* @__PURE__ */ new Map();
247
+ let pendingRouteIds = new Set(routeIds);
248
+ while (pendingRouteIds.size > 0) {
249
+ const routes = await ctx.db.getRepository("desktopRoutes").find({
250
+ fields: ["id", "parentId"],
251
+ filter: {
252
+ id: Array.from(pendingRouteIds)
253
+ }
254
+ });
255
+ pendingRouteIds = /* @__PURE__ */ new Set();
256
+ for (const route of routes) {
257
+ const routeId = route.get("id");
258
+ if (routeId === null || routeId === void 0) {
259
+ continue;
260
+ }
261
+ const normalizedRouteId = String(routeId);
262
+ const parentId = route.get("parentId");
263
+ const normalizedParentId = parentId === null || parentId === void 0 ? void 0 : String(parentId);
264
+ parentIdByRouteId.set(normalizedRouteId, normalizedParentId);
265
+ if (normalizedParentId && !parentIdByRouteId.has(normalizedParentId)) {
266
+ pendingRouteIds.add(normalizedParentId);
267
+ }
268
+ }
269
+ }
270
+ for (const routeId of Array.from(routeIds)) {
271
+ const visitedRouteIds = /* @__PURE__ */ new Set([routeId]);
272
+ let parentId = parentIdByRouteId.get(routeId);
273
+ while (parentId) {
274
+ if (!routeIds.has(parentId) || visitedRouteIds.has(parentId)) {
275
+ routeIds.delete(routeId);
276
+ break;
277
+ }
278
+ visitedRouteIds.add(parentId);
279
+ parentId = parentIdByRouteId.get(parentId);
280
+ }
281
+ }
282
+ }
283
+ async function addDesktopRouteWriteLayout(ctx, next) {
284
+ var _a, _b, _c;
285
+ const params = (_a = ctx.action) == null ? void 0 : _a.params;
286
+ const hasExplicitLayoutScope = hasActionParam(params, "layout");
287
+ const explicitLayoutUid = hasExplicitLayoutScope ? getExplicitRequestedLayoutUid(params.layout) : void 0;
288
+ if (hasExplicitLayoutScope && !explicitLayoutUid) {
289
+ ctx.throw(400, ctx.t("Invalid layout scope", { ns: import_constants.NAMESPACE }));
290
+ return;
291
+ }
292
+ const layoutUid = explicitLayoutUid ?? import_constants.DEFAULT_ADMIN_UI_LAYOUT.uid;
293
+ const uiLayout = await ctx.db.getRepository("uiLayouts").findOne({
294
+ filter: {
295
+ uid: layoutUid,
296
+ enabled: true
297
+ }
298
+ });
299
+ if (!uiLayout && hasExplicitLayoutScope) {
300
+ ctx.throw(400, ctx.t("Invalid layout scope", { ns: import_constants.NAMESPACE }));
301
+ return;
302
+ }
303
+ if (uiLayout) {
304
+ (_c = ctx.action) == null ? void 0 : _c.mergeParams({
305
+ values: withDesktopRouteUiLayout((_b = ctx.action) == null ? void 0 : _b.params.values, layoutUid)
306
+ });
307
+ }
308
+ await next();
309
+ }
310
+ async function preventUiLayoutUidChange(ctx, next) {
311
+ var _a, _b;
312
+ const values = (_a = ctx.action) == null ? void 0 : _a.params.values;
313
+ if (!isDesktopRouteCreateValue(values) || !Object.prototype.hasOwnProperty.call(values, "uid")) {
314
+ await next();
315
+ return;
316
+ }
317
+ const requestedUid = values.uid;
318
+ const filterByTk = (_b = ctx.action) == null ? void 0 : _b.params.filterByTk;
319
+ if (typeof requestedUid !== "string" || filterByTk === null || filterByTk === void 0 || Array.isArray(filterByTk)) {
320
+ ctx.throw(400, ctx.t("UID cannot be changed", { ns: import_constants.NAMESPACE }));
321
+ return;
322
+ }
323
+ const record = await ctx.db.getRepository("uiLayouts").findOne({
324
+ filterByTk
325
+ });
326
+ if (record && record.get("uid") !== requestedUid) {
327
+ ctx.throw(400, ctx.t("UID cannot be changed", { ns: import_constants.NAMESPACE }));
328
+ return;
329
+ }
330
+ await next();
331
+ }
332
+ async function preventDefaultAdminUiLayoutUpdate(ctx, next) {
333
+ var _a;
334
+ if (!hasUnsafeDefaultAdminUiLayoutValues((_a = ctx.action) == null ? void 0 : _a.params.values)) {
335
+ await next();
336
+ return;
337
+ }
338
+ const targets = await findUiLayoutActionTargets(ctx);
339
+ if (targets.some(isDefaultAdminUiLayoutRecord)) {
340
+ ctx.throw(400, ctx.t("Default AdminLayout cannot be changed", { ns: import_constants.NAMESPACE }));
341
+ return;
342
+ }
343
+ await next();
344
+ }
345
+ async function preventDefaultAdminUiLayoutDestroy(ctx, next) {
346
+ const targets = await findUiLayoutActionTargets(ctx);
347
+ if (targets.some(isDefaultAdminUiLayoutRecord)) {
348
+ ctx.throw(400, ctx.t("Default AdminLayout cannot be deleted", { ns: import_constants.NAMESPACE }));
349
+ return;
350
+ }
351
+ await next();
352
+ }
353
+ function getCurrentRoles(ctx) {
354
+ const currentRoles = ctx.state.currentRoles;
355
+ if (!Array.isArray(currentRoles)) {
356
+ return [];
357
+ }
358
+ return currentRoles.filter((role) => typeof role === "string");
359
+ }
360
+ async function getLayoutAccessibleRouteIds(ctx, layoutUid) {
361
+ const currentRoles = getCurrentRoles(ctx);
362
+ if (currentRoles.includes("root")) {
363
+ return;
364
+ }
365
+ const routePermissions = await ctx.db.getRepository("rolesDesktopRoutes").find({
366
+ fields: ["desktopRouteId"],
367
+ filter: {
368
+ roleName: currentRoles
369
+ }
370
+ });
371
+ const permittedRouteIds = /* @__PURE__ */ new Set();
372
+ for (const permission of routePermissions) {
373
+ const routeId = permission.get("desktopRouteId");
374
+ if (routeId !== null && routeId !== void 0) {
375
+ permittedRouteIds.add(String(routeId));
376
+ }
377
+ }
378
+ if (permittedRouteIds.size === 0) {
379
+ return permittedRouteIds;
380
+ }
381
+ const layoutRoutes = await ctx.db.getRepository("desktopRoutes").find({
382
+ fields: ["id"],
383
+ filter: {
384
+ ...getDesktopRouteLayoutFilterByUid(layoutUid),
385
+ id: Array.from(permittedRouteIds)
386
+ }
387
+ });
388
+ const routeIds = /* @__PURE__ */ new Set();
389
+ for (const route of layoutRoutes) {
390
+ const routeId = route.get("id");
391
+ if (routeId !== null && routeId !== void 0) {
392
+ routeIds.add(String(routeId));
393
+ }
394
+ }
395
+ await removeRouteIdsWithUnauthorizedAncestors(ctx, routeIds);
396
+ return routeIds;
397
+ }
398
+ function removeInaccessibleRoute(route, allowedRouteIds) {
399
+ if (!route || !allowedRouteIds) {
400
+ return route;
401
+ }
402
+ const id = getRouteId(route);
403
+ if (!id || !allowedRouteIds.has(id)) {
404
+ return null;
405
+ }
406
+ return route;
407
+ }
408
+ function buildAccessibleRouteTreeWithAncestors(routes, accessibleRouteIds) {
409
+ const routeById = /* @__PURE__ */ new Map();
410
+ const childrenByParentId = /* @__PURE__ */ new Map();
411
+ const roots = [];
412
+ for (const route of routes) {
413
+ const id = getRouteId(route);
414
+ if (!id) {
415
+ continue;
416
+ }
417
+ setRouteChildren(route, void 0);
418
+ routeById.set(id, route);
419
+ }
420
+ for (const route of routes) {
421
+ const id = getRouteId(route);
422
+ if (!id || !routeById.has(id)) {
423
+ continue;
424
+ }
425
+ const parentId = getRouteParentId(route);
426
+ if (!parentId || !routeById.has(parentId)) {
427
+ roots.push(route);
428
+ continue;
429
+ }
430
+ const children = childrenByParentId.get(parentId) ?? [];
431
+ children.push(route);
432
+ childrenByParentId.set(parentId, children);
433
+ }
434
+ const visitRoute = (route, visitingRouteIds) => {
435
+ const id = getRouteId(route);
436
+ if (!id || visitingRouteIds.has(id)) {
437
+ return void 0;
438
+ }
439
+ visitingRouteIds.add(id);
440
+ const children = childrenByParentId.get(id) ?? [];
441
+ const visibleChildren = children.map((child) => visitRoute(child, visitingRouteIds)).filter((child) => child !== void 0);
442
+ visitingRouteIds.delete(id);
443
+ if (!accessibleRouteIds.has(id) && visibleChildren.length === 0) {
444
+ return void 0;
445
+ }
446
+ setRouteChildren(route, visibleChildren.length > 0 ? visibleChildren : void 0);
447
+ return route;
448
+ };
449
+ return roots.map((route) => visitRoute(route, /* @__PURE__ */ new Set())).filter((route) => route !== void 0);
450
+ }
451
+ async function includeRouteAncestorsForListAccessible(ctx, routes, layoutFilter) {
452
+ if (!Array.isArray(routes) || getCurrentRoles(ctx).includes("root")) {
453
+ return routes;
454
+ }
455
+ const accessibleRouteIds = /* @__PURE__ */ new Set();
456
+ collectRouteIds(routes, accessibleRouteIds);
457
+ if (accessibleRouteIds.size === 0) {
458
+ return routes;
459
+ }
460
+ const layoutRoutes = await ctx.db.getRepository("desktopRoutes").find({
461
+ sort: "sort",
462
+ filter: layoutFilter
463
+ });
464
+ return buildAccessibleRouteTreeWithAncestors(layoutRoutes, accessibleRouteIds);
465
+ }
466
+ async function addDesktopRouteLayoutFilter(ctx, next) {
467
+ var _a, _b;
468
+ const layoutContext = await getDesktopRouteLayoutContext(ctx);
469
+ (_a = ctx.action) == null ? void 0 : _a.mergeParams({
470
+ filter: layoutContext.filter
471
+ });
472
+ await next();
473
+ if (!layoutContext.valid || !layoutContext.layoutUid) {
474
+ ctx.body = [];
475
+ return;
476
+ }
477
+ if (getCurrentRoles(ctx).includes("root")) {
478
+ ctx.body = removeNestedRootRoutes(
479
+ await includeRouteAncestorsForListAccessible(ctx, ctx.body, layoutContext.filter)
480
+ );
481
+ return;
482
+ }
483
+ const routeIds = await getLayoutAccessibleRouteIds(ctx, layoutContext.layoutUid);
484
+ if (!routeIds || routeIds.size === 0) {
485
+ ctx.body = [];
486
+ return;
487
+ }
488
+ const routes = await ctx.db.getRepository("desktopRoutes").find({
489
+ tree: true,
490
+ sort: "sort",
491
+ filter: {
492
+ ...(_b = ctx.action) == null ? void 0 : _b.params.filter,
493
+ id: Array.from(routeIds)
494
+ }
495
+ });
496
+ ctx.body = removeNestedRootRoutes(await includeRouteAncestorsForListAccessible(ctx, routes, layoutContext.filter));
497
+ }
498
+ async function addDesktopRouteGetLayoutFilter(ctx, next) {
499
+ var _a, _b, _c;
500
+ const layoutContext = await getDesktopRouteLayoutContext(ctx);
501
+ (_a = ctx.action) == null ? void 0 : _a.mergeParams({
502
+ filter: layoutContext.filter
503
+ });
504
+ await next();
505
+ if (!layoutContext.valid || !layoutContext.layoutUid) {
506
+ ctx.status = 204;
507
+ ctx.body = void 0;
508
+ return;
509
+ }
510
+ if (getCurrentRoles(ctx).includes("root")) {
511
+ if (ctx.body) {
512
+ return;
513
+ }
514
+ ctx.status = 204;
515
+ ctx.body = void 0;
516
+ return;
517
+ }
518
+ const routeIds = await getLayoutAccessibleRouteIds(ctx, layoutContext.layoutUid);
519
+ let route = removeInaccessibleRoute(ctx.body, routeIds);
520
+ if (!route && routeIds && routeIds.size > 0) {
521
+ route = await ctx.db.getRepository("desktopRoutes").findOne({
522
+ sort: "sort",
523
+ ...(_b = ctx.action) == null ? void 0 : _b.params,
524
+ filter: {
525
+ ...(_c = ctx.action) == null ? void 0 : _c.params.filter,
526
+ id: Array.from(routeIds)
527
+ }
528
+ });
529
+ }
530
+ if (route == null) {
531
+ ctx.status = 204;
532
+ ctx.body = void 0;
533
+ return;
534
+ }
535
+ ctx.body = route;
536
+ }
537
+ function pickUiLayoutRuntimeFields(record) {
538
+ const result = {};
539
+ for (const field of UI_LAYOUT_RUNTIME_FIELDS) {
540
+ result[field] = record.get(field);
541
+ }
542
+ return result;
543
+ }
544
+ function pickUiLayoutRolePermissionTargetFields(record) {
545
+ const result = {};
546
+ for (const field of UI_LAYOUT_ROLE_PERMISSION_TARGET_FIELDS) {
547
+ result[field] = record.get(field);
548
+ }
549
+ return result;
550
+ }
551
+ function pickDesktopRouteRolePermissionTargetFields(route) {
552
+ const result = {};
553
+ for (const field of DESKTOP_ROUTE_ROLE_PERMISSION_TARGET_FIELDS) {
554
+ result[field] = getRouteValue(route, field) ?? null;
555
+ }
556
+ const children = getRouteValue(route, "children");
557
+ result.children = Array.isArray(children) ? children.map((child) => pickDesktopRouteRolePermissionTargetFields(child)) : [];
558
+ return result;
559
+ }
560
+ async function listAccessibleUiLayouts(ctx, next) {
561
+ const records = await ctx.db.getRepository("uiLayouts").find({
562
+ filter: {
563
+ enabled: true
564
+ },
565
+ fields: [...UI_LAYOUT_RUNTIME_FIELDS],
566
+ sort: ["uid"]
567
+ });
568
+ ctx.body = records.map((record) => pickUiLayoutRuntimeFields(record));
569
+ await next();
570
+ }
571
+ async function listEnabledUiLayouts(ctx, next) {
572
+ const records = await ctx.db.getRepository("uiLayouts").find({
573
+ filter: {
574
+ enabled: true
575
+ },
576
+ fields: [...UI_LAYOUT_RUNTIME_FIELDS],
577
+ sort: ["uid"]
578
+ });
579
+ ctx.body = records.map((record) => pickUiLayoutRuntimeFields(record));
580
+ await next();
581
+ }
582
+ async function listUiLayoutRolePermissionTargets(ctx, next) {
583
+ const records = await ctx.db.getRepository("uiLayouts").find({
584
+ filter: {
585
+ enabled: true
586
+ },
587
+ fields: [...UI_LAYOUT_ROLE_PERMISSION_TARGET_FIELDS],
588
+ sort: ["uid"]
589
+ });
590
+ ctx.body = records.map((record) => pickUiLayoutRolePermissionTargetFields(record));
591
+ await next();
592
+ }
593
+ async function listDesktopRouteRolePermissionTargets(ctx, next) {
594
+ const layoutContext = await getDesktopRouteLayoutContext(ctx);
595
+ if (!layoutContext.valid) {
596
+ ctx.body = [];
597
+ await next();
598
+ return;
599
+ }
600
+ const routes = await ctx.db.getRepository("desktopRoutes").find({
601
+ tree: true,
602
+ sort: "sort",
603
+ filter: layoutContext.filter,
604
+ fields: [...DESKTOP_ROUTE_ROLE_PERMISSION_TARGET_FIELDS]
605
+ });
606
+ ctx.body = removeNestedRootRoutes(routes).map((route) => pickDesktopRouteRolePermissionTargetFields(route));
607
+ await next();
608
+ }
609
+ class PluginUiLayoutServer extends import_server.Plugin {
610
+ async afterAdd() {
611
+ }
612
+ async beforeLoad() {
613
+ }
614
+ async load() {
615
+ this.app.acl.registerSnippet({
616
+ name: "pm.routes",
617
+ actions: ROUTES_MANAGEMENT_ACTIONS
618
+ });
619
+ this.app.acl.registerSnippet({
620
+ name: "pm.acl.roles",
621
+ actions: ROLE_LAYOUT_PERMISSION_TARGET_ACTIONS
622
+ });
623
+ this.app.acl.allow("uiLayouts", "listEnabled", "public");
624
+ this.app.acl.allow("uiLayouts", "listAccessible", "loggedIn");
625
+ this.app.resourceManager.registerActionHandler("uiLayouts:listEnabled", listEnabledUiLayouts);
626
+ this.app.resourceManager.registerActionHandler("uiLayouts:listAccessible", listAccessibleUiLayouts);
627
+ this.app.resourceManager.registerActionHandler(
628
+ "uiLayouts:listRolePermissionTargets",
629
+ listUiLayoutRolePermissionTargets
630
+ );
631
+ this.app.resourceManager.registerActionHandler(
632
+ "desktopRoutes:listRolePermissionTargets",
633
+ listDesktopRouteRolePermissionTargets
634
+ );
635
+ this.app.resourceManager.registerPreActionHandler("uiLayouts:update", preventUiLayoutUidChange);
636
+ this.app.resourceManager.registerPreActionHandler("uiLayouts:update", preventDefaultAdminUiLayoutUpdate);
637
+ this.app.resourceManager.registerPreActionHandler("uiLayouts:destroy", preventDefaultAdminUiLayoutDestroy);
638
+ this.app.resourceManager.registerPreActionHandler("desktopRoutes:create", addDesktopRouteWriteLayout, {
639
+ tag: DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG
640
+ });
641
+ this.app.resourceManager.registerPreActionHandler("desktopRoutes:updateOrCreate", addDesktopRouteWriteLayout, {
642
+ tag: DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG
643
+ });
644
+ this.app.resourceManager.registerPreActionHandler("desktopRoutes:listAccessible", addDesktopRouteLayoutFilter);
645
+ this.app.resourceManager.registerPreActionHandler("desktopRoutes:getAccessible", addDesktopRouteGetLayoutFilter);
646
+ }
647
+ async install() {
648
+ await (0, import_ensureDefaultUiLayout.ensureDefaultUiLayout)(this.db);
649
+ }
650
+ async afterEnable() {
651
+ await (0, import_ensureDefaultUiLayout.ensureDefaultUiLayout)(this.db);
652
+ }
653
+ async afterDisable() {
654
+ }
655
+ async remove() {
656
+ }
657
+ }
658
+ var plugin_default = PluginUiLayoutServer;
659
+ // Annotate the CommonJS export names for ESM import in node:
660
+ 0 && (module.exports = {
661
+ PluginUiLayoutServer
662
+ });
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@nocobase/plugin-ui-layout",
3
+ "version": "2.2.0-beta.6",
4
+ "license": "Apache-2.0",
5
+ "main": "dist/server/index.js",
6
+ "displayName": "UI layout",
7
+ "displayName.zh-CN": "UI 布局",
8
+ "description": "Provides desktop layout, mobile layout, and route management pages.",
9
+ "description.zh-CN": "提供桌面端布局、移动端布局、路由管理页面。",
10
+ "devDependencies": {
11
+ "@ant-design/icons": "5.x",
12
+ "@dnd-kit/core": "^6.0.0",
13
+ "@emotion/css": "11.x",
14
+ "@formily/antd-v5": "1.x",
15
+ "@formily/json-schema": "2.x",
16
+ "@formily/react": "2.x",
17
+ "@formily/reactive": "2.x",
18
+ "@formily/shared": "2.x",
19
+ "ahooks": "3.x",
20
+ "antd": "5.x",
21
+ "lodash": "4.x",
22
+ "react": "^18.2.0",
23
+ "react-i18next": "11.x",
24
+ "react-router-dom": "6.x",
25
+ "sequelize": "^6.26.0"
26
+ },
27
+ "peerDependencies": {
28
+ "@nocobase/client": "2.x",
29
+ "@nocobase/client-v2": "2.x",
30
+ "@nocobase/database": "2.x",
31
+ "@nocobase/flow-engine": "2.x",
32
+ "@nocobase/server": "2.x",
33
+ "@nocobase/test": "2.x",
34
+ "@nocobase/utils": "2.x"
35
+ },
36
+ "gitHead": "dc9246516f8a3efd6056f22fe7b3bc947bd4575b"
37
+ }
package/server.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './dist/server';
2
+ export { default } from './dist/server';
package/server.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require('./dist/server/index.js');