@vario-software/vario-app-framework-backend 2025.46.2 → 2026.3.2

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/api/Api.js CHANGED
@@ -227,11 +227,13 @@ class Api
227
227
 
228
228
  async onError(error)
229
229
  {
230
+ const maskedBody = this.suppressLogs ? '[secret]' : this.body;
231
+
230
232
  const message = {
231
233
  request: {
232
234
  requestUrl: this.fullPath,
233
235
  requestOptions: this.requestOptions,
234
- body: this.body,
236
+ body: maskedBody,
235
237
  },
236
238
  response: error,
237
239
  duration: `${(performance.now() - this.timer).toFixed(2)}ms`,
@@ -248,7 +250,7 @@ class Api
248
250
  request: {
249
251
  requestUrl: this.fullPath,
250
252
  requestOptions: this.requestOptions,
251
- body: this.body,
253
+ body: maskedBody,
252
254
  },
253
255
  response: {
254
256
  data: error,
package/api/ErpApi.js CHANGED
@@ -8,7 +8,7 @@ const Migration = require('#backend/api/modules/migration.js');
8
8
  const TextEnum = require('#backend/api/modules/textEnum.js');
9
9
  const Webhook = require('#backend/api/modules/webhook.js');
10
10
  const PermittedToken = require('#backend/api/modules/permittedToken.js');
11
- const { validateOfflineToken } = require('#backend/utils/token.js');
11
+ const { validateOfflineToken, isAppTokenExpired, refreshAppToken } = require('#backend/utils/token.js');
12
12
 
13
13
  const singletonPromise = new PromiseSingletonMap();
14
14
  class ErpApi extends Api
@@ -20,21 +20,26 @@ class ErpApi extends Api
20
20
  } = getContext();
21
21
 
22
22
  const {
23
- excecuteAsAppUser = runAsAppUser,
23
+ executeAsAppUser = runAsAppUser,
24
24
  ...restOptions
25
25
  } = options;
26
26
 
27
27
  super(path, restOptions);
28
28
 
29
- this.excecuteAsAppUser = excecuteAsAppUser;
29
+ this.executeAsAppUser = executeAsAppUser;
30
30
  }
31
31
 
32
32
  async onBeforeRequest()
33
33
  {
34
34
  const { baseUrl, Authorization } = await this.getAuthorization();
35
35
 
36
- if (!this.excecuteAsAppUser)
36
+ if (!this.executeAsAppUser)
37
37
  {
38
+ if (isAppTokenExpired())
39
+ {
40
+ await refreshAppToken();
41
+ }
42
+
38
43
  this.setHeaders({
39
44
  'X-Forwarded-App-Token': getAppToken(),
40
45
  });
@@ -8,7 +8,7 @@ const Webhook = class
8
8
  this.ApiAdapter = ApiAdapter;
9
9
  }
10
10
 
11
- register = async function(destinationQueue, url)
11
+ register = async function(destinationQueue, url, destinationOwner)
12
12
  {
13
13
  const apiUrl = `${process.env.WEBHOOK_HOST ?? `https://${getRequest().get('host')}`}`;
14
14
 
@@ -18,13 +18,14 @@ const Webhook = class
18
18
  method: 'POST',
19
19
  body: JSON.stringify({
20
20
  url: `${apiUrl}${url}`,
21
+ destinationOwner,
21
22
  destinationQueue,
22
23
  appIdentifier: app.client.appIdentifier,
23
24
  }),
24
25
  });
25
26
  };
26
27
 
