@nocobase/plugin-multi-portal 3.0.0-alpha.1 → 3.0.0-alpha.3

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.
@@ -8,19 +8,19 @@
8
8
  */
9
9
 
10
10
  module.exports = {
11
- "@nocobase/flow-engine": "3.0.0-alpha.1",
12
- "@nocobase/client": "3.0.0-alpha.1",
13
- "@nocobase/client-v2": "3.0.0-alpha.1",
11
+ "@nocobase/flow-engine": "3.0.0-alpha.3",
12
+ "@nocobase/client": "3.0.0-alpha.3",
13
+ "@nocobase/client-v2": "3.0.0-alpha.3",
14
14
  "antd": "5.24.2",
15
15
  "react": "18.2.0",
16
16
  "react-router-dom": "6.30.1",
17
- "@nocobase/database": "3.0.0-alpha.1",
18
- "@nocobase/server": "3.0.0-alpha.1",
19
- "@nocobase/resourcer": "3.0.0-alpha.1",
20
- "@nocobase/utils": "3.0.0-alpha.1",
17
+ "@nocobase/database": "3.0.0-alpha.3",
18
+ "@nocobase/server": "3.0.0-alpha.3",
19
+ "@nocobase/resourcer": "3.0.0-alpha.3",
20
+ "@nocobase/utils": "3.0.0-alpha.3",
21
21
  "ahooks": "3.7.8",
22
22
  "antd-style": "3.7.1",
23
- "@nocobase/plugin-ui-layout": "3.0.0-alpha.1",
23
+ "@nocobase/plugin-ui-layout": "3.0.0-alpha.3",
24
24
  "@ant-design/icons": "5.6.1",
25
25
  "sequelize": "6.35.2"
26
26
  };
