adminizer 4.3.0-build.99 → 4.3.1

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 (163) hide show
  1. package/assets/add-9NSjG6Jx.js +1 -0
  2. package/assets/add-form-uUiOWEy_.js +1 -0
  3. package/assets/add-group-Db0HkA1m.js +1 -0
  4. package/assets/add-group-form-DMz0_Oo9.js +1 -0
  5. package/assets/add-user-BMklpOqf.js +1 -0
  6. package/assets/{add-user-form-DrclnTOd.js → add-user-form-izlXhv2-.js} +1 -1
  7. package/assets/{app-D8oqdt8J.css → app-BJd5vD4i.css} +1 -1
  8. package/assets/app-layout-DcgYmhNb.js +1 -0
  9. package/assets/{app-shell-CHEYhVco.js → app-shell-C8Cnr5Hy.js} +1 -1
  10. package/assets/app-sidebar-HwvXNZzk.js +1 -0
  11. package/assets/app-sidebar-header-C8TTx8yd.js +1 -0
  12. package/assets/app.js +5 -5
  13. package/assets/{breadcrumbs-C439DWrC.js → breadcrumbs-DSMzNYiS.js} +1 -1
  14. package/assets/catalog-C2INmTwI.js +6 -0
  15. package/assets/catalog-D4n6dpGl.css +1 -0
  16. package/assets/{dashboard-Bi5KeZu2.js → dashboard-DwFL6nqn.js} +1 -1
  17. package/assets/{date-picker-DbJ8nHp5.js → date-picker-kW3wZQr_.js} +1 -1
  18. package/assets/{del-modal-_b3CSNHb.js → del-modal-DyIWFflP.js} +1 -1
  19. package/assets/{field-renderer-4obZ_jUo.js → field-renderer-BNQVfOir.js} +3 -3
  20. package/assets/form-CAIoGuhZ.js +1 -0
  21. package/assets/form-NUd-JZJN.js +1 -0
  22. package/assets/{geo-json-CadjdhwW.js → geo-json-YeWQOYnT.js} +1 -1
  23. package/assets/{home-BUoDaWe0.js → home-CteDT1us.js} +2 -2
  24. package/assets/{icon-DM49pZW3.js → icon-DpkedrXG.js} +1 -1
  25. package/assets/{init-user-DNrB8Xt5.js → init-user-DaOvt0vG.js} +1 -1
  26. package/assets/{input-error--IOnWghB.js → input-error-CmXZF6xy.js} +1 -1
  27. package/assets/list-CXQmJbH8.js +1 -0
  28. package/assets/{list-table-BKxxQkCW.js → list-table-BPpHDVPf.js} +4 -4
  29. package/assets/{login-6Xb8bcPq.js → login-DR6fvKtm.js} +1 -1
  30. package/assets/manifest.json +82 -82
  31. package/assets/{material-icon-BedgD2NE.js → material-icon-DwkE6wKL.js} +1 -1
  32. package/assets/media-manager-DYOfUh8F.js +14 -0
  33. package/assets/{module-B4-7WkY5.js → module-BIUqcf4-.js} +1 -1
  34. package/assets/{nav-footer-BwkQkwow.js → nav-footer-B85HLj-j.js} +1 -1
  35. package/assets/{nav-main-9Eb_A0rf.js → nav-main-BqKm87JZ.js} +1 -1
  36. package/assets/nav-user-DuCZNsZX.js +1 -0
  37. package/assets/notification-BQTjxH2H.js +27 -0
  38. package/assets/pagination-render-io0M2m2O.js +1 -0
  39. package/assets/{register-BIyjCy8M.js → register-hJD1ETdf.js} +1 -1
  40. package/assets/{tabs-BK_5wdPA.js → tabs-BhkFoerH.js} +1 -1
  41. package/assets/{theme-switcher-CKiJbMkh.js → theme-switcher-cgyA7_cO.js} +1 -1
  42. package/assets/{toast-editor-BZM5RHph.js → toast-editor-DwRk6WqT.js} +1 -1
  43. package/assets/{user-info-CcYdmjMZ.js → user-info-BGQvqBmO.js} +1 -1
  44. package/assets/{user-menu-content-DbUk8ZeY.js → user-menu-content-BDm88xaM.js} +1 -1
  45. package/assets/welcome-ks57d2us.js +1 -0
  46. package/controllers/add.js +8 -8
  47. package/controllers/addGroup.js +3 -3
  48. package/controllers/addUser.js +3 -3
  49. package/controllers/catalog/Catalog.js +22 -21
  50. package/controllers/catalog/FrontendCatalogAdapter.d.ts +82 -0
  51. package/controllers/catalog/FrontendCatalogAdapter.js +228 -0
  52. package/controllers/catalog/FrontentCatalogAdapter.d.ts +4 -4
  53. package/controllers/catalog/FrontentCatalogAdapter.js +28 -10
  54. package/controllers/ckeditorUpload.js +1 -1
  55. package/controllers/edit.js +21 -12
  56. package/controllers/editGroup.js +3 -3
  57. package/controllers/editUser.js +3 -3
  58. package/controllers/form.js +12 -10
  59. package/controllers/initUser.js +2 -2
  60. package/controllers/list.js +5 -5
  61. package/controllers/login.js +7 -4
  62. package/controllers/media-manager/ThumbController.js +2 -2
  63. package/controllers/media-manager/mediaManagerAdapter.js +2 -2
  64. package/controllers/media-manager/mediaManagerApi.js +2 -3
  65. package/controllers/navigation/edit.js +2 -2
  66. package/controllers/notifications/NotificationController.d.ts +1 -1
  67. package/controllers/notifications/NotificationController.js +6 -5
  68. package/controllers/register.js +1 -1
  69. package/controllers/remove.js +4 -4
  70. package/controllers/view.js +13 -11
  71. package/helpers/accessRightsHelper.js +1 -1
  72. package/helpers/controllerHelper.js +1 -1
  73. package/helpers/fieldsHelper.js +3 -3
  74. package/helpers/formHelper.js +1 -1
  75. package/helpers/inertiaAddHelper.js +3 -4
  76. package/helpers/inertiaFromHelper.js +2 -3
  77. package/helpers/inertiaListHelper.js +1 -1
  78. package/helpers/menuHelper.js +1 -1
  79. package/helpers/translationHelper.js +2 -2
  80. package/index.d.ts +5 -0
  81. package/index.js +31 -26
  82. package/interfaces/adminpanelConfig.d.ts +12 -0
  83. package/lib/Adminizer.d.ts +4 -3
  84. package/lib/Adminizer.js +41 -47
  85. package/lib/DataAccessor.js +3 -3
  86. package/lib/I18n.js +1 -1
  87. package/lib/PolicyManager.js +1 -1
  88. package/lib/catalog/AbstractCatalog.d.ts +6 -4
  89. package/lib/catalog/AbstractCatalog.js +5 -4
  90. package/lib/catalog/Navigation.d.ts +9 -7
  91. package/lib/catalog/Navigation.js +71 -42
  92. package/lib/controls/codeEditor/MonacoEditor.js +1 -1
  93. package/lib/controls/geojsoneditor/GeoEditor.js +1 -1
  94. package/lib/controls/jsoneditor/JsonEditor.js +1 -1
  95. package/lib/controls/markdown/ToastUiEditor.js +1 -1
  96. package/lib/controls/table/Handsontable.js +1 -1
  97. package/lib/controls/wysiwyg/CKeditor.js +1 -1
  98. package/lib/inertia/inertiaAdapter.js +31 -13
  99. package/lib/media-manager/AbstractMediaManager.d.ts +5 -3
  100. package/lib/media-manager/DefaultMediaManager.d.ts +3 -3
  101. package/lib/media-manager/DefaultMediaManager.js +21 -16
  102. package/lib/media-manager/Items.js +7 -6
  103. package/lib/media-manager/MediaManagerHandler.d.ts +3 -3
  104. package/lib/media-manager/MediaManagerHandler.js +4 -4
  105. package/lib/media-manager/Thumb.d.ts +2 -1
  106. package/lib/media-manager/Thumb.js +3 -4
  107. package/lib/media-manager/helpers/MediaManagerHelper.d.ts +7 -2
  108. package/lib/media-manager/helpers/MediaManagerHelper.js +12 -8
  109. package/lib/model/AbstractModel.js +1 -1
  110. package/lib/model/ModelHandler.js +1 -1
  111. package/lib/model/adapter/sequelize.js +1 -1
  112. package/lib/model/adapter/waterline.js +7 -1
  113. package/lib/notifications/AbstractNotificationService.js +1 -1
  114. package/lib/notifications/GeneralNotificationService.js +2 -2
  115. package/lib/notifications/NotificationHandler.js +1 -1
  116. package/lib/notifications/SystemNotificationService.js +2 -2
  117. package/lib/requestProcessor.js +2 -2
  118. package/lib/widgets/abstractAction.js +1 -1
  119. package/lib/widgets/abstractInfo.js +1 -1
  120. package/lib/widgets/abstractLink.js +1 -1
  121. package/lib/widgets/abstractSwitch.js +1 -1
  122. package/migrations/index.js +1 -1
  123. package/migrations/umzugExports.js +1 -1
  124. package/package.json +4 -2
  125. package/system/Router.js +31 -29
  126. package/system/bindAccessRights.js +7 -0
  127. package/system/bindAuthorization.js +4 -4
  128. package/system/bindControls.js +6 -6
  129. package/system/bindCors.d.ts +2 -0
  130. package/system/bindCors.js +41 -0
  131. package/system/bindDashboardWidgets.js +1 -1
  132. package/system/bindForms.js +1 -1
  133. package/system/bindInertia.js +4 -4
  134. package/system/bindMediaManager.js +2 -3
  135. package/system/bindModels.js +1 -1
  136. package/system/bindNavigation.d.ts +1 -1
  137. package/system/bindNavigation.js +34 -36
  138. package/system/bindNotifications.js +7 -7
  139. package/system/bindReqFunctions.js +7 -4
  140. package/system/bindTranslations.js +2 -2
  141. package/system/defaults.js +10 -2
  142. package/translations/ru.json +121 -120
  143. package/ui/components/handsontable.js +6 -3
  144. package/ui/lib/utils.d.ts +1 -0
  145. package/ui/lib/utils.js +16 -0
  146. package/assets/add-1E1UsMVZ.js +0 -1
  147. package/assets/add-form-CGa9QbwE.js +0 -1
  148. package/assets/add-group-7yAK6uFY.js +0 -1
  149. package/assets/add-group-form-BNmpvW8K.js +0 -1
  150. package/assets/add-user-NoYkTfZi.js +0 -1
  151. package/assets/app-layout-i2zN9Rn-.js +0 -1
  152. package/assets/app-sidebar-DeF8WNmW.js +0 -1
  153. package/assets/app-sidebar-header-DdM1EOrb.js +0 -1
  154. package/assets/catalog-B9hdop59.js +0 -6
  155. package/assets/catalog-C71LuRag.css +0 -1
  156. package/assets/form-Bte5kzjB.js +0 -1
  157. package/assets/form-oUlsuu7h.js +0 -1
  158. package/assets/list-C04Krl_W.js +0 -1
  159. package/assets/media-manager-UQT6RhI8.js +0 -14
  160. package/assets/nav-user-CRAfyE-x.js +0 -1
  161. package/assets/notification-ma2x7vka.js +0 -27
  162. package/assets/pagination-render-D3UyVAlr.js +0 -1
  163. package/assets/welcome-cWh1qnl_.js +0 -1
