@nocobase/plugin-client 2.1.0-alpha.10 → 2.1.0-alpha.11

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.
@@ -62,6 +62,31 @@ async function getLang(ctx) {
62
62
  }
63
63
  return lang;
64
64
  }
65
+ function getModelValue(record, key) {
66
+ if (!record) {
67
+ return void 0;
68
+ }
69
+ return typeof record.get === "function" ? record.get(key) : record[key];
70
+ }
71
+ function isManagedFlowRouteShell(record, schemaUid) {
72
+ const actualSchemaUid = String(getModelValue(record, "x-uid") ?? getModelValue(record, "uid") ?? "").trim();
73
+ const schema = getModelValue(record, "schema");
74
+ if (!import_lodash.default.isPlainObject(schema)) {
75
+ return false;
76
+ }
77
+ return actualSchemaUid === schemaUid && (schema == null ? void 0 : schema["x-component"]) === "FlowRoute" && String((schema == null ? void 0 : schema["x-uid"]) ?? "").trim() === schemaUid;
78
+ }
79
+ function isTransactionFinished(transaction) {
80
+ return !!(transaction == null ? void 0 : transaction.finished);
81
+ }
82
+ async function rollbackTransaction(transaction, options = {}) {
83
+ var _a, _b;
84
+ if (!transaction || isTransactionFinished(transaction)) {
85
+ return;
86
+ }
87
+ await transaction.rollback();
88
+ await ((_b = (_a = options.flowModelsRepository) == null ? void 0 : _a.emitTransactionRollback) == null ? void 0 : _b.call(_a, transaction));
89
+ }
65
90
  class PluginClientServer extends import_server.Plugin {
66
91
  async beforeLoad() {
67
92
  }
@@ -170,7 +195,9 @@ class PluginClientServer extends import_server.Plugin {
170
195
  "desktopRoutes:update",
171
196
  "desktopRoutes:move",
172
197
  "desktopRoutes:destroy",
173
- "desktopRoutes:updateOrCreate"
198
+ "desktopRoutes:updateOrCreate",
199
+ "desktopRoutes:createV2",
200
+ "desktopRoutes:destroyV2"
174
201
  ]
175
202
  });
176
203
  this.app.acl.registerSnippet({
@@ -328,6 +355,221 @@ class PluginClientServer extends import_server.Plugin {
328
355
  }
329
356
  await next();
330
357
  });