@@ -1 +1 @@
1
- {"author":"Isaac Z. Schlueter","name":"tar","description":"tar for node","version":"7.5.15","repository":{"type":"git","url":"https://github.com/isaacs/node-tar.git"},"scripts":{"genparse":"node scripts/generate-parse-fixtures.js","snap":"tap","test":"tap","pretest":"npm run prepare","presnap":"npm run prepare","prepare":"tshy && bash scripts/build.sh","preversion":"npm test","postversion":"npm publish","prepublishOnly":"git push origin --follow-tags","format":"prettier --write . --log-level warn","typedoc":"typedoc --tsconfig .tshy/esm.json ./src/*.ts","lint":"oxlint --fix src test","postsnap":"npm run lint","postlint":"npm run format"},"dependencies":{"@isaacs/fs-minipass":"^4.0.0","chownr":"^3.0.0","minipass":"^7.1.2","minizlib":"^3.1.0","yallist":"^5.0.0"},"devDependencies":{"@types/node":"^25.5.0","chmodr":"^2.0.2","end-of-stream":"^1.4.3","esbuild":"^0.27.4","events-to-array":"^2.0.3","mutate-fs":"^2.1.1","nock":"^13.5.4","oxlint":"^1.57.0","oxlint-tsgolint":"^0.17.3","prettier":"^3.8.1","rimraf":"^6.1.2","tap":"^21.6.2","tshy":"^3.3.2","typedoc":"^0.28.18"},"license":"BlueOak-1.0.0","engines":{"node":">=18"},"files":["dist"],"tshy":{"exports":{"./package.json":"./package.json",".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.min.js"},"require":{"types":"./dist/commonjs/index.d.ts","default":"./dist/commonjs/index.min.js"}},"./raw":"./src/index.ts","./c":"./src/create.ts","./create":"./src/create.ts","./replace":"./src/create.ts","./r":"./src/create.ts","./list":"./src/list.ts","./t":"./src/list.ts","./update":"./src/update.ts","./u":"./src/update.ts","./extract":"./src/extract.ts","./x":"./src/extract.ts","./pack":"./src/pack.ts","./unpack":"./src/unpack.ts","./parse":"./src/parse.ts","./read-entry":"./src/read-entry.ts","./write-entry":"./src/write-entry.ts","./header":"./src/header.ts","./pax":"./src/pax.ts","./types":"./src/types.ts"}},"exports":{"./package.json":"./package.json",".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.min.js"},"require":{"types":"./dist/commonjs/index.d.ts","default":"./dist/commonjs/index.min.js"}},"./raw":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/commonjs/index.d.ts","default":"./dist/commonjs/index.js"}},"./c":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./create":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./replace":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./r":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./list":{"import":{"types":"./dist/esm/list.d.ts","default":"./dist/esm/list.js"},"require":{"types":"./dist/commonjs/list.d.ts","default":"./dist/commonjs/list.js"}},"./t":{"import":{"types":"./dist/esm/list.d.ts","default":"./dist/esm/list.js"},"require":{"types":"./dist/commonjs/list.d.ts","default":"./dist/commonjs/list.js"}},"./update":{"import":{"types":"./dist/esm/update.d.ts","default":"./dist/esm/update.js"},"require":{"types":"./dist/commonjs/update.d.ts","default":"./dist/commonjs/update.js"}},"./u":{"import":{"types":"./dist/esm/update.d.ts","default":"./dist/esm/update.js"},"require":{"types":"./dist/commonjs/update.d.ts","default":"./dist/commonjs/update.js"}},"./extract":{"import":{"types":"./dist/esm/extract.d.ts","default":"./dist/esm/extract.js"},"require":{"types":"./dist/commonjs/extract.d.ts","default":"./dist/commonjs/extract.js"}},"./x":{"import":{"types":"./dist/esm/extract.d.ts","default":"./dist/esm/extract.js"},"require":{"types":"./dist/commonjs/extract.d.ts","default":"./dist/commonjs/extract.js"}},"./pack":{"import":{"types":"./dist/esm/pack.d.ts","default":"./dist/esm/pack.js"},"require":{"types":"./dist/commonjs/pack.d.ts","default":"./dist/commonjs/pack.js"}},"./unpack":{"import":{"types":"./dist/esm/unpack.d.ts","default":"./dist/esm/unpack.js"},"require":{"types":"./dist/commonjs/unpack.d.ts","default":"./dist/commonjs/unpack.js"}},"./parse":{"import":{"types":"./dist/esm/parse.d.ts","default":"./dist/esm/parse.js"},"require":{"types":"./dist/commonjs/parse.d.ts","default":"./dist/commonjs/parse.js"}},"./read-entry":{"import":{"types":"./dist/esm/read-entry.d.ts","default":"./dist/esm/read-entry.js"},"require":{"types":"./dist/commonjs/read-entry.d.ts","default":"./dist/commonjs/read-entry.js"}},"./write-entry":{"import":{"types":"./dist/esm/write-entry.d.ts","default":"./dist/esm/write-entry.js"},"require":{"types":"./dist/commonjs/write-entry.d.ts","default":"./dist/commonjs/write-entry.js"}},"./header":{"import":{"types":"./dist/esm/header.d.ts","default":"./dist/esm/header.js"},"require":{"types":"./dist/commonjs/header.d.ts","default":"./dist/commonjs/header.js"}},"./pax":{"import":{"types":"./dist/esm/pax.d.ts","default":"./dist/esm/pax.js"},"require":{"types":"./dist/commonjs/pax.d.ts","default":"./dist/commonjs/pax.js"}},"./types":{"import":{"types":"./dist/esm/types.d.ts","default":"./dist/esm/types.js"},"require":{"types":"./dist/commonjs/types.d.ts","default":"./dist/commonjs/types.js"}}},"type":"module","main":"./dist/commonjs/index.min.js","types":"./dist/commonjs/index.d.ts","module":"./dist/esm/index.min.js","_lastModified":"2026-07-30T03:55:55.733Z"}
1
+ {"author":"Isaac Z. Schlueter","name":"tar","description":"tar for node","version":"7.5.15","repository":{"type":"git","url":"https://github.com/isaacs/node-tar.git"},"scripts":{"genparse":"node scripts/generate-parse-fixtures.js","snap":"tap","test":"tap","pretest":"npm run prepare","presnap":"npm run prepare","prepare":"tshy && bash scripts/build.sh","preversion":"npm test","postversion":"npm publish","prepublishOnly":"git push origin --follow-tags","format":"prettier --write . --log-level warn","typedoc":"typedoc --tsconfig .tshy/esm.json ./src/*.ts","lint":"oxlint --fix src test","postsnap":"npm run lint","postlint":"npm run format"},"dependencies":{"@isaacs/fs-minipass":"^4.0.0","chownr":"^3.0.0","minipass":"^7.1.2","minizlib":"^3.1.0","yallist":"^5.0.0"},"devDependencies":{"@types/node":"^25.5.0","chmodr":"^2.0.2","end-of-stream":"^1.4.3","esbuild":"^0.27.4","events-to-array":"^2.0.3","mutate-fs":"^2.1.1","nock":"^13.5.4","oxlint":"^1.57.0","oxlint-tsgolint":"^0.17.3","prettier":"^3.8.1","rimraf":"^6.1.2","tap":"^21.6.2","tshy":"^3.3.2","typedoc":"^0.28.18"},"license":"BlueOak-1.0.0","engines":{"node":">=18"},"files":["dist"],"tshy":{"exports":{"./package.json":"./package.json",".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.min.js"},"require":{"types":"./dist/commonjs/index.d.ts","default":"./dist/commonjs/index.min.js"}},"./raw":"./src/index.ts","./c":"./src/create.ts","./create":"./src/create.ts","./replace":"./src/create.ts","./r":"./src/create.ts","./list":"./src/list.ts","./t":"./src/list.ts","./update":"./src/update.ts","./u":"./src/update.ts","./extract":"./src/extract.ts","./x":"./src/extract.ts","./pack":"./src/pack.ts","./unpack":"./src/unpack.ts","./parse":"./src/parse.ts","./read-entry":"./src/read-entry.ts","./write-entry":"./src/write-entry.ts","./header":"./src/header.ts","./pax":"./src/pax.ts","./types":"./src/types.ts"}},"exports":{"./package.json":"./package.json",".":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.min.js"},"require":{"types":"./dist/commonjs/index.d.ts","default":"./dist/commonjs/index.min.js"}},"./raw":{"import":{"types":"./dist/esm/index.d.ts","default":"./dist/esm/index.js"},"require":{"types":"./dist/commonjs/index.d.ts","default":"./dist/commonjs/index.js"}},"./c":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./create":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./replace":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./r":{"import":{"types":"./dist/esm/create.d.ts","default":"./dist/esm/create.js"},"require":{"types":"./dist/commonjs/create.d.ts","default":"./dist/commonjs/create.js"}},"./list":{"import":{"types":"./dist/esm/list.d.ts","default":"./dist/esm/list.js"},"require":{"types":"./dist/commonjs/list.d.ts","default":"./dist/commonjs/list.js"}},"./t":{"import":{"types":"./dist/esm/list.d.ts","default":"./dist/esm/list.js"},"require":{"types":"./dist/commonjs/list.d.ts","default":"./dist/commonjs/list.js"}},"./update":{"import":{"types":"./dist/esm/update.d.ts","default":"./dist/esm/update.js"},"require":{"types":"./dist/commonjs/update.d.ts","default":"./dist/commonjs/update.js"}},"./u":{"import":{"types":"./dist/esm/update.d.ts","default":"./dist/esm/update.js"},"require":{"types":"./dist/commonjs/update.d.ts","default":"./dist/commonjs/update.js"}},"./extract":{"import":{"types":"./dist/esm/extract.d.ts","default":"./dist/esm/extract.js"},"require":{"types":"./dist/commonjs/extract.d.ts","default":"./dist/commonjs/extract.js"}},"./x":{"import":{"types":"./dist/esm/extract.d.ts","default":"./dist/esm/extract.js"},"require":{"types":"./dist/commonjs/extract.d.ts","default":"./dist/commonjs/extract.js"}},"./pack":{"import":{"types":"./dist/esm/pack.d.ts","default":"./dist/esm/pack.js"},"require":{"types":"./dist/commonjs/pack.d.ts","default":"./dist/commonjs/pack.js"}},"./unpack":{"import":{"types":"./dist/esm/unpack.d.ts","default":"./dist/esm/unpack.js"},"require":{"types":"./dist/commonjs/unpack.d.ts","default":"./dist/commonjs/unpack.js"}},"./parse":{"import":{"types":"./dist/esm/parse.d.ts","default":"./dist/esm/parse.js"},"require":{"types":"./dist/commonjs/parse.d.ts","default":"./dist/commonjs/parse.js"}},"./read-entry":{"import":{"types":"./dist/esm/read-entry.d.ts","default":"./dist/esm/read-entry.js"},"require":{"types":"./dist/commonjs/read-entry.d.ts","default":"./dist/commonjs/read-entry.js"}},"./write-entry":{"import":{"types":"./dist/esm/write-entry.d.ts","default":"./dist/esm/write-entry.js"},"require":{"types":"./dist/commonjs/write-entry.d.ts","default":"./dist/commonjs/write-entry.js"}},"./header":{"import":{"types":"./dist/esm/header.d.ts","default":"./dist/esm/header.js"},"require":{"types":"./dist/commonjs/header.d.ts","default":"./dist/commonjs/header.js"}},"./pax":{"import":{"types":"./dist/esm/pax.d.ts","default":"./dist/esm/pax.js"},"require":{"types":"./dist/commonjs/pax.d.ts","default":"./dist/commonjs/pax.js"}},"./types":{"import":{"types":"./dist/esm/types.d.ts","default":"./dist/esm/types.js"},"require":{"types":"./dist/commonjs/types.d.ts","default":"./dist/commonjs/types.js"}}},"type":"module","main":"./dist/commonjs/index.min.js","types":"./dist/commonjs/index.d.ts","module":"./dist/esm/index.min.js","_lastModified":"2026-07-30T10:32:21.491Z"}
@@ -28,9 +28,10 @@ export declare class PluginMultiPortalServer extends Plugin {
28
28
  private getAppName;
29
29
  private getMultiPortalStorageItem;
30
30
  private warnPortalStorageSyncFailed;
31
+ private logPortalBuildHtml;
31
32
  private getPortalStorageTaskKey;
32
33
  private runPortalStorageTask;
33
- private removePortalStorageDist;
34
+ private removePortalStorageIndexHtml;
34
35
  private waitForPortalStorageTasks;
35
36
  private schedulePortalTemplateCopyAndBuild;
36
37
  private ensurePortalStorageItem;
@@ -597,12 +597,14 @@ async function buildPortalStorageItem(portalDir, item) {
597
597
  const buildEnv = getPortalStorageCommandEnv({
598
598
  NOCOBASE_API_URL: getPortalStorageApiUrl(item.appName),
599
599
  NOCOBASE_PORTAL_BASE: getPortalDeployBasePath(item.appName, item.portalName),
600
+ SKIP_YARN_COREPACK_CHECK: "1",
601
+ COREPACK_ENABLE_STRICT: "0",
600
602
  COREPACK_ENABLE_PROJECT_SPEC: "0"
601
603
  });
602
604
  await appendPortalStorageLog(logPath, `Building portal ${item.appName}/${item.portalName}.`);
603
605
  await appendPortalStorageLog(
604
606
  logPath,
605
- `Build environment: NOCOBASE_API_URL=${buildEnv.NOCOBASE_API_URL || ""} NOCOBASE_PORTAL_BASE=${buildEnv.NOCOBASE_PORTAL_BASE || ""} APP_PUBLIC_PATH=${buildEnv.APP_PUBLIC_PATH || ""}`
607
+ `Build environment: NOCOBASE_API_URL=${buildEnv.NOCOBASE_API_URL || ""} NOCOBASE_PORTAL_BASE=${buildEnv.NOCOBASE_PORTAL_BASE || ""} APP_PUBLIC_PATH=${buildEnv.APP_PUBLIC_PATH || ""} SKIP_YARN_COREPACK_CHECK=${buildEnv.SKIP_YARN_COREPACK_CHECK || ""} COREPACK_ENABLE_STRICT=${buildEnv.COREPACK_ENABLE_STRICT || ""} COREPACK_ENABLE_PROJECT_SPEC=${buildEnv.COREPACK_ENABLE_PROJECT_SPEC || ""}`
606
608
  );
607
609
  await runPortalStorageCommandOnce("yarn", ["build:html"], {
608
610
  cwd: portalDir,
@@ -1095,39 +1097,6 @@ async function seedHistoricalMultiPortals(db) {
1095
1097
  }
1096
1098
  await repairFixedLayoutMultiPortalRecords(db);
1097
1099
  }
1098
- async function preventUiLayoutRouteNameConflict(ctx, next) {
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
- }
1128
- }
1129
- await next();
1130
- }
1131
1100
  async function preventMultiPortalBackingLayoutChange(ctx, next) {
1132
1101
  const targets = await getMultiPortalWriteTargets(ctx, ["uiLayoutUid"]);
1133
1102
  for (const { existing, values } of targets) {
@@ -1242,7 +1211,6 @@ async function assertDesktopRouteParentsBelongToScope(ctx, scope, values = ((_a)
1242
1211
  id: parentIds,
1243
1212
  ...scope.filter
1244
1213
  },
1245
- ...transaction ? { lock: transaction.LOCK.UPDATE } : {},
1246
1214
  transaction
1247
1215
  });
1248
1216
  if (new Set(normalizeDesktopRouteTargetIds(parents)).size !== new Set(parentIds.map(String)).size) {
@@ -1276,7 +1244,6 @@ async function assertDesktopRouteUpsertMatchesBelongToScope(ctx, scope, values =
1276
1244
  ...filter,
1277
1245
  ...scope.filter
1278
1246
  },
1279
- ...transaction ? { lock: transaction.LOCK.UPDATE } : {},
1280
1247
  transaction
1281
1248
  });
1282
1249
  const allMatchIds = normalizeDesktopRouteTargetIds(allMatches);
@@ -1691,7 +1658,7 @@ async function findDesktopRouteMutationTargets(ctx, scopeFilter, transaction, se
1691
1658
  ...filter ?? {},
1692
1659
  ...scopeFilter ?? {}
1693
1660
  },
1694
- ...transaction ? { lock: transaction.LOCK.UPDATE } : {},
1661
+ ...transaction && !scopeFilter ? { lock: transaction.LOCK.UPDATE } : {},
1695
1662
  transaction
1696
1663
  });