27
- deregister = async function(destinationQueue, url)
28
+ deregister = async function(destinationQueue, url, destinationOwner)
28
29
  {
29
30
  const apiUrl = `${process.env.WEBHOOK_HOST ?? `https://${getRequest().get('host')}`}`;
30
31
 
@@ -34,6 +35,7 @@ const Webhook = class
34
35
  method: 'POST',
35
36
  body: JSON.stringify({
36
37
  url: `${apiUrl}${url}`,
38
+ destinationOwner,
37
39
  destinationQueue,
38
40
  appIdentifier: app.client.appIdentifier,
39
41
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vario-software/vario-app-framework-backend",
3
- "version": "2025.46.2",
3
+ "version": "2026.03.2",
4
4
  "repository": "https://github.com/vario-software/vario-app-framework",
5
5
  "author": "VARIO Software AG",
6
6
  "homepage": "https://www.vario.ag",
package/utils/migrator.js CHANGED
@@ -144,16 +144,16 @@ const Migrator = class
144
144
  return textEnumGroup;
145
145
  },
146
146
 
147
- registerWebhook: async (destinationQueue, url) =>
147
+ registerWebhook: async (destinationQueue, url, destinationOwner) =>
148
148
  {
149
- await this.ApiAdapter.webhook.register(destinationQueue, url);
149
+ await this.ApiAdapter.webhook.register(destinationQueue, url, destinationOwner);
150
150
 
151
151
  await this.methods.log(`Webhook for destination "${destinationQueue}" registered\n`);
152
152
  },
153
153
 
154
- deregisterWebhook: async (destinationQueue, url) =>
154
+ deregisterWebhook: async (destinationQueue, url, destinationOwner) =>
155
155
  {
156
- await this.ApiAdapter.webhook.deregister(destinationQueue, url);
156
+ await this.ApiAdapter.webhook.deregister(destinationQueue, url, destinationOwner);
157
157
 
158
158
  await this.methods.log(`Webhook for destination "${destinationQueue}" deregistered\n`);
159
159
  },
@@ -353,22 +353,79 @@ const Migrator = class
353
353
 
354
354
  if (existingProxyId)
355
355
  {
356
- this.methods.updateAppScriptingTrigger(triggerId, script, existingProxyId);
356
+ await this.methods.updateAppScriptingTrigger(triggerId, script, existingProxyId);
357
357
 
358
358
  return;
359
359
  }
360
360
 
361
+ const { data: existingGroups } = await this.ApiAdapter.fetch(
362
+ '/cmn/computed-queries/scripting/script-module-groups',
363
+ {
364
+ useInternalApi: true,
365
+ method: 'POST',
366
+ body: {
367
+ adhocPreset: {
368
+ queryPredicate: {
369
+ type: 'FILTER',
370
+ operator: 'EQUALS',
371
+ property: 'name',
372
+ values: [this.app.client.appIdentifier],
373
+ },
374
+ results: [
375
+ { property: 'id' },
376
+ { property: 'name' },
377
+ ],
378
+ },
379
+ },
380
+ },
381
+ );
382
+
383
+ let scriptGroup;
384
+
385
+ if (existingGroups?.data && existingGroups.data.length > 0)
386
+ {
387
+ scriptGroup = existingGroups.data[0];
388
+ }
389
+ else
390
+ {
391
+ const { data: newGroup } = await this.ApiAdapter.fetch(
392
+ '/cmn/scripting/module-groups',
393
+ {
394
+ useInternalApi: true,
395
+ method: 'POST',
396
+ body: { name: this.app.client.appIdentifier },
397
+ },
398
+ );
399
+ scriptGroup = newGroup;
400
+ }
401
+
402
+ const { data: scriptModule } = await this.ApiAdapter.fetch(
403
+ '/cmn/scripting/modules/presettings',
404
+ {
405
+ useInternalApi: true,
406
+ method: 'POST',
407
+ body: {
408
+ name: triggerId,
409
+ script: typeof script === 'string' ? script : JSON.stringify(script),
410
+ domain: 'app',
411
+ groupRef: { id: scriptGroup.id },
412
+ permissionAggregation: {},
413
+ },
414
+ },
415
+ );
416
+
361
417
  await this.ApiAdapter.fetch(
362
418
  '/community/latest/cmn/system/app-scripting-proxy',
363
419
  {
364
420
  useInternalApi: true,
365
421
  method: 'POST',
366
- body: JSON.stringify({
422
+ body: {
367
423
  appIdentifier: this.app.client.appIdentifier,
368
424
  triggerId,
369
- script,
370
- }),
371
- });
425
+ scriptModuleRef: { id: scriptModule.id },
426
+ },
427
+ },
428
+ );
372
429
 
373
430
  await this.methods.log(`App-Script-Trigger with id "${triggerId}" successfully created\n`);
374
431
  },
