@openinc/parse-server-opendash 4.0.34 → 4.0.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,3 +30,4 @@ For a full list of options see the file at [CONFIG.md](https://github.com/open-i
30
30
  ## Development
31
31
 
32
32
  See the file at [DEVELOPMENT.md](https://github.com/open-inc/node-parse-server-opendash/blob/master/DEVELOPMENT.md)
33
+
@@ -25,4 +25,7 @@ export declare namespace Permissions {
25
25
  enum DOCUMENTATION {
26
26
  see_app = "documentation:can-see-app"
27
27
  }
28
+ enum BDE {
29
+ timetracking_can_edit_times = "bde:timetracking-edit-times"
30
+ }
28
31
  }
@@ -39,6 +39,10 @@ var Permissions;
39
39
  // Access
40
40
  DOCUMENTATION["see_app"] = "documentation:can-see-app";
41
41
  })(DOCUMENTATION = Permissions.DOCUMENTATION || (Permissions.DOCUMENTATION = {}));
42
+ let BDE;
43
+ (function (BDE) {
44
+ BDE["timetracking_can_edit_times"] = "bde:timetracking-edit-times";
45
+ })(BDE = Permissions.BDE || (Permissions.BDE = {}));
42
46
  // can also be used for nested permissions
43
47
  // Example:
44
48
  // export namespace TestPlugin {
@@ -12,13 +12,7 @@ async function init(name) {
12
12
  const userId = request.user?.id;
13
13
  let roles = [];
14
14
  if (request.user) {
15
- // TODO, search roles recursivly
16
- const roleObjects = await new node_1.default.Query(node_1.default.Role)
17
- .equalTo("users", request.user)
18
- .find({ useMasterKey: true });
19
- roles = roleObjects
20
- .map((role) => role.getName())
21
- .filter(Boolean);
15
+ roles = await getUserRoles(request.user);
22
16
  }
23
17
  const parseSchema = await node_1.default.Schema.all();
24
18
  const schema = parseSchema
@@ -50,6 +44,27 @@ async function init(name) {
50
44
  };
51
45
  });
52
46
  }