1697
1664
  }
@@ -1843,7 +1810,6 @@ async function guardDesktopRouteMoveScope(ctx, next) {
1843
1810
  id: routeIds,
1844
1811
  ...scope.filter
1845
1812
  },
1846
- lock: transaction.LOCK.UPDATE,
1847
1813
  transaction
1848
1814
  });
1849
1815
  if (normalizeDesktopRouteTargetIds(allRoutes).join(",") !== normalizeDesktopRouteTargetIds(scopedRoutes).join(",") || scopedRoutes.length !== routeIds.length) {
@@ -1873,7 +1839,6 @@ async function guardDesktopRouteMoveScope(ctx, next) {
1873
1839
  id: parentIds,
1874
1840
  ...scope.filter
1875
1841
  },
1876
- lock: transaction.LOCK.UPDATE,
1877
1842
  transaction
1878
1843
  });
1879
1844
  if (normalizeDesktopRouteTargetIds(parents).join(",") !== parentIds.map(String).sort().join(",")) {
@@ -1921,7 +1886,6 @@ async function guardDesktopRouteMoveScope(ctx, next) {
1921
1886
  $gte: targetSort
1922
1887
  }
1923
1888
  },
1924
- lock: transaction.LOCK.UPDATE,
1925
1889
  transaction