@@ -377,20 +434,45 @@ const Migrator = class
377
434
  {
378
435
  if (!id)
379
436
  {
380
- id = await this.getAppScriptingTriggerId(triggerId);
437
+ id = await this.methods.getAppScriptingTriggerId(triggerId);
381
438
  }
382
439
 
383
- await this.ApiAdapter.fetch(
440
+ const { data: proxy } = await this.ApiAdapter.fetch(
384
441
  `/community/latest/cmn/system/app-scripting-proxy/${id}`,
442
+ {
443
+ useInternalApi: true,
444
+ method: 'GET',
445
+ },
446
+ );
447
+
448
+ const scriptModuleId = proxy?.scriptModuleRef?.id;
449
+
450
+ if (!scriptModuleId)
451
+ {
452
+ await this.methods.log(`App-Script-Trigger with id "${triggerId}" has no scriptModuleRef\n`, 'ERROR');
453
+
454
+ return;
455
+ }
456
+
457
+ const { data: existingScriptModule } = await this.ApiAdapter.fetch(
458
+ `/cmn/scripting/modules/${scriptModuleId}/presettings`,
459
+ {
460
+ useInternalApi: true,
461
+ method: 'GET',
462
+ },
463
+ );
464
+
465
+ await this.ApiAdapter.fetch(
466
+ `/cmn/scripting/modules/${scriptModuleId}/presettings`,
385
467
  {
386
468
  useInternalApi: true,
387
469
  method: 'PUT',
388
- body: JSON.stringify({
389
- appIdentifier: this.app.client.appIdentifier,
390
- triggerId,
391
- script,
392
- }),
393
- });
470
+ body: {
471
+ ...existingScriptModule,
472
+ script: typeof script === 'string' ? script : JSON.stringify(script),
473
+ },
474
+ },
475
+ );
394
476
 
395
477
  await this.methods.log(`App-Script-Trigger with id "${triggerId}" successfully updated\n`);
396
478
  },
@@ -400,7 +482,7 @@ const Migrator = class
400
482
  const { data: existingProxy } = await this.ApiAdapter.vql({
401
483
  statement: `
402
484
  SELECT id
403
- FROM system.queryAppScriptingProxies
485
+ FROM system.queryAppScriptingProxies
404
486
  WHERE appIdentifier = '${this.app.client.appIdentifier}'
405
487
  AND triggerId = '${triggerId}'
406
488
  `,
package/utils/token.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const { jwtVerify, decodeJwt, importJWK } = require('jose');
2
2
  const { getApp } = require('#backend/utils/context.js');
3
+ const { getAppToken, getContext } = require('#backend/utils/context.js');
3
4
 
4
5
  function validateOfflineToken(offlineToken)
5
6
  {
@@ -82,7 +83,45 @@ function validateAppToken(appToken)
82
83
  });
83
84
  }
84
85
 
86
+ function isAppTokenExpired()
87
+ {
88
+ const { accessToken } = getContext();
89
+
90
+ const { exp } = accessToken;
91
+
92
+ return ((exp - 2) < (Date.now() / 1000));
93
+ }
94
+
95
+ async function refreshAppToken()
96
+ {
97
+ const appToken = getAppToken();
98
+ const app = getApp();
99
+
100
+ const { data } = await app.erp.fetch(`/cmn/apps/${app.client.appIdentifier}/refresh-token`, {
101
+ method: 'POST',
102
+ body: appToken,
103
+ useInternalApi: true,
104
+ headers: {
105
+ 'Content-Type': 'text/plain',
106
+ },
107
+ secret: true,
108
+ executeAsAppUser: true,
109
+ secretsToMask: ['bearerToken'],
110
+ });
111
+
112
+ const newAppToken = data.bearerToken;
113
+
114
+ const accessToken = await validateAppToken(newAppToken);
115
+
116
+ const context = getContext();
117
+
118
+ context.appToken = newAppToken;
119
+ context.accessToken = accessToken;
120
+ }
121
+
85
122
  module.exports = {
86
123
  validateOfflineToken,
87
124
  validateAppToken,
125
+ isAppTokenExpired,
126
+ refreshAppToken,
88
127
  };