47
+ // Returns all role names the user belongs to, including transitive parent roles.
48
+ // In Parse, roles can contain other roles via the "roles" relation, so a user in
49
+ // role "staff" also inherits permissions from "admin" if "staff" is a child of "admin".
50
+ async function getUserRoles(user) {
51
+ // visited prevents infinite loops in case of cyclic role definitions
52
+ const visited = new Set();
53
+ async function collect(roleQuery) {
54
+ const found = await roleQuery.find({ useMasterKey: true });
55
+ for (const role of found) {
56
+ const name = role.getName();
57
+ if (name && !visited.has(name)) {
58
+ visited.add(name);
59
+ // Find parent roles that contain this role as a child and continue upward
60
+ await collect(new node_1.default.Query(node_1.default.Role).equalTo("roles", role));
61
+ }
62
+ }
63
+ }
64
+ // Entry point: direct roles of the user
65
+ await collect(new node_1.default.Query(node_1.default.Role).equalTo("users", user));
66
+ return [...visited];
67
+ }
53
68
  async function getTenants() {
54
69
  try {
55
70
  const tenants = await new node_1.default.Query(index_js_2.Tenant)
@@ -8,7 +8,11 @@ async function init() {
8
8
  (0, index_js_2.beforeSaveHook)(index_js_3.Alarm, async (request) => {
9
9
  const { object, original, user } = request;
10
10
  await (0, index_js_2.defaultHandler)(request);
11
- // TODO
11
+ //Do not allow tenant users to read alarms of other users by default, but allow custom ACLs
12
+ await (0, index_js_2.defaultAclHandler)(request, {
13
+ allowCustomACL: true,
14
+ denyTenantUserRead: true,
15
+ });
12
16
  });
13
17
  (0, index_js_2.afterSaveHook)(index_js_3.Alarm, async ({ object, original, user }) => {
14
18
  if (index_js_1.ConfigInstance.getInstance().getBoolean("OPENWARE")) {
@@ -7,7 +7,12 @@ async function init() {
7
7
  (0, index_js_1.beforeSaveHook)(index_js_2.BDE_Article, async (request) => {
8
8
  const { object, original, user } = request;
9
9
  await (0, index_js_1.defaultHandler)(request);
10
- await (0, index_js_1.defaultAclHandler)(request);
10
+ // Ported from the former MES_Article hook: tenant users may manage the
11
+ // shared article catalog, and custom ACLs are honoured.
12
+ await (0, index_js_1.defaultAclHandler)(request, {
13
+ allowCustomACL: true,
14
+ allowTenantUserWrite: true,
15
+ });
11
16
  // TODO
12
17
  });
13
18
  (0, index_js_1.afterSaveHook)(index_js_2.BDE_Article, async (request) => {
@@ -12,6 +12,8 @@ async function init() {
12
12
  });
13
13
  (0, index_js_1.afterSaveHook)(index_js_2.BDE_WorkTime, async (request) => {
14
14
  const { object, original, user } = request;
15
+ if (object.get("status") === "disrupted")
16
+ return;
15
17
  await addToWorkOrder(object);
16
18
  });
17
19
  }
@@ -22,7 +24,7 @@ async function addToWorkOrder(workTime) {
22
24
  const workOrder = await workTime.workOrder?.fetch({ useMasterKey: true });
23
25
  if (!workOrder)
24
26
  return;
25
- workOrder.set("amountGood", (workOrder.amountGood || 0) + workTime.amountGood);
26
- workOrder.set("amountScrap", (workOrder.amountScrap || 0) + workTime.amountScrap);
27
+ workOrder.set("amountGood", (workOrder.amountGood || 0) + (workTime.amountGood || 0));
28
+ workOrder.set("amountScrap", (workOrder.amountScrap || 0) + (workTime.amountScrap || 0));
27
29
  await workOrder.save(null, { useMasterKey: true });
28
30
  }
@@ -97,8 +97,8 @@ async function init() {
97
97
  .get("article")
98
98
  ?.fetch({ useMasterKey: true });
99
99
  if (article) {
100
- object.set("articlenr", article.get("artnr"));
101
- object.set("articlename", article?.get("name"));
100
+ object.set("articlenr", article.get("articleNumber"));
101
+ object.set("articlename", article?.get("description"));
102
102
  object.set("targettime", object.targettime
103
103
  ? object.targettime
104
104
  : article?.get("targettime") || 0);
@@ -1,4 +1,5 @@
1
1
  import Parse from "parse/node";
2
+ import type { Tenant } from "./Tenant";
2
3
  import type { _User } from "./_User";
3
4
  export interface AlarmAttributes {
4
5
  id: string;
@@ -12,6 +13,7 @@ export interface AlarmAttributes {
12
13
  item_source?: string | undefined;
13
14
  name?: string | undefined;
14
15
  sent?: number | undefined;
16
+ tenant?: Tenant | undefined;
15
17
  trigger?: any | undefined;
16
18
  user: _User;
17
19
  }
@@ -32,6 +34,8 @@ export declare class Alarm extends Parse.Object<AlarmAttributes> {
32
34
  set name(value: string | undefined);
33
35
  get sent(): number | undefined;
34
36
  set sent(value: number | undefined);
37
+ get tenant(): Tenant | undefined;
38
+ set tenant(value: Tenant | undefined);
35
39
  get trigger(): any | undefined;
36
40
  set trigger(value: any | undefined);
37
41
  get user(): _User;
@@ -52,6 +52,12 @@ class Alarm extends node_1.default.Object {
52
52
  set sent(value) {
53
53
  super.set("sent", value);
54
54
  }
55
+ get tenant() {
56
+ return super.get("tenant");
57
+ }
58
+ set tenant(value) {
59
+ super.set("tenant", value);
60
+ }
55
61
  get trigger() {
56
62
  return super.get("trigger");
57
63
  }
@@ -8,6 +8,7 @@ export interface BDE_ArticleAttributes {
8
8
  articleNumber?: string | undefined;
9
9
  description?: string | undefined;
10
10
  isDeleted: boolean;
11
+ targettime?: number | undefined;
11
12
  tenant?: Tenant | undefined;
12
13
  unit?: string | undefined;
13
14
  }
@@ -20,6 +21,8 @@ export declare class BDE_Article extends Parse.Object<BDE_ArticleAttributes> {
20
21
  set description(value: string | undefined);
21
22
  get isDeleted(): boolean;
22
23
  set isDeleted(value: boolean);
24
+ get targettime(): number | undefined;
25
+ set targettime(value: number | undefined);
23
26
  get tenant(): Tenant | undefined;
24
27
  set tenant(value: Tenant | undefined);
25
28
  get unit(): string | undefined;
@@ -28,6 +28,12 @@ class BDE_Article extends node_1.default.Object {
28
28
  set isDeleted(value) {
29
29
  super.set("isDeleted", value);
30
30
  }
31
+ get targettime() {
32
+ return super.get("targettime");
33
+ }
34
+ set targettime(value) {
35
+ super.set("targettime", value);
36
+ }
31
37
  get tenant() {
32
38
  return super.get("tenant");
33
39
  }
@@ -1,5 +1,12 @@
1
1
  import Parse from "parse/node";
2
2
  import type { Tenant } from "./Tenant";
3
+ /**
4
+ * @deprecated Superseded by {@link BDE_Article} (OD3_BDE_Article). The MES
5
+ * module now stores articles as BDE_Article (artnr → articleNumber,
6
+ * name → description, targettime → targettime). This class is kept only so
7
+ * existing OD3_MES_Article records remain readable until they are migrated.
8
+ * Do not reference it for new records.
9
+ */
3
10
  export interface MES_ArticleAttributes {
4
11
  id: string;
5
12
  objectId: string;
@@ -1,5 +1,5 @@
1
1
  import Parse from "parse/node";
2
- import type { MES_Article } from "./MES_Article";
2
+ import type { BDE_Article } from "./BDE_Article";
3
3
  import type { Source } from "./Source";
4
4
  import type { Tenant } from "./Tenant";
5
5
  export interface MES_OrderAttributes {
@@ -7,7 +7,7 @@ export interface MES_OrderAttributes {
7
7
  objectId: string;
8
8
  createdAt: Date;
9
9
  updatedAt: Date;
10
- article?: MES_Article | undefined;
10
+ article?: BDE_Article | undefined;
11
11
  articlename?: string | undefined;
12
12
  articlenr?: string | undefined;
13
13
  duration: number;
@@ -24,8 +24,8 @@ export interface MES_OrderAttributes {
24
24
  export declare class MES_Order extends Parse.Object<MES_OrderAttributes> {
25
25
  static className: string;
26
26
  constructor(data?: Partial<MES_OrderAttributes>);
27
- get article(): MES_Article | undefined;
28
- set article(value: MES_Article | undefined);
27
+ get article(): BDE_Article | undefined;
28
+ set article(value: BDE_Article | undefined);
29
29
  get articlename(): string | undefined;
30
30
  set articlename(value: string | undefined);
31
31
  get articlenr(): string | undefined;
@@ -1,5 +1,5 @@
1
1
  import Parse from "parse/node";
2
- import type { MES_Article } from "./MES_Article";
2
+ import type { BDE_Article } from "./BDE_Article";
3
3
  import type { Source } from "./Source";
4
4
  import type { Tenant } from "./Tenant";
5
5
  export interface MES_OrderPlanAttributes {
@@ -7,7 +7,7 @@ export interface MES_OrderPlanAttributes {
7
7
  objectId: string;
8
8
  createdAt: Date;
9
9
  updatedAt: Date;
10
- article?: MES_Article | undefined;
10
+ article?: BDE_Article | undefined;
11
11
  ordernr: string;
12
12
  planEnd?: Date | undefined;
13
13
  planStart?: Date | undefined;
@@ -19,8 +19,8 @@ export interface MES_OrderPlanAttributes {
19
19
  export declare class MES_OrderPlan extends Parse.Object<MES_OrderPlanAttributes> {
20
20
  static className: string;
21
21
  constructor(data?: Partial<MES_OrderPlanAttributes>);
22
- get article(): MES_Article | undefined;
23
- set article(value: MES_Article | undefined);
22
+ get article(): BDE_Article | undefined;
23
+ set article(value: BDE_Article | undefined);
24
24
  get ordernr(): string;
25
25
  set ordernr(value: string);
26
26
  get planEnd(): Date | undefined;
@@ -1,6 +1,6 @@
1
1
  import Parse from "parse/node";
2
- import type { Tenant } from "./Tenant";
3
2
  import { MetaConfig } from "./custom/MetaField";
3
+ import type { Tenant } from "./Tenant";
4
4
  export interface Meta_ConfigAttributes {
5
5
  id: string;
6
6
  objectId: string;
@@ -1,7 +1,7 @@
1
1
  import Parse from "parse/node";
2
+ import { MetaConfig } from "./custom/MetaField";
2
3
  import type { Meta_Config } from "./Meta_Config";
3
4
  import type { Tenant } from "./Tenant";
4
- import { MetaConfig } from "./custom/MetaField";
5
5
  export interface Meta_EntryAttributes {
6
6
  id: string;
7
7
  objectId: string;
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@openinc/parse-server-opendash",
3
- "version": "4.0.34",
3
+ "version": "4.0.39",
4
4
  "description": "Parse Server Cloud Code for open.INC Stack.",
5
- "packageManager": "pnpm@10.33.0",
5
+ "packageManager": "pnpm@11.9.0",
6
6
  "keywords": [
7
7
  "parse",
8
8
  "opendash"
@@ -69,17 +69,17 @@
69
69
  "@openinc/parse-server-schema": "^4.1.1",
70
70
  "amqplib": "^0.10.9",
71
71
  "cron": "^4.4.0",
72
- "dayjs": "^1.11.20",
72
+ "dayjs": "^1.11.21",
73
73
  "dotenv": "^17.4.2",
74
74
  "fast-equals": "^6.0.0",
75
- "i18next": "^26.2.0",
76
- "i18next-fs-backend": "^2.6.5",
75
+ "i18next": "^26.3.1",
76
+ "i18next-fs-backend": "^2.6.6",
77
77
  "jsonwebtoken": "^9.0.3",
78
- "jwks-rsa": "^4.0.1",
79
- "nodemailer": "^8.0.7",
78
+ "jwks-rsa": "^4.1.0",
79
+ "nodemailer": "^9.0.1",
80
80
  "nunjucks": "^3.2.4",
81
81
  "parse": "^8.6.0",
82
- "parse-server": "^9.9.0",
82
+ "parse-server": "9.9.1-alpha.13",
83
83
  "pdf-img-convert": "2.0.0",
84
84
  "rimraf": "^6.1.3",
85
85
  "table": "^6.9.0",
@@ -88,22 +88,15 @@
88
88
  },
89
89
  "devDependencies": {
90
90
  "@types/jsonwebtoken": "^9.0.10",
91
- "@types/node": "^25.9.0",
92
- "@types/nodemailer": "^7.0.11",
91
+ "@types/node": "^25.9.4",
92
+ "@types/nodemailer": "^8.0.1",
93
93
  "@types/nunjucks": "^3.2.6",
94
94
  "@types/safe-timers": "^1.1.2",
95
95
  "@types/web-push": "^3.6.4",
96
- "concurrently": "^9.2.1",
96
+ "concurrently": "^9.2.3",
97
97
  "ts-node": "^10.9.2",
98
98
  "typedoc": "^0.28.19",
99
- "typedoc-plugin-markdown": "^4.11.0",
99
+ "typedoc-plugin-markdown": "^4.12.0",
100
100
  "typescript": "^5.9.3"
101
- },
102
- "pnpm": {
103
- "auditConfig": {
104
- "ignoreGhsas": [
105
- "GHSA-w5hq-g745-h8pq"
106
- ]
107
- }
108
101
  }
109
102
  }
package/schema/Alarm.json CHANGED
@@ -23,6 +23,11 @@
23
23
  "type": "Number",
24
24
  "required": false
25
25
  },
26
+ "tenant": {
27
+ "type": "Pointer",
28
+ "targetClass": "{{PREFIX}}Tenant",
29
+ "required": false
30
+ },
26
31
  "trigger": {
27
32
  "type": "Object"
28
33
  },
@@ -13,6 +13,11 @@
13
13
  "required": false,
14
14
  "defaultValue": false
15
15
  },
16
+ "targettime": {
17
+ "type": "Number",
18
+ "required": false,
19
+ "defaultValue": 0
20
+ },
16
21
  "tenant": {
17
22
  "type": "Pointer",
18
23
  "targetClass": "{{PREFIX}}Tenant",
@@ -2,7 +2,7 @@
2
2
  "fields": {
3
3
  "article": {
4
4
  "type": "Pointer",
5
- "targetClass": "{{PREFIX}}MES_Article",
5
+ "targetClass": "{{PREFIX}}BDE_Article",
6
6
  "required": false
7
7
  },
8
8
  "articlename": {
@@ -2,7 +2,7 @@
2
2
  "fields": {
3
3
  "article": {
4
4
  "type": "Pointer",
5
- "targetClass": "{{PREFIX}}MES_Article",
5
+ "targetClass": "{{PREFIX}}BDE_Article",
6
6
  "required": false
7
7
  },
8
8
  "ordernr": {