1926
1890
  });
1927
1891
  const affectedRouteIds = affectedRoutes.map((route) => route.get("id"));
@@ -2019,7 +1983,7 @@ async function findDesktopRouteDestroyRoots(ctx, transaction, scopeFilter) {
2019
1983
  ...scopeFilter ?? {},
2020
1984
  ...filterByTk.length ? { id: filterByTk } : {}
2021
1985
  },
2022
- lock: transaction.LOCK.UPDATE,
1986
+ ...scopeFilter ? {} : { lock: transaction.LOCK.UPDATE },
2023
1987
  transaction
2024
1988
  });
2025
1989
  return collectDesktopRouteIds(records);
@@ -2085,13 +2049,20 @@ async function detachDesktopRouteTreeOwner(ctx, routeIds, scope, transaction) {
2085
2049
  }
2086
2050
  async function detachOwnerAndFindDestroyRoots(ctx, routeIds, scope, transaction) {
2087
2051
  var _a;
2052
+ await ctx.db.getRepository("desktopRoutes").find({
2053
+ fields: ["id"],
2054
+ filter: {
2055
+ id: routeIds
2056
+ },
2057
+ lock: transaction.LOCK.UPDATE,
2058
+ transaction
2059
+ });
2088
2060
  const routes = await ctx.db.getRepository("desktopRoutes").find({
2089
2061
  fields: ["id", "parentId"],
2090
2062
  appends: ["multiPortals", "uiLayouts"],
2091
2063
  filter: {
2092
2064
  id: routeIds
2093
2065
  },
2094
- lock: transaction.LOCK.UPDATE,
2095
2066
  transaction
2096
2067
  });
2097
2068
  const routesById = new Map(routes.map((route) => [String(route.get("id")), route]));
@@ -2373,6 +2344,14 @@ class PluginMultiPortalServer extends import_server.Plugin {
2373
2344
  error
2374
2345
  });