@@ -1,6 +1,6 @@
1
- import { AbstractMediaManager, } from "./AbstractMediaManager";
2
- import { populateVariants } from "./helpers/MediaManagerHelper";
3
- import { ApplicationItem, ImageItem, TextItem, VideoItem } from "./Items";
1
+ import { AbstractMediaManager, } from "./AbstractMediaManager.js";
2
+ import { populateVariants } from "./helpers/MediaManagerHelper.js";
3
+ import { ApplicationItem, ImageItem, TextItem, VideoItem } from "./Items.js";
4
4
  export class DefaultMediaManager extends AbstractMediaManager {
5
5
  itemTypes = [];
6
6
  model = "mediamanagerap";
@@ -57,7 +57,7 @@ export class DefaultMediaManager extends AbstractMediaManager {
57
57
  }
58
58
  async setRelations(data, model, modelId, widgetName) {
59
59
  let modelAssociations = await this.adminizer.modelHandler.model.get(this.modelAssoc)["_find"]({
60
- where: { modelId: modelId, model: model, widgetName: widgetName },
60
+ where: { modelId: +modelId, model: model.toLowerCase(), widgetName: widgetName },
61
61
  });
62
62
  for (const modelAssociation of modelAssociations) {
63
63
  const q = {};
@@ -69,26 +69,31 @@ export class DefaultMediaManager extends AbstractMediaManager {
69
69
  for (const [key, widgetItem] of data.entries()) {
70
70
  await this.adminizer.modelHandler.model.get(this.modelAssoc)["_create"]({
71
71
  mediaManagerId: this.id,
72
- model: model,
73
- modelId: modelId,
72
+ model: model.toLowerCase(),
73
+ modelId: +modelId,
74
74
  [fieldName]: widgetItem.id,
75
75
  widgetName: widgetName,
76
76
  sortOrder: key + 1,
77
77
  });
78
78
  }
79
79
  }
80
- async getItemsList(items) {
80
+ async getRelations(model, widgetName, modelId) {
81
81
  let widgetItems = [];
82
- for (const item of items) {
83
- //TODO refactor CRUD functions for DataAccessor usage
84
- let file = (await this.adminizer.modelHandler.model.get(this.model)["_find"]({
85
- where: { id: item.id }
86
- }, { populate: [["variants", { sort: "createdAt DESC" }]] }))[0];
82
+ const fieldName = this.adminizer.ormAdapters[0].ormType === 'sequelize' ? 'fileRef' : 'file';
83
+ let files = await this.adminizer.modelHandler.model.get(this.modelAssoc)['_find']({
84
+ where: {
85
+ model: model.toLowerCase(),
86
+ widgetName: widgetName,
87
+ modelId: +modelId
88
+ },
89
+ sort: "sortOrder ASC"
90
+ }, { populate: [[fieldName, {}]] });
91
+ for (const file of files) {
87
92
  widgetItems.push({
88
- id: file.id,
89
- mimeType: file.mimeType,
90
- url: file.url,
91
- variants: file.variants,
93
+ id: file[fieldName].id,
94
+ mimeType: file[fieldName].mimeType,
95
+ url: file[fieldName].url,
96
+ variants: []
92
97
  });
93
98
  }
94
99
  return widgetItems;
@@ -1,9 +1,10 @@
1
- import { File } from "./AbstractMediaManager";
2
- import { populateVariants, randomFileName } from "./helpers/MediaManagerHelper";
1
+ import { File } from "./AbstractMediaManager.js";
2
+ import { populateVariants, randomFileName } from "./helpers/MediaManagerHelper.js";
3
3
  import sizeOf from "image-size";
4
4
  import sharp from "sharp";
5
5
  import * as fs from 'fs';
6
6
  import * as path from 'path';
7
+ import * as process from "node:process";
7
8
  export class ImageItem extends File {
8
9
  type = "image";
9
10
  model = "mediamanagerap";
@@ -55,7 +56,7 @@ export class ImageItem extends File {
55
56
  parent: null,
56
57
  mimeType: file.mimetype,
57
58
  size: file.size,
58
- path: file.path,
59
+ path: `${this.fileStoragePath}/${this.urlPathPrefix}/${filename}`,
59
60
  group: group,
60
61
  tag: "origin",
61
62
  filename: origFileName,
@@ -64,7 +65,7 @@ export class ImageItem extends File {
64
65
  await this.createMeta(parent.id);
65
66
  await this.addFileMeta(file.path, parent.id);
66
67
  // create file variants
67
- if (Object.keys(this.imageSizes).length) {
68
+ if (Object.keys(this.imageSizes).length && file.mimetype !== 'image/svg+xml') {
68
69
  await this.createVariants(file, parent, filename, group);
69
70
  }
70
71
  // TODO refactor CRUD functions for DataAccessor usage
@@ -148,7 +149,7 @@ export class ImageItem extends File {
148
149
  }
149
150
  async resizeImage(input, output, width, height) {
150
151
  // Get the directory from the output path
151
- const outputDir = path.dirname(output);
152
+ const outputDir = `${process.cwd()}/${path.dirname(output)}`;
152
153
  // Check if the directory exists, and create it if it doesn't
153
154
  if (!fs.existsSync(outputDir)) {
154
155
  fs.mkdirSync(outputDir, { recursive: true });
@@ -209,7 +210,7 @@ export class TextItem extends ImageItem {
209
210
  parent: null,
210
211
  mimeType: file.mimetype,
211
212
  size: file.size,
212
- path: file.path,
213
+ path: `${this.fileStoragePath}/${filename}`,
213
214
  group: group,
214
215
  filename: origFileName,
215
216
  tag: "origin",
@@ -1,6 +1,6 @@
1
1
  import { AbstractMediaManager } from "./AbstractMediaManager";
2
2
  export declare class MediaManagerHandler {
3
- private static managers;
4
- static add(manager: AbstractMediaManager): void;
5
- static get(id: string): AbstractMediaManager;
3
+ private managers;
4
+ add(manager: AbstractMediaManager): void;
5
+ get(id: string): AbstractMediaManager;
6
6
  }
@@ -1,10 +1,10 @@
1
- import { Adminizer } from "../Adminizer";
1
+ import { Adminizer } from "../Adminizer.js";
2
2
  export class MediaManagerHandler {
3
- static managers = [];
4
- static add(manager) {
3
+ managers = [];
4
+ add(manager) {
5
5
  this.managers.push(manager);
6
6
  }
7
- static get(id) {
7
+ get(id) {
8
8
  let instance = this.managers.find(e => e.id === id);
9
9
  /**
10
10
  * ✨ We magically get one
@@ -1,3 +1,4 @@
1
+ import { Adminizer } from "../Adminizer";
1
2
  export declare class MediaManagerThumb {
2
- static getThumb(id: string, managerId: string): Promise<Buffer<ArrayBufferLike>>;
3
+ static getThumb(id: string, managerId: string, adminizer: Adminizer): Promise<Buffer<ArrayBufferLike>>;
3
4
  }
@@ -1,10 +1,9 @@
1
1
  import sharp from "sharp";
2
2
  import { promises as fs } from 'fs';
3
- import { MediaManagerHandler } from "./MediaManagerHandler";
4
3
  export class MediaManagerThumb {
5
- static async getThumb(id, managerId) {
4
+ static async getThumb(id, managerId, adminizer) {
6
5
  const fileExists = async (path) => !!(await fs.stat(path).catch(e => false));
7
- const manager = MediaManagerHandler.get(managerId);
6
+ const manager = adminizer.mediaManagerHandler.get(managerId);
8
7
  const path = await manager.getOrigin(id);
9
8
  const baseThumbPath = `${process.cwd()}/.tmp/thumbs`;
10
9
  await fs.mkdir(baseThumbPath, { recursive: true });
@@ -13,7 +12,7 @@ export class MediaManagerThumb {
13
12
  return await fs.readFile(thumb);
14
13
  }
15
14
  else {
16
- await sharp(path)
15
+ await sharp(`${process.cwd()}/${path}`)
17
16
  .resize({ width: 150, height: 150 })
18
17
  .toFile(thumb);
19
18
  return await fs.readFile(thumb);
@@ -11,19 +11,24 @@ type PostParams = Record<string, string | number | boolean | object | string[] |
11
11
  export declare function randomFileName(filenameOrig: string, type: string, prefix: boolean): string;
12
12
  /**
13
13
  * Save media manager relations to database.
14
+ * @param adminizer
14
15
  * @param fields
15
16
  * @param reqData
16
17
  * @param model
17
18
  * @param recordId
18
19
  */
19
- export declare function saveRelationsMediaManager(fields: Fields, reqData: PostParams, model: string, recordId: string): Promise<void>;
20
+ export declare function saveRelationsMediaManager(adminizer: Adminizer, fields: Fields, reqData: PostParams, model: string, recordId: number): Promise<void>;
20
21
  /**
21
22
  * Get realtions
23
+ * @param adminizer
22
24
  * @param data
25
+ * @param model
26
+ * @param widgetName
23
27
  */
24
- export declare function getRelationsMediaManager(data: MediaManagerWidgetJSON): Promise<import("../AbstractMediaManager").MediaManagerWidgetClientItem[]>;
28
+ export declare function getRelationsMediaManager(adminizer: Adminizer, data: MediaManagerWidgetJSON): Promise<import("../AbstractMediaManager").MediaManagerWidgetClientItem[]>;
25
29
  /**
26
30
  * Delate Ralations
31
+ * @param adminizer
27
32
  * @param model
28
33
  * @param record
29
34
  */
@@ -1,4 +1,3 @@
1
- import { MediaManagerHandler } from "../MediaManagerHandler";
2
1
  /**
3
2
  * Create a random file name with prefix and type. If prefix is true, the file name will be prefixed with a random string.
4
3
  * @param filenameOrig
@@ -13,32 +12,37 @@ export function randomFileName(filenameOrig, type, prefix) {
13
12
  }
14
13
  /**
15
14
  * Save media manager relations to database.
15
+ * @param adminizer
16
16
  * @param fields
17
17
  * @param reqData
18
18
  * @param model
19
19
  * @param recordId
20
20
  */
21
- export async function saveRelationsMediaManager(fields, reqData, model, recordId) {
21
+ export async function saveRelationsMediaManager(adminizer, fields, reqData, model, recordId) {
22
22
  for (let prop in reqData) {
23
23
  let fieldConfigConfig = fields[prop].config;
24
24
  let options = fieldConfigConfig.options;
25
25
  if (fieldConfigConfig.type === 'mediamanager') {
26
26
  let data = reqData[prop];
27
- let mediaManager = MediaManagerHandler.get(options?.id ?? 'default');
27
+ let mediaManager = adminizer.mediaManagerHandler.get(options?.id ?? 'default');
28
28
  await mediaManager.setRelations(data, model, recordId, prop);
29
29
  }
30
30
  }
31
31
  }
32
32
  /**
33
33
  * Get realtions
34
+ * @param adminizer
34
35
  * @param data
36
+ * @param model
37
+ * @param widgetName
35
38
  */
36
- export async function getRelationsMediaManager(data) {
37
- let mediaManager = MediaManagerHandler.get(data.mediaManagerId);
38
- return await mediaManager.getItemsList(data.list ?? []);
39
+ export async function getRelationsMediaManager(adminizer, data) {
40
+ let mediaManager = adminizer.mediaManagerHandler.get(data.mediaManagerId);
41
+ return await mediaManager.getRelations(data.model, data.widgetName, data.modelId);
39
42
  }
40
43
  /**
41
44
  * Delate Ralations
45
+ * @param adminizer
42
46
  * @param model
43
47
  * @param record
44
48
  */
@@ -48,9 +52,9 @@ export async function deleteRelationsMediaManager(adminizer, model, record) {
48
52
  let field = config.fields[key];
49
53
  if (field && field.type === 'mediamanager') {
50
54
  const option = field.options;
51
- let mediaManager = MediaManagerHandler.get(option?.id ?? 'default');
55
+ let mediaManager = adminizer.mediaManagerHandler.get(option?.id ?? 'default');
52
56
  let emptyData = [];
53
- await mediaManager.setRelations(emptyData, model, record[0].id, key);
57
+ await mediaManager.setRelations(emptyData, model, +record[0].id, key);
54
58
  }
55
59
  }
56
60
  }
@@ -1,4 +1,4 @@
1
- import { formatChanges, sanitizeForDiff } from "../../helpers/diffHelpers";
1
+ import { formatChanges, sanitizeForDiff } from "../../helpers/diffHelpers.js";
2
2
  import { diff } from "deep-object-diff";
3
3
  export class AbstractModel {
4
4
  modelname;
@@ -1,4 +1,4 @@
1
- import { Adminizer } from "../Adminizer";
1
+ import { Adminizer } from "../Adminizer.js";
2
2
  export class ModelHandler {
3
3
  models = new Map();
4
4
  add(modelName, modelInstance) {
@@ -1,5 +1,5 @@
1
1
  import { DataTypes, Op } from "sequelize";
2
- import { AbstractAdapter, AbstractModel } from "../AbstractModel";
2
+ import { AbstractAdapter, AbstractModel } from "../AbstractModel.js";
3
3
  import path from "path";
4
4
  import fs from "fs";
5
5
  import { pathToFileURL } from "url";
@@ -1,4 +1,4 @@
1
- import { AbstractAdapter, AbstractModel } from "../AbstractModel";
1
+ import { AbstractAdapter, AbstractModel } from "../AbstractModel.js";
2
2
  import { v4 as uuid } from "uuid";
3
3
  import Waterline from "waterline";
4
4
  import path from "node:path";
@@ -33,6 +33,12 @@ export class WaterlineModel extends AbstractModel {
33
33
  return await query;
34
34
  }
35
35
  async _find(criteria = {}, options = {}) {
36
+ // Hack
37
+ const attributes = this.model.attributes;
38
+ const associations = Object.keys(attributes).filter(attr => attributes[attr].model || attributes[attr].collection);
39
+ //@ts-ignore
40
+ if (associations.includes(criteria?.sort?.split(' ')[0]) && JSON.stringify(criteria.where) === '{}')
41
+ criteria.sort = undefined;
36
42
  let query = this.model.find(criteria);
37
43
  if (options.populate && Array.isArray(options.populate) && options.populate.length > 0) {
38
44
  options.populate.forEach(([field, populateCriteria]) => {
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
- import { Adminizer } from '../Adminizer';
2
+ import { Adminizer } from '../Adminizer.js';
3
3
  /**
4
4
  * ░█████╗░██████╗░░██████╗████████╗██████╗░░█████╗░░█████╗░████████╗
5
5
  * ██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝
@@ -1,5 +1,5 @@
1
- import { AbstractNotificationService } from './AbstractNotificationService';
2
- import { Adminizer } from '../Adminizer';
1
+ import { AbstractNotificationService } from './AbstractNotificationService.js';
2
+ import { Adminizer } from '../Adminizer.js';
3
3
  export class GeneralNotificationService extends AbstractNotificationService {
4
4
  notificationClass = 'general';
5
5
  icon = 'info';
@@ -1,5 +1,5 @@
1
1
  import { EventEmitter } from 'events';
2
- import { Adminizer } from '../Adminizer';
2
+ import { Adminizer } from '../Adminizer.js';
3
3
  export class NotificationHandler extends EventEmitter {
4
4
  services = new Map();
5
5
  constructor() {
@@ -1,5 +1,5 @@
1
- import { AbstractNotificationService } from './AbstractNotificationService';
2
- import { Adminizer } from '../Adminizer';
1
+ import { AbstractNotificationService } from './AbstractNotificationService.js';
2
+ import { Adminizer } from '../Adminizer.js';
3
3
  export class SystemNotificationService extends AbstractNotificationService {
4
4
  notificationClass = 'system';
5
5
  icon = 'settings';
@@ -1,5 +1,5 @@
1
- import { isObject } from "../helpers/JsUtils";
2
- import { Adminizer } from "./Adminizer";
1
+ import { isObject } from "../helpers/JsUtils.js";
2
+ import { Adminizer } from "./Adminizer.js";
3
3
  /**
4
4
  * Default helper that will contain all methods
5
5
  * that should help with processing request details and bind
@@ -1,4 +1,4 @@
1
- import { BaseWidget } from "./abstractWidgetBase";
1
+ import { BaseWidget } from "./abstractWidgetBase.js";
2
2
  export class ActionBase extends BaseWidget {
3
3
  widgetType = "action";
4
4
  }
@@ -1,4 +1,4 @@
1
- import { BaseWidget } from "./abstractWidgetBase";
1
+ import { BaseWidget } from "./abstractWidgetBase.js";
2
2
  export class InfoBase extends BaseWidget {
3
3
  widgetType = "info";
4
4
  /** Widget background css color */
@@ -1,4 +1,4 @@
1
- import { BaseWidget } from "./abstractWidgetBase";
1
+ import { BaseWidget } from "./abstractWidgetBase.js";
2
2
  export class LinkBase extends BaseWidget {
3
3
  widgetType = "link";
4
4
  }
@@ -1,4 +1,4 @@
1
- import { BaseWidget } from "./abstractWidgetBase";
1
+ import { BaseWidget } from "./abstractWidgetBase.js";
2
2
  export class SwitchBase extends BaseWidget {
3
3
  widgetType = "switcher";
4
4
  }
@@ -1,4 +1,4 @@
1
- import { umzugExports } from "./umzugExports";
1
+ import { umzugExports } from "./umzugExports.js";
2
2
  export const migrations = {
3
3
  umzug: umzugExports
4
4
  };
@@ -1,4 +1,4 @@
1
- import { up as up0001, down as down0001 } from "./umzug/0001";
1
+ import { up as up0001, down as down0001 } from "./umzug/0001.js";
2
2
  export const umzugExports = [
3
3
  {
4
4
  name: "0001",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "adminizer",
3
3
  "type": "module",
4
- "version": "4.3.0-build.99",
4
+ "version": "4.3.1",
5
5
  "main": "index.js",
6
6
  "exports": {
7
7
  ".": "./index.js",
@@ -16,8 +16,9 @@
16
16
  "dev:no-seed-clean": "cross-env VITE_ENV=dev NO_SEED_DATA=true CLEAN_TMP=true ORM=sequelize tsx watch --tsconfig ./fixture/tsconfig.json ./fixture/index.ts",
17
17
  "dev:waterline": "cross-env VITE_ENV=dev ORM=waterline tsx watch --tsconfig ./fixture/tsconfig.json ./fixture/index.ts",
18
18
  "dev:waterline:no-seed": "cross-env VITE_ENV=dev NO_SEED_DATA=true ORM=waterline tsx watch --tsconfig ./fixture/tsconfig.json ./fixture/index.ts",
19
+ "dev:cors": "cross-env VITE_ENV=dev NO_SEED_DATA=true ORM=waterline FRONTEND_URL=http://localhost:8080 tsx watch --tsconfig ./fixture/tsconfig.json ./fixture/index.ts",
19
20
  "build:assets": "vite build",
20
- "compile:backend": "tsc -p src/tsconfig.json",
21
+ "compile:backend": "tsc -p src/tsconfig.json && node ./scripts/appendJsExtension.js",
21
22
  "compile:ui": "tsc -p tsconfig.ui.json",
22
23
  "watch:backend": "tsc -w -p src/tsconfig.json",
23
24
  "copy:backend": "node copy-backend.js",
@@ -72,6 +73,7 @@
72
73
  "typeorm": "^0.3.20",
73
74
  "uuid": "^11.0.5",
74
75
  "waterline": "^0.15.2",
76
+ "sequelize": "^6.37.7",
75
77
  "winston": "^3.17.0"
76
78
  },
77
79
  "devDependencies": {
package/system/Router.js CHANGED
@@ -1,24 +1,24 @@
1
- import _dashboard from "../controllers/dashboard";
2
- import _welcome from "../controllers/welcome";
3
- import _list from "../controllers/list";
4
- import _edit from "../controllers/edit";
5
- import _add from "../controllers/add";
6
- import _view from "../controllers/view";
7
- import _remove from "../controllers/remove";
8
- import { ckEditorUpload } from "../controllers/ckeditorUpload";
9
- import _form from "../controllers/form";
10
- import { widgetSwitchController } from "../controllers/widgets/switch";
11
- import _getAllWidgets from "../controllers/getAllWidgets";
12
- import _widgetsDB from "../controllers/widgetsDB";
13
- import { widgetInfoController } from '../controllers/widgets/Info';
14
- import { widgetActionController } from '../controllers/widgets/Action';
15
- import { widgetCustomController } from "../controllers/widgets/Custom";
16
- import { catalogController } from "../controllers/catalog/Catalog";
17
- import { mediaManagerController } from "../controllers/media-manager/mediaManagerApi";
18
- import { thumbController } from "../controllers/media-manager/ThumbController";
19
- import { Adminizer } from "../lib/Adminizer";
20
- import timezones from "../controllers/timezones";
21
- import { NotificationController } from "../controllers/notifications/NotificationController";
1
+ import _dashboard from "../controllers/dashboard.js";
2
+ import _welcome from "../controllers/welcome.js";
3
+ import _list from "../controllers/list.js";
4
+ import _edit from "../controllers/edit.js";
5
+ import _add from "../controllers/add.js";
6
+ import _view from "../controllers/view.js";
7
+ import _remove from "../controllers/remove.js";
8
+ import { ckEditorUpload } from "../controllers/ckeditorUpload.js";
9
+ import _form from "../controllers/form.js";
10
+ import { widgetSwitchController } from "../controllers/widgets/switch.js";
11
+ import _getAllWidgets from "../controllers/getAllWidgets.js";
12
+ import _widgetsDB from "../controllers/widgetsDB.js";
13
+ import { widgetInfoController } from '../controllers/widgets/Info.js';
14
+ import { widgetActionController } from '../controllers/widgets/Action.js';
15
+ import { widgetCustomController } from "../controllers/widgets/Custom.js";
16
+ import { catalogController } from "../controllers/catalog/Catalog.js";
17
+ import { mediaManagerController } from "../controllers/media-manager/mediaManagerApi.js";
18
+ import { thumbController } from "../controllers/media-manager/ThumbController.js";
19
+ import { Adminizer } from "../lib/Adminizer.js";
20
+ import timezones from "../controllers/timezones.js";
21
+ import { NotificationController } from "../controllers/notifications/NotificationController.js";
22
22
  export default class Router {
23
23
  static onlyOnce = false;
24
24
  /**
@@ -67,7 +67,9 @@ export default class Router {
67
67
  * Edit form
68
68
  * */
69
69
  adminizer.app.all(`${adminizer.config.routePrefix}/form/:slug`, adminizer.policyManager.bindPolicies(policies, _form));
70
- // Create a base entity route
70
+ /**
71
+ * Create a base entity route
72
+ */
71
73
  let baseRoute = `${adminizer.config.routePrefix}/:entityType(form|model)/:entityName`;
72
74
  /**
73
75
  * Catalog
@@ -91,13 +93,13 @@ export default class Router {
91
93
  if (adminizer.config.notifications.enabled) {
92
94
  adminizer.app.get(`${adminizer.config.routePrefix}/notifications`, adminizer.policyManager.bindPolicies(policies, NotificationController.viewAll));
93
95
  adminizer.app.post(`${adminizer.config.routePrefix}/notifications`, adminizer.policyManager.bindPolicies(policies, NotificationController.viewAll));
94
- adminizer.app.get(`${adminizer.config.routePrefix}/api/notifications/stream`, adminizer.policyManager.bindPolicies(policies, NotificationController.getNotificationsStream));
95
- adminizer.app.get(`${adminizer.config.routePrefix}/api/notifications/get-classes`, adminizer.policyManager.bindPolicies(policies, NotificationController.getNotificationClasses));
96
- adminizer.app.get(`${adminizer.config.routePrefix}/api/notifications/:notificationClass`, adminizer.policyManager.bindPolicies(policies, NotificationController.getNotificationsByClass));
97
- adminizer.app.get(`${adminizer.config.routePrefix}/api/notifications`, adminizer.policyManager.bindPolicies(policies, NotificationController.getUserNotifications));
98
- adminizer.app.put(`${adminizer.config.routePrefix}/api/notifications/:notificationClass/:id/read`, adminizer.policyManager.bindPolicies(policies, NotificationController.markAsRead));
99
- adminizer.app.put(`${adminizer.config.routePrefix}/api/notifications/read-all`, adminizer.policyManager.bindPolicies(policies, NotificationController.markAllAsRead));
100
- adminizer.app.post(`${adminizer.config.routePrefix}/api/notifications/search`, adminizer.policyManager.bindPolicies(policies, NotificationController.search));
96
+ adminizer.app.get(`${adminizer.config.routePrefix}/notifications/api/stream`, adminizer.policyManager.bindPolicies(policies, NotificationController.getNotificationsStream));
97
+ adminizer.app.get(`${adminizer.config.routePrefix}/notifications/api/get-classes`, adminizer.policyManager.bindPolicies(policies, NotificationController.getNotificationClasses));
98
+ adminizer.app.get(`${adminizer.config.routePrefix}/notifications/api/:notificationClass`, adminizer.policyManager.bindPolicies(policies, NotificationController.getNotificationsByClass));
99
+ adminizer.app.get(`${adminizer.config.routePrefix}/notifications/api`, adminizer.policyManager.bindPolicies(policies, NotificationController.getUserNotifications));
100
+ adminizer.app.put(`${adminizer.config.routePrefix}/notifications/api/:notificationClass/:id/read`, adminizer.policyManager.bindPolicies(policies, NotificationController.markAsRead));
101
+ adminizer.app.put(`${adminizer.config.routePrefix}/notifications/api/read-all`, adminizer.policyManager.bindPolicies(policies, NotificationController.markAllAsRead));
102
+ adminizer.app.post(`${adminizer.config.routePrefix}/notifications/api/search`, adminizer.policyManager.bindPolicies(policies, NotificationController.search));
101
103
  }
102
104
  /**
103
105
  * List of records
@@ -60,6 +60,13 @@ export default async function bindAccessRights(adminizer) {
60
60
  id: `widgets`, name: "Widgets",
61
61
  description: "Access to widgets", department: "Widgets"
62
62
  });
63
+ // Allow access login
64
+ adminizer.accessRightsHelper.registerToken({
65
+ id: 'access-to-adminpanel',
66
+ name: "Allowed log in",
67
+ description: "Are users allowed to log in to the admin panel?",
68
+ department: "Admin panel"
69
+ });
63
70
  // Default user group
64
71
  if (adminizer.config.registration && adminizer.config.registration.enable) {
65
72
  // TODO refactor CRUD functions for DataAccessor usage
@@ -1,7 +1,7 @@
1
- import _login from "../controllers/login";
2
- import _register from "../controllers/register";
3
- import _initUser from "../controllers/initUser";
4
- import { Adminizer } from "../lib/Adminizer";
1
+ import _login from "../controllers/login.js";
2
+ import _register from "../controllers/register.js";
3
+ import _initUser from "../controllers/initUser.js";
4
+ import { Adminizer } from "../lib/Adminizer.js";
5
5
  import { generate } from "password-hash";
6
6
  export default async function bindAuthorization(adminizer) {
7
7
  let admins;
@@ -1,9 +1,9 @@
1
- import { CKeditor } from "../lib/controls/wysiwyg/CKeditor";
2
- import { ToastUiEditor } from "../lib/controls/markdown/ToastUiEditor";
3
- import { Handsontable } from "../lib/controls/table/Handsontable";
4
- import { JsonEditor } from "../lib/controls/jsoneditor/JsonEditor";
5
- import { MonacoEditor } from "../lib/controls/codeEditor/MonacoEditor";
6
- import { GeoEditor } from "../lib/controls/geojsoneditor/GeoEditor";
1
+ import { CKeditor } from "../lib/controls/wysiwyg/CKeditor.js";
2
+ import { ToastUiEditor } from "../lib/controls/markdown/ToastUiEditor.js";
3
+ import { Handsontable } from "../lib/controls/table/Handsontable.js";
4
+ import { JsonEditor } from "../lib/controls/jsoneditor/JsonEditor.js";
5
+ import { MonacoEditor } from "../lib/controls/codeEditor/MonacoEditor.js";
6
+ import { GeoEditor } from "../lib/controls/geojsoneditor/GeoEditor.js";
7
7
  export function bindControls(adminizer) {
8
8
  // bind wysiwyg
9
9
  adminizer.controlsHandler.add(new CKeditor(adminizer));
@@ -0,0 +1,2 @@
1
+ import { Adminizer } from "../lib/Adminizer";
2
+ export declare function bindCors(adminizer: Adminizer): void;
@@ -0,0 +1,41 @@
1
+ export function bindCors(adminizer) {
2
+ if (adminizer.config?.cors?.enabled) {
3
+ const corsConfig = adminizer.config.cors;
4
+ // Поддерживаем массив разрешенных origin
5
+ const allowedOrigins = Array.isArray(corsConfig.origin)
6
+ ? corsConfig.origin
7
+ : [corsConfig.origin];
8
+ adminizer.app.all(`${adminizer.config.routePrefix}/${corsConfig.path}`, (req, res, next) => {
9
+ const requestOrigin = req.headers.origin;
10
+ // Проверяем разрешен ли origin
11
+ const isOriginAllowed = !requestOrigin || allowedOrigins.includes(requestOrigin);
12
+ if (requestOrigin && !isOriginAllowed) {
13
+ console.log(`❌ CORS: Blocked request from ${requestOrigin}`);
14
+ if (req.method === 'OPTIONS') {
15
+ // Для preflight - 200 без CORS headers
16
+ return res.status(200).end();
17
+ }
18
+ else {
19
+ // Для основных запросов - ошибка
20
+ return res.status(403).json({
21
+ error: 'CORS policy: Origin not allowed'
22
+ });
23
+ }
24
+ }
25
+ // Запрос с разрешенного origin или без Origin
26
+ if (isOriginAllowed) {
27
+ // Для CORS запросов возвращаем тот же origin (или первый из списка)
28
+ const allowOrigin = requestOrigin || allowedOrigins[0];
29
+ res.header('Access-Control-Allow-Origin', allowOrigin);
30
+ res.header('Access-Control-Allow-Credentials', corsConfig.credentials !== false ? 'true' : 'false');
31
+ res.header('Access-Control-Allow-Methods', corsConfig.methods?.join(',') || 'GET,POST,PUT,DELETE,OPTIONS');
32
+ res.header('Access-Control-Allow-Headers', corsConfig.allowedHeaders?.join(',') || 'Content-Type,Authorization,X-Requested-With,X-CSRF-Token,x-xsrf-token');
33
+ }
34
+ if (req.method === 'OPTIONS') {
35
+ return res.status(200).end();
36
+ }
37
+ next();
38
+ });
39
+ console.log('✅ API CORS middleware enabled. Allowed origins:', allowedOrigins);
40
+ }
41
+ }
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import { Adminizer } from '../lib/Adminizer';
3
+ import { Adminizer } from '../lib/Adminizer.js';
4
4
  export default async function bindDashboardWidgets(adminizer) {
5
5
  if (adminizer.config.dashboard && typeof adminizer.config.dashboard !== "boolean" && adminizer.config.dashboard.autoloadWidgetsPath) {
6
6
  try {
@@ -1,5 +1,5 @@
1
1
  import * as fs from "fs";
2
- import { FormHelper } from "../helpers/formHelper";
2
+ import { FormHelper } from "../helpers/formHelper.js";
3
3
  export default async function bindForms(adminizer) {
4
4
  if (fs.existsSync(adminizer.config.forms.path)) {
5
5
  let formsDir = fs.readdirSync(adminizer.config.forms.path);
@@ -1,8 +1,8 @@
1
- import flash from "../lib/inertia/flash";
2
- import inertia from "../lib/inertia/inertiaAdapter";
1
+ import flash from "../lib/inertia/flash.js";
2
+ import inertia from "../lib/inertia/inertiaAdapter.js";
3
3
  import fs from "fs";
4
4
  import path from "node:path";
5
- import { InertiaMenuHelper } from "../helpers/inertiaMenuHelper";
5
+ import { InertiaMenuHelper } from "../helpers/inertiaMenuHelper.js";
6
6
  export function bindInertia(adminizer) {
7
7
  const viteRender = () => {
8
8
  if (process.env.VITE_ENV === 'dev') {
@@ -102,7 +102,7 @@ export function bindInertia(adminizer) {
102
102
  enabled: true,
103
103
  cookieName: 'XSRF-TOKEN',
104
104
  headerName: 'x-xsrf-token'
105
- },
105
+ }
106
106
  }));
107
107
  adminizer.app.use((req, _, next) => {
108
108
  checkAuth(req, adminizer);