@openinc/parse-server-opendash 4.0.33 → 4.0.38

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")) {
@@ -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
  }
@@ -7,21 +7,22 @@ exports.init = init;
7
7
  const node_1 = __importDefault(require("parse/node"));
8
8
  const AMQPBusConnection_js_1 = require("../features/openware/services/AMQPBusConnection.js");
9
9
  const index_js_1 = require("../features/schema/index.js");
10
- const index_js_2 = require("../types/index.js");
10
+ const index_js_2 = require("../features/config/index.js");
11
+ const index_js_3 = require("../types/index.js");
11
12
  async function init() {
12
13
  const schema = await node_1.default.Schema.all();
13
- const publishEnabled = (process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_ENABLE || "FALSE").toUpperCase() === "TRUE";
14
- console.log(`ChangeLog publishing enabled: ${publishEnabled}`);
14
+ const config = index_js_2.ConfigInstance.getInstance();
15
+ console.log(`ChangeLog publishing enabled: ${config.getBoolean("CHANGELOG_PUBLISH_ENABLE")}`);
15
16
  const blacklistedClasses = [
16
17
  "_Session",
17
18
  "_Installation",
18
- ...(process.env.OPENINC_PARSE_CHANGELOG_BLACKLIST || "")
19
+ ...(config.get("CHANGELOG_BLACKLIST") || "")
19
20
  .split(",")
20
21
  .map((c) => c.trim())
21
22
  .filter((c) => c.length > 0),
22
23
  ];
23
24
  console.log(`ChangeLog blacklisted classes: ${blacklistedClasses.join(", ") || "None"}`);
24
- (0, index_js_1.beforeSaveHook)(index_js_2.Changelog.className, async (request) => {
25
+ (0, index_js_1.beforeSaveHook)(index_js_3.Changelog.className, async (request) => {
25
26
  const { object, original, user } = request;
26
27
  await (0, index_js_1.defaultHandler)(request);
27
28
  await (0, index_js_1.defaultAclHandler)(request);
@@ -29,16 +30,16 @@ async function init() {
29
30
  object.getACL().setReadAccess(user.id, true);
30
31
  }
31
32
  });
32
- if (publishEnabled) {
33
+ if (config.getBoolean("CHANGELOG_PUBLISH_ENABLE")) {
33
34
  const params = {
34
- host: process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_HOST || "localhost",
35
- vhost: process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_VHOST || "configuration",
36
- port: parseInt(process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_PORT || "5672"),
37
- username: process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_USERNAME || "guest",
38
- password: process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_PASSWORD || "guest",
39
- exchange: process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_EXCHANGE || "topic",
40
- topicPrefix: process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_TOPIC_PREFIX || "config",
41
- managementPort: parseInt(process.env.OPENINC_PARSE_CHANGELOG_PUBLISH_MANAGEMENT_PORT || "15672"),
35
+ host: config.get("CHANGELOG_PUBLISH_HOST") || "localhost",
36
+ vhost: config.get("CHANGELOG_PUBLISH_VHOST") || "configuration",
37
+ port: parseInt(config.get("CHANGELOG_PUBLISH_PORT") || "5672"),
38
+ username: config.get("CHANGELOG_PUBLISH_USERNAME") || "guest",
39
+ password: config.get("CHANGELOG_PUBLISH_PASSWORD") || "guest",
40
+ exchange: config.get("CHANGELOG_PUBLISH_EXCHANGE") || "topic",
41
+ topicPrefix: config.get("CHANGELOG_PUBLISH_TOPIC_PREFIX") || "config",
42
+ managementPort: parseInt(config.get("CHANGELOG_PUBLISH_MANAGEMENT_PORT") || "15672"),
42
43
  };
43
44
  const connection = new AMQPBusConnection_js_1.AMQPBusConnection(params);
44
45
  try {
@@ -47,7 +48,7 @@ async function init() {
47
48
  console.log(`Establishing Connection to ${url} for ChangeLog publishing...`);
48
49
  await connection.connect();
49
50
  console.log(`Connected to AMQPBus at ${url} for ChangeLog publishing.`);
50
- (0, index_js_1.afterSaveHook)(index_js_2.Changelog.className, async (request) => {
51
+ (0, index_js_1.afterSaveHook)(index_js_3.Changelog.className, async (request) => {
51
52
  const { object: changelog } = request;
52
53
  const msg = JSON.stringify(changelog.toJSON());
53
54
  const topic = `${params.topicPrefix && params.topicPrefix.length > 0
@@ -63,8 +64,8 @@ async function init() {
63
64
  }
64
65
  schema.forEach((classSchema) => {
65
66
  if (!classSchema.className ||
66
- classSchema.className === index_js_2.Changelog.className ||
67
- classSchema.className === index_js_2.Permission.className) {
67
+ classSchema.className === index_js_3.Changelog.className ||
68
+ classSchema.className === index_js_3.Permission.className) {
68
69
  console.log(`Skipping ChangeLog hook for class ${classSchema.className}`);
69
70
  return;
70
71
  }
@@ -81,7 +82,7 @@ async function init() {
81
82
  console.log(`Skipping ChangeLog entry for class ${classSchema.className} and object ${object.id} due to blacklisted class.`);
82
83
  return;
83
84
  }
84
- const changelog = new index_js_2.Changelog();
85
+ const changelog = new index_js_3.Changelog();
85
86
  changelog.set("nameOfClass", classSchema.className);
86
87
  changelog.set("changedObject", object.id);
87
88
  changelog.set("operation", "delete");
@@ -109,7 +110,7 @@ async function init() {
109
110
  console.log(`Skipping ChangeLog entry for class ${classSchema.className} and object ${object.id} due to ${context.skipChangelog ? "context flag" : "blacklisted class"}.`);
110
111
  return;
111
112
  }
112
- const changelog = new index_js_2.Changelog();
113
+ const changelog = new index_js_3.Changelog();
113
114
  changelog.set("nameOfClass", classSchema.className);
114
115
  changelog.set("changedObject", object.id);
115
116
  changelog.set("operation", original ? "update" : "create");
@@ -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
  }
@@ -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.33",
3
+ "version": "4.0.38",
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.0.10",
75
+ "i18next": "^26.2.0",
76
76
  "i18next-fs-backend": "^2.6.5",
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
- "parse": "^8.5.0",
82
- "parse-server": "^9.8.0",
81
+ "parse": "^8.6.0",
82
+ "parse-server": "^9.9.0",
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.6.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
  },