2375
2346
  }
2347
+ logPortalBuildHtml(item, status, reason) {
2348
+ var _a, _b;
2349
+ (_b = (_a = this.app.logger) == null ? void 0 : _a.info) == null ? void 0 : _b.call(_a, `Portal yarn build:html ${status} for ${item.appName}/${item.portalName}`, {
2350
+ appName: item.appName,
2351
+ portalName: item.portalName,
2352
+ reason
2353
+ });
2354
+ }
2376
2355
  getPortalStorageTaskKey(item) {
2377
2356
  return `${item.appName}/${item.portalName}`;
2378
2357
  }
@@ -2391,10 +2370,9 @@ class PluginMultiPortalServer extends import_server.Plugin {
2391
2370
  }
2392
2371
  await run();
2393
2372
  }
2394
- async removePortalStorageDist(item) {
2395
- await import_fs.default.promises.rm((0, import_utils.storagePathJoin)("portals", item.appName, item.portalName, "dist"), {
2396
- force: true,
2397
- recursive: true
2373
+ async removePortalStorageIndexHtml(item) {
2374
+ await import_fs.default.promises.rm((0, import_utils.storagePathJoin)("portals", item.appName, item.portalName, "dist", "index.html"), {
2375
+ force: true
2398
2376
  });
2399
2377
  }
2400
2378
  waitForPortalStorageTasks() {
@@ -2408,6 +2386,7 @@ class PluginMultiPortalServer extends import_server.Plugin {
2408
2386
  var _a;
2409
2387
  const taskKey = this.getPortalStorageTaskKey(item);
2410
2388
  if (this.portalStorageTaskKeys.has(taskKey)) {
2389
+ this.logPortalBuildHtml(item, "skipped", "a storage task is already running");
2411
2390
  await ((_a = template.cleanup) == null ? void 0 : _a.call(template));
2412
2391
  return;
2413
2392
  }
@@ -2431,11 +2410,14 @@ class PluginMultiPortalServer extends import_server.Plugin {
2431
2410
  await ensurePortalStorageConfigFiles(portalDir, item);
2432
2411
  await appendPortalStorageLog(logPath, `Portal configuration files updated in ${portalDir}.`);
2433
2412
  if (item.enabled) {
2413
+ this.logPortalBuildHtml(item, "requested", "storage directory was initialized");
2434
2414
  await buildPortalStorageItem(portalDir, item);
2415
+ this.logPortalBuildHtml(item, "completed", "yarn build:html finished successfully");
2435
2416
  return;
2436
2417
  }
2437
- await this.removePortalStorageDist(item);
2438
- await appendPortalStorageLog(logPath, `Portal dist directory removed for ${item.appName}/${item.portalName}.`);
2418
+ this.logPortalBuildHtml(item, "skipped", "the portal is disabled");
2419
+ await this.removePortalStorageIndexHtml(item);
2420
+ await appendPortalStorageLog(logPath, `Portal index.html removed for ${item.appName}/${item.portalName}.`);
2439
2421
  } catch (error) {
2440
2422
  await appendPortalStorageLog(
2441
2423
  logPath,
@@ -2451,32 +2433,44 @@ class PluginMultiPortalServer extends import_server.Plugin {
2451
2433
  this.portalStorageTasks.set(taskKey, task);
2452
2434
  await task;
2453
2435
  }
2454
- async ensurePortalStorageItem(item) {
2436
+ async ensurePortalStorageItem(item, options = {}) {
2455
2437
  const portalDir = (0, import_utils.storagePathJoin)("portals", item.appName, item.portalName);
2456
2438
  const portalIndex = import_path.default.join(portalDir, "dist", "index.html");
2439
+ const logPath = getPortalStorageLogPath(item);
2457
2440
  if (!await pathExists(portalDir)) {
2458
- const template = await resolvePortalTemplate(getInitPortalTemplate(), getPortalStorageLogPath(item));
2441
+ const template = await resolvePortalTemplate(getInitPortalTemplate(), logPath);
2459
2442
  await this.schedulePortalTemplateCopyAndBuild(item, template, portalDir);
2460
2443
  return;
2461
2444
  }
2462
2445
  await ensurePortalStorageConfigFiles(portalDir, item);
2463
2446
  if (item.enabled) {
2464
- if (!await pathExists(portalIndex)) {
2447
+ const hasPortalIndex = await pathExists(portalIndex);
2448
+ if (options.forceBuild || !hasPortalIndex) {
2449
+ this.logPortalBuildHtml(
2450
+ item,
2451
+ "requested",
2452
+ options.forceBuild ? "forceBuild is enabled" : "dist/index.html does not exist"
2453
+ );
2465
2454
  await buildPortalStorageItem(portalDir, item);
2455
+ this.logPortalBuildHtml(item, "completed", "yarn build:html finished successfully");
2456
+ } else {
2457
+ this.logPortalBuildHtml(item, "skipped", "dist/index.html already exists");
2466
2458
  }
2467
2459
  return;
2468
2460
  }
2469
- await this.removePortalStorageDist(item);
2461
+ this.logPortalBuildHtml(item, "skipped", "the portal is disabled");
2462
+ await this.removePortalStorageIndexHtml(item);
2463
+ await appendPortalStorageLog(logPath, `Portal index.html removed for ${item.appName}/${item.portalName}.`);
2470
2464
  }
2471
- async syncMultiPortalStorageItem(multiPortal, options, syncPrevious = false) {
2465
+ async syncMultiPortalStorageItem(multiPortal, options, syncPrevious = false, forceBuild = false) {
2472
2466
  const currentItem = this.getMultiPortalStorageItem(multiPortal);
2473
2467
  const previousItem = syncPrevious ? this.getMultiPortalStorageItem(multiPortal, true) : null;
2474
2468
  await this.runPortalStorageTask(async () => {
2475
2469
  if (previousItem && (!currentItem || previousItem.appName !== currentItem.appName || previousItem.portalName !== currentItem.portalName || !currentItem.enabled)) {
2476
- await this.removePortalStorageDist(previousItem);
2470
+ await this.removePortalStorageIndexHtml(previousItem);
2477
2471
  }
2478
2472
  if (currentItem) {
2479
- await this.ensurePortalStorageItem(currentItem);
2473
+ await this.ensurePortalStorageItem(currentItem, { forceBuild });
2480
2474
  }
2481
2475
  }, options);
2482
2476
  }
@@ -2825,24 +2819,20 @@ class PluginMultiPortalServer extends import_server.Plugin {
2825
2819
  );
2826
2820
  this.app.resourceManager.registerPreActionHandler("multiPortals:create", captureSkipCreatePortalDirectory);
2827
2821
  this.app.resourceManager.registerPreActionHandler("multiPortals:create", normalizeMultiPortalSlugValues);
2828
- this.app.resourceManager.registerPreActionHandler("multiPortals:create", preventUiLayoutRouteNameConflict);
2829
2822
  this.app.resourceManager.registerPreActionHandler("multiPortals:update", preventMultiPortalBackingLayoutChange);
2830
2823
  this.app.resourceManager.registerPreActionHandler("multiPortals:update", normalizeMultiPortalSlugValues);
2831
- this.app.resourceManager.registerPreActionHandler("multiPortals:update", preventUiLayoutRouteNameConflict);
2832
2824
  this.app.resourceManager.registerPreActionHandler(
2833
2825
  "multiPortals:firstOrCreate",
2834
2826
  preventMultiPortalBackingLayoutChange
2835
2827
  );
2836
2828
  this.app.resourceManager.registerPreActionHandler("multiPortals:firstOrCreate", captureSkipCreatePortalDirectory);
2837
2829
  this.app.resourceManager.registerPreActionHandler("multiPortals:firstOrCreate", normalizeMultiPortalSlugValues);
2838
- this.app.resourceManager.registerPreActionHandler("multiPortals:firstOrCreate", preventUiLayoutRouteNameConflict);
2839
2830
  this.app.resourceManager.registerPreActionHandler(
2840
2831
  "multiPortals:updateOrCreate",
2841
2832
  preventMultiPortalBackingLayoutChange
2842
2833
  );
2843
2834
  this.app.resourceManager.registerPreActionHandler("multiPortals:updateOrCreate", captureSkipCreatePortalDirectory);
2844
2835
  this.app.resourceManager.registerPreActionHandler("multiPortals:updateOrCreate", normalizeMultiPortalSlugValues);
2845
- this.app.resourceManager.registerPreActionHandler("multiPortals:updateOrCreate", preventUiLayoutRouteNameConflict);
2846
2836
  this.app.resourceManager.registerPreActionHandler("desktopRoutes:create", addDesktopRouteCreateMultiPortal, {
2847
2837
  after: UI_LAYOUT_DESKTOP_ROUTE_WRITE_LAYOUT_HANDLER_TAG
2848
2838
  });
@@ -2903,7 +2893,7 @@ class PluginMultiPortalServer extends import_server.Plugin {
2903
2893
  if (shouldSkipCreatePortalDirectory(options)) {
2904
2894
  return;
2905
2895
  }
2906
- await this.syncMultiPortalStorageItem(multiPortal, options);
2896
+ await this.syncMultiPortalStorageItem(multiPortal, options, false, true);
2907
2897
  });
2908
2898
  this.app.db.on("multiPortals.afterUpdate", async (multiPortal, options) => {
2909
2899
  await this.publishAppManifestItem(multiPortal, options);
@@ -0,0 +1,152 @@
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
+ declare const _default: {
10
+ openapi: string;
11
+ info: {
12
+ title: string;
13
+ version: string;
14
+ };
15
+ paths: {
16
+ '/roles/{roleName}/multiPortals:list': {
17
+ get: {
18
+ tags: string[];
19
+ summary: string;
20
+ description: string;
21
+ parameters: {
22
+ $ref: string;
23
+ }[];
24
+ responses: {
25
+ 200: {
26
+ description: string;
27
+ content: {
28
+ 'application/json': {
29
+ schema: {
30
+ type: string;
31
+ properties: {
32
+ data: {
33
+ type: string;
34
+ items: {
35
+ $ref: string;
36
+ };
37
+ };
38
+ };
39
+ };
40
+ };
41
+ };
42
+ };
43
+ };
44
+ };
45
+ };
46
+ '/roles/{roleName}/multiPortals:add': {
47
+ post: {
48
+ tags: string[];
49
+ summary: string;
50
+ description: string;
51
+ parameters: {
52
+ $ref: string;
53
+ }[];
54
+ requestBody: {
55
+ required: boolean;
56
+ content: {
57
+ 'application/json': {
58
+ schema: {
59
+ $ref: string;
60
+ };
61
+ };
62
+ };
63
+ };
64
+ responses: {
65
+ 200: {
66
+ description: string;
67
+ };
68
+ };
69
+ };
70
+ };
71
+ '/roles/{roleName}/multiPortals:remove': {
72
+ post: {
73
+ tags: string[];
74
+ summary: string;
75
+ description: string;
76
+ parameters: {
77
+ $ref: string;
78
+ }[];
79
+ requestBody: {
80
+ required: boolean;
81
+ content: {
82
+ 'application/json': {
83
+ schema: {
84
+ $ref: string;
85
+ };
86
+ };
87
+ };
88
+ };
89
+ responses: {
90
+ 200: {
91
+ description: string;
92
+ };
93
+ };
94
+ };
95
+ };
96
+ };
97
+ components: {
98
+ parameters: {
99
+ RoleNamePath: {
100
+ name: string;
101
+ in: string;
102
+ description: string;
103
+ required: boolean;
104
+ schema: {
105
+ type: string;
106
+ };
107
+ };
108
+ };
109
+ schemas: {
110
+ PortalAccessWrite: {
111
+ type: string;
112
+ properties: {
113
+ values: {
114
+ type: string;
115
+ description: string;
116
+ items: {
117
+ type: string;
118
+ };
119
+ minItems: number;
120
+ uniqueItems: boolean;
121
+ };
122
+ };
123
+ required: string[];
124
+ };
125
+ PortalAccessTarget: {
126
+ type: string;
127
+ additionalProperties: boolean;
128
+ properties: {
129
+ uid: {
130
+ type: string;
131
+ };
132
+ title: {
133
+ type: string;
134
+ };
135
+ portalName: {
136
+ type: string;
137
+ };
138
+ routePath: {
139
+ type: string;
140
+ };
141
+ portalType: {
142
+ type: string;
143
+ };
144
+ enabled: {
145
+ type: string;
146
+ };
147
+ };
148
+ };
149
+ };
150
+ };
151
+ };
152
+ export default _default;
@@ -0,0 +1,142 @@
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 swagger_exports = {};
28
+ __export(swagger_exports, {
29
+ default: () => swagger_default
30
+ });
31
+ module.exports = __toCommonJS(swagger_exports);
32
+ var swagger_default = {
33
+ openapi: "3.0.2",
34
+ info: {
35
+ title: "NocoBase API - Multi-portal plugin",
36
+ version: "1.0.0"
37
+ },
38
+ paths: {
39
+ "/roles/{roleName}/multiPortals:list": {
40
+ get: {
41
+ tags: ["roles.multiPortals"],
42
+ summary: "List Portals granted to a role",
43
+ description: "List existing custom Portals that the role is explicitly allowed to enter.",
44
+ parameters: [{ $ref: "#/components/parameters/RoleNamePath" }],
45
+ responses: {
46
+ 200: {
47
+ description: "OK",
48
+ content: {
49
+ "application/json": {
50
+ schema: {
51
+ type: "object",
52
+ properties: {
53
+ data: {
54
+ type: "array",
55
+ items: { $ref: "#/components/schemas/PortalAccessTarget" }
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+ },
65
+ "/roles/{roleName}/multiPortals:add": {
66
+ post: {
67
+ tags: ["roles.multiPortals"],
68
+ summary: "Grant Portal entry access to a role",
69
+ description: "Allow the role to enter one or more existing custom Portals by uid.",
70
+ parameters: [{ $ref: "#/components/parameters/RoleNamePath" }],
71
+ requestBody: {
72
+ required: true,
73
+ content: {
74
+ "application/json": {
75
+ schema: { $ref: "#/components/schemas/PortalAccessWrite" }
76
+ }
77
+ }
78
+ },
79
+ responses: {
80
+ 200: { description: "OK" }
81
+ }
82
+ }
83
+ },
84
+ "/roles/{roleName}/multiPortals:remove": {
85
+ post: {
86
+ tags: ["roles.multiPortals"],
87
+ summary: "Revoke Portal entry access from a role",
88
+ description: "Prevent the role from entering one or more existing custom Portals by uid.",
89
+ parameters: [{ $ref: "#/components/parameters/RoleNamePath" }],
90
+ requestBody: {
91
+ required: true,
92
+ content: {
93
+ "application/json": {
94
+ schema: { $ref: "#/components/schemas/PortalAccessWrite" }
95
+ }
96
+ }
97
+ },
98
+ responses: {
99
+ 200: { description: "OK" }
100
+ }
101
+ }
102
+ }
103
+ },
104
+ components: {
105
+ parameters: {
106
+ RoleNamePath: {
107
+ name: "roleName",
108
+ in: "path",
109
+ description: "Role name.",
110
+ required: true,
111
+ schema: { type: "string" }
112
+ }
113
+ },
114
+ schemas: {
115
+ PortalAccessWrite: {
116
+ type: "object",
117
+ properties: {
118
+ values: {
119
+ type: "array",
120
+ description: "Portal uids.",
121
+ items: { type: "string" },
122
+ minItems: 1,
123
+ uniqueItems: true
124
+ }
125
+ },
126
+ required: ["values"]
127
+ },
128
+ PortalAccessTarget: {
129
+ type: "object",
130
+ additionalProperties: true,
131
+ properties: {
132
+ uid: { type: "string" },
133
+ title: { type: "string" },
134
+ portalName: { type: "string" },
135
+ routePath: { type: "string" },
136
+ portalType: { type: "string" },
137
+ enabled: { type: "boolean" }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/plugin-multi-portal",
3
- "version": "3.0.0-alpha.1",
3
+ "version": "3.0.0-alpha.3",
4
4
  "main": "dist/server/index.js",
5
5
  "nocobase": {
6
6
  "supportedVersions": [
@@ -13,7 +13,7 @@
13
13
  "description.zh-CN": "为 Client V2 提供内置 Portal 注册、入口访问与路由权限管理。",
14
14
  "devDependencies": {
15
15
  "@ant-design/icons": "5.x",
16
- "@nocobase/plugin-ui-layout": "3.0.0-alpha.1",
16
+ "@nocobase/plugin-ui-layout": "3.0.0-alpha.3",
17
17
  "ahooks": "3.x",
18
18
  "antd": "5.x",
19
19
  "react": "^18.2.0",
@@ -29,5 +29,5 @@
29
29
  "@nocobase/server": "2.x",
30
30
  "@nocobase/test": "2.x"
31
31
  },
32
- "gitHead": "22d8be8ece179cfa5c8a4ebb2c896c92bd418e03"
32
+ "gitHead": "9899cd799c070c7bd85140148ea5b100b046bdc7"
33
33
  }