358
+ this.app.resourceManager.registerActionHandler("desktopRoutes:createV2", async (ctx, next) => {
359
+ var _a, _b, _c, _d, _e;
360
+ const { values } = ctx.action.params;
361
+ const schemaUid = String((values == null ? void 0 : values.schemaUid) || "").trim();
362
+ const title = String((values == null ? void 0 : values.title) || "").trim();
363
+ const icon = values == null ? void 0 : values.icon;
364
+ const parentId = (values == null ? void 0 : values.parentId) ?? null;
365
+ if (!schemaUid) {
366
+ return ctx.throw(400, { code: "INVALID_PARAMS", message: "desktopRoutes:createV2 requires 'schemaUid'" });
367
+ }
368
+ if (!title) {
369
+ return ctx.throw(400, { code: "INVALID_PARAMS", message: "desktopRoutes:createV2 requires 'title'" });
370
+ }
371
+ const menuSchemaUid = `menu-${schemaUid}`;
372
+ const tabSchemaUid = `tabs-${schemaUid}`;
373
+ const tabSchemaName = `tab-${schemaUid}`;
374
+ const desktopRoutesRepository = ctx.db.getRepository("desktopRoutes");
375
+ const uiSchemasRepository = ctx.db.getRepository("uiSchemas");
376
+ const uiSchemasModel = (_a = ctx.db.getCollection("uiSchemas")) == null ? void 0 : _a.model;
377
+ const flowModelsRepository = ctx.db.getRepository("flowModels");
378
+ const transaction = await ctx.db.sequelize.transaction();
379
+ const pickPage = (record) => ({
380
+ id: record == null ? void 0 : record.id,
381
+ schemaUid: record == null ? void 0 : record.schemaUid,
382
+ title: record == null ? void 0 : record.title,
383
+ icon: record == null ? void 0 : record.icon,
384
+ parentId: (record == null ? void 0 : record.parentId) ?? null,
385
+ menuSchemaUid: record == null ? void 0 : record.menuSchemaUid
386
+ });
387
+ const pickTab = (record) => record ? {
388
+ id: record == null ? void 0 : record.id,
389
+ schemaUid: record == null ? void 0 : record.schemaUid,
390
+ tabSchemaName: record == null ? void 0 : record.tabSchemaName
391
+ } : null;
392
+ const isSameValue = (a, b) => String(a ?? "") === String(b ?? "");
393
+ try {
394
+ const existingPage = await desktopRoutesRepository.findOne({
395
+ filter: { type: "flowPage", schemaUid },
396
+ transaction
397
+ });
398
+ if (existingPage) {
399
+ const pageJson = ((_b = existingPage.toJSON) == null ? void 0 : _b.call(existingPage)) || existingPage;
400
+ const existingUiSchema = await uiSchemasRepository.findOne({
401
+ filterByTk: schemaUid,
402
+ transaction
403
+ });
404
+ if (existingUiSchema && !isManagedFlowRouteShell(existingUiSchema, schemaUid)) {
405
+ ctx.throw(409, {
406
+ code: "CONFLICT",
407
+ message: `desktopRoutes:createV2 schemaUid '${schemaUid}' is already occupied by a non-FlowRoute uiSchema`
408
+ });
409
+ }
410
+ const conflict = !isSameValue(pageJson == null ? void 0 : pageJson.title, title) || !isSameValue(pageJson == null ? void 0 : pageJson.icon, icon) || !isSameValue((pageJson == null ? void 0 : pageJson.parentId) ?? null, parentId ?? null);
411
+ if (conflict) {
412
+ ctx.throw(409, {
413
+ code: "CONFLICT",
414
+ message: `desktopRoutes:createV2 schemaUid '${schemaUid}' already exists with different fields`,
415
+ details: {
416
+ expected: { title, icon, parentId: parentId ?? null },
417
+ actual: { title: pageJson == null ? void 0 : pageJson.title, icon: pageJson == null ? void 0 : pageJson.icon, parentId: (pageJson == null ? void 0 : pageJson.parentId) ?? null }
418
+ }
419
+ });
420
+ }
421
+ const existingTab = await desktopRoutesRepository.findOne({
422
+ filter: { parentId: pageJson == null ? void 0 : pageJson.id, type: "tabs", hidden: true },
423
+ transaction
424
+ });
425
+ ctx.body = { page: pickPage(pageJson), defaultTab: pickTab(((_c = existingTab == null ? void 0 : existingTab.toJSON) == null ? void 0 : _c.call(existingTab)) || existingTab) };
426
+ await next();
427
+ await transaction.commit();
428
+ return;
429
+ }
430
+ if (uiSchemasModel) {
431
+ const [uiSchemaMutex] = await uiSchemasModel.findOrCreate({
432
+ where: { "x-uid": schemaUid },
433
+ defaults: {
434
+ "x-uid": schemaUid,
435
+ schema: {
436
+ type: "void",
437
+ "x-component": "FlowRoute",
438
+ "x-uid": schemaUid
439
+ }
440
+ },
441
+ transaction
442
+ });
443
+ if (!isManagedFlowRouteShell(uiSchemaMutex, schemaUid)) {
444
+ ctx.throw(409, {
445
+ code: "CONFLICT",
446
+ message: `desktopRoutes:createV2 schemaUid '${schemaUid}' is already occupied by a non-FlowRoute uiSchema`
447
+ });
448
+ }
449
+ const dialect = ctx.db.sequelize.getDialect();
450
+ const supportsLock = dialect !== "sqlite";
451
+ const lockedUiSchema = await uiSchemasModel.findByPk(
452
+ schemaUid,
453
+ supportsLock ? { transaction, lock: transaction.LOCK.UPDATE } : { transaction }
454
+ );
455
+ if (lockedUiSchema && !isManagedFlowRouteShell(lockedUiSchema, schemaUid)) {
456
+ ctx.throw(409, {
457
+ code: "CONFLICT",
458
+ message: `desktopRoutes:createV2 schemaUid '${schemaUid}' is already occupied by a non-FlowRoute uiSchema`
459
+ });
460
+ }
461
+ }
462
+ const existingAfterLock = await desktopRoutesRepository.findOne({
463
+ filter: { type: "flowPage", schemaUid },
464
+ transaction
465
+ });
466
+ if (existingAfterLock) {
467
+ const pageJson = ((_d = existingAfterLock.toJSON) == null ? void 0 : _d.call(existingAfterLock)) || existingAfterLock;
468
+ const conflict = !isSameValue(pageJson == null ? void 0 : pageJson.title, title) || !isSameValue(pageJson == null ? void 0 : pageJson.icon, icon) || !isSameValue((pageJson == null ? void 0 : pageJson.parentId) ?? null, parentId ?? null);
469
+ if (conflict) {
470
+ ctx.throw(409, {
471
+ code: "CONFLICT",
472
+ message: `desktopRoutes:createV2 schemaUid '${schemaUid}' already exists with different fields`,
473
+ details: {
474
+ expected: { title, icon, parentId: parentId ?? null },
475
+ actual: { title: pageJson == null ? void 0 : pageJson.title, icon: pageJson == null ? void 0 : pageJson.icon, parentId: (pageJson == null ? void 0 : pageJson.parentId) ?? null }
476
+ }
477
+ });
478
+ }
479
+ const existingTab = await desktopRoutesRepository.findOne({
480
+ filter: { parentId: pageJson == null ? void 0 : pageJson.id, type: "tabs", hidden: true },
481
+ transaction
482
+ });
483
+ ctx.body = { page: pickPage(pageJson), defaultTab: pickTab(((_e = existingTab == null ? void 0 : existingTab.toJSON) == null ? void 0 : _e.call(existingTab)) || existingTab) };
484
+ await next();
485
+ await transaction.commit();
486
+ return;
487
+ }
488
+ const createdPage = await desktopRoutesRepository.create({
489
+ transaction,
490
+ values: {
491
+ type: "flowPage",
492
+ schemaUid,
493
+ title,
494
+ icon,
495
+ parentId: parentId ?? null,
496
+ menuSchemaUid,
497
+ enableTabs: false
498
+ }
499
+ });
500
+ const createdTab = await desktopRoutesRepository.create({
501
+ transaction,
502
+ values: {
503
+ type: "tabs",
504
+ parentId: createdPage.get("id"),
505
+ schemaUid: tabSchemaUid,
506
+ tabSchemaName,
507
+ hidden: true
508
+ }
509
+ });
510
+ await flowModelsRepository.ensureModel(
511
+ { parentId: schemaUid, subKey: "page", subType: "object", use: "RootPageModel", async: true },
512
+ { transaction }
513
+ );
514
+ await flowModelsRepository.ensureModel(
515
+ { parentId: tabSchemaUid, subKey: "grid", subType: "object", use: "BlockGridModel", async: true },
516
+ { transaction }
517
+ );
518
+ ctx.body = {
519
+ page: pickPage(createdPage.toJSON()),
520
+ defaultTab: pickTab(createdTab.toJSON())
521
+ };
522
+ await next();
523
+ await transaction.commit();
524
+ } catch (error) {
525
+ await rollbackTransaction(transaction, { flowModelsRepository });
526
+ throw error;
527
+ }
528
+ });
529
+ this.app.resourceManager.registerActionHandler("desktopRoutes:destroyV2", async (ctx, next) => {
530
+ const { values } = ctx.action.params;
531
+ const schemaUid = String((values == null ? void 0 : values.schemaUid) || "").trim();
532
+ if (!schemaUid) {
533
+ return ctx.throw(400, { code: "INVALID_PARAMS", message: "desktopRoutes:destroyV2 requires 'schemaUid'" });
534
+ }
535
+ const desktopRoutesRepository = ctx.db.getRepository("desktopRoutes");
536
+ const uiSchemasRepository = ctx.db.getRepository("uiSchemas");
537
+ const transaction = await ctx.db.sequelize.transaction();
538
+ try {
539
+ const page = await desktopRoutesRepository.findOne({
540
+ filter: { type: "flowPage", schemaUid },
541
+ transaction
542
+ });
543
+ const uiSchema = await uiSchemasRepository.findOne({
544
+ filterByTk: schemaUid,
545
+ transaction
546
+ });
547
+ if (uiSchema && !isManagedFlowRouteShell(uiSchema, schemaUid)) {
548
+ ctx.throw(409, {
549
+ code: "CONFLICT",
550
+ message: `desktopRoutes:destroyV2 schemaUid '${schemaUid}' is occupied by a non-FlowRoute uiSchema`
551
+ });
552
+ }
553
+ if (page) {
554
+ await desktopRoutesRepository.destroy({
555
+ filterByTk: page.get("id"),
556
+ transaction
557
+ });
558
+ }
559
+ if (uiSchema) {
560
+ await uiSchemasRepository.destroy({
561
+ filterByTk: schemaUid,
562
+ transaction
563
+ });
564
+ }
565
+ ctx.body = { ok: true };
566
+ await next();
567
+ await transaction.commit();
568
+ } catch (error) {
569
+ await rollbackTransaction(transaction);
570
+ throw error;
571
+ }
572
+ });
331
573
  this.app.resourceManager.registerActionHandler("roles.desktopRoutes:set", async (ctx, next) => {
332
574
  let { values } = ctx.action.params;
333
575
  if (values.length) {