@vario-software/vario-app-framework-backend 2026.22.2 → 2026.25.0
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/ErpApi.js +17 -4
- package/api/helpers/streamResponse.js +8 -0
- package/modules/accessToken.js +20 -0
- package/modules/baseUrlCache.js +20 -8
- package/modules/offlineToken.js +20 -0
- package/package.json +1 -1
package/api/ErpApi.js
CHANGED
|
@@ -9,6 +9,7 @@ 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
11
|
const { validateOfflineToken, isAppTokenExpired, refreshAppToken } = require('#backend/utils/token.js');
|
|
12
|
+
const HttpError = require('#backend/utils/httpError.js');
|
|
12
13
|
|
|
13
14
|
const singletonPromise = new PromiseSingletonMap();
|
|
14
15
|
class ErpApi extends Api
|
|
@@ -58,7 +59,10 @@ class ErpApi extends Api
|
|
|
58
59
|
{
|
|
59
60
|
const tenant = getTenant();
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
if (tenant)
|
|
63
|
+
{
|
|
64
|
+
await this.app.accessToken.delete(tenant);
|
|
65
|
+
}
|
|
62
66
|
}
|
|
63
67
|
}
|
|
64
68
|
|
|
@@ -66,13 +70,22 @@ class ErpApi extends Api
|
|
|
66
70
|
{
|
|
67
71
|
const tenant = getTenant();
|
|
68
72
|
|
|
73
|
+
if (!tenant)
|
|
74
|
+
{
|
|
75
|
+
throw new HttpError(
|
|
76
|
+
'MISSING_TENANT',
|
|
77
|
+
400,
|
|
78
|
+
'backend/api/ErpApi',
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
69
82
|
return singletonPromise.run(tenant, async () =>
|
|
70
83
|
{
|
|
71
84
|
const savedAccessToken = await this.app.accessToken.get(tenant);
|
|
72
85
|
|
|
73
86
|
if (savedAccessToken)
|
|
74
87
|
{
|
|
75
|
-
const baseUrl = await this.app.baseUrlCache.get();
|
|
88
|
+
const baseUrl = await this.app.baseUrlCache.get(tenant);
|
|
76
89
|
|
|
77
90
|
return {
|
|
78
91
|
baseUrl,
|
|
@@ -86,7 +99,7 @@ class ErpApi extends Api
|
|
|
86
99
|
const domain = iss.replace('https://sso.', '').split('/')[0];
|
|
87
100
|
const baseUrl = `https://${tenant}.${domain}`;
|
|
88
101
|
|
|
89
|
-
await this.app.baseUrlCache.set(baseUrl);
|
|
102
|
+
await this.app.baseUrlCache.set(tenant, baseUrl);
|
|
90
103
|
|
|
91
104
|
const {
|
|
92
105
|
access_token: accessToken,
|
|
@@ -98,7 +111,7 @@ class ErpApi extends Api
|
|
|
98
111
|
|
|
99
112
|
const expiresAt = Date.now() + (expiresIn * 0.9) * 1000;
|
|
100
113
|
|
|
101
|
-
this.app.accessToken.set(tenant, accessToken, expiresAt);
|
|
114
|
+
await this.app.accessToken.set(tenant, accessToken, expiresAt);
|
|
102
115
|
|
|
103
116
|
return {
|
|
104
117
|
baseUrl,
|
|
@@ -18,6 +18,13 @@ function streamResponse(res, handler)
|
|
|
18
18
|
res.write(`${JSON.stringify(data)}\n`);
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
// Keep the connection non-idle during long silent phases (e.g. polling for the
|
|
22
|
+
// ERP import to extract). Reverse proxies/gateways close idle connections with a
|
|
23
|
+
// 504 after ~60s; a periodic blank line resets that timer. Blank lines are ignored
|
|
24
|
+
// by the client parsers (they filter out non-JSON / empty lines).
|
|
25
|
+
const HEARTBEAT_INTERVAL_MS = 15_000;
|
|
26
|
+
const heartbeat = setInterval(() => res.write('\n'), HEARTBEAT_INTERVAL_MS);
|
|
27
|
+
|
|
21
28
|
return handler(onProgress)
|
|
22
29
|
.then(result =>
|
|
23
30
|
{
|
|
@@ -29,6 +36,7 @@ function streamResponse(res, handler)
|
|
|
29
36
|
})
|
|
30
37
|
.finally(() =>
|
|
31
38
|
{
|
|
39
|
+
clearInterval(heartbeat);
|
|
32
40
|
res.end();
|
|
33
41
|
});
|
|
34
42
|
}
|
package/modules/accessToken.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const HttpError = require('#backend/utils/httpError.js');
|
|
2
|
+
|
|
1
3
|
class AccessToken
|
|
2
4
|
{
|
|
3
5
|
#cache = {};
|
|
@@ -9,6 +11,8 @@ class AccessToken
|
|
|
9
11
|
|
|
10
12
|
async get(tenant)
|
|
11
13
|
{
|
|
14
|
+
this.#assertTenant(tenant);
|
|
15
|
+
|
|
12
16
|
const tokenData = this.#cache[tenant];
|
|
13
17
|
|
|
14
18
|
if (!tokenData)
|
|
@@ -28,6 +32,8 @@ class AccessToken
|
|
|
28
32
|
|
|
29
33
|
async set(tenant, accessToken, expiresAt)
|
|
30
34
|
{
|
|
35
|
+
this.#assertTenant(tenant);
|
|
36
|
+
|
|
31
37
|
this.#cache[tenant] = {
|
|
32
38
|
accessToken,
|
|
33
39
|
expiresAt,
|
|
@@ -36,8 +42,22 @@ class AccessToken
|
|
|
36
42
|
|
|
37
43
|
async delete(tenant)
|
|
38
44
|
{
|
|
45
|
+
this.#assertTenant(tenant);
|
|
46
|
+
|
|
39
47
|
delete this.#cache[tenant];
|
|
40
48
|
}
|
|
49
|
+
|
|
50
|
+
#assertTenant(tenant)
|
|
51
|
+
{
|
|
52
|
+
if (!tenant)
|
|
53
|
+
{
|
|
54
|
+
throw new HttpError(
|
|
55
|
+
'MISSING_TENANT',
|
|
56
|
+
400,
|
|
57
|
+
'backend/modules/accessToken',
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
module.exports = AccessToken;
|
package/modules/baseUrlCache.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { getTenant } = require('#backend/utils/context.js');
|
|
2
1
|
const { validateOfflineToken } = require('#backend/utils/token.js');
|
|
2
|
+
const HttpError = require('#backend/utils/httpError.js');
|
|
3
3
|
|
|
4
4
|
class BaseUrlCache
|
|
5
5
|
{
|
|
@@ -15,9 +15,21 @@ class BaseUrlCache
|
|
|
15
15
|
return Promise.resolve();
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
#assertTenant(tenant)
|
|
19
19
|
{
|
|
20
|
-
|
|
20
|
+
if (!tenant)
|
|
21
|
+
{
|
|
22
|
+
throw new HttpError(
|
|
23
|
+
'MISSING_TENANT',
|
|
24
|
+
400,
|
|
25
|
+
'backend/modules/baseUrlCache',
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async get(tenant)
|
|
31
|
+
{
|
|
32
|
+
this.#assertTenant(tenant);
|
|
21
33
|
|
|
22
34
|
if (this.#cache[tenant])
|
|
23
35
|
{
|
|
@@ -30,21 +42,21 @@ class BaseUrlCache
|
|
|
30
42
|
const domain = iss.replace('https://sso.', '').split('/')[0];
|
|
31
43
|
const baseUrl = `https://${tenant}.${domain}`;
|
|
32
44
|
|
|
33
|
-
await this.set(baseUrl);
|
|
45
|
+
await this.set(tenant, baseUrl);
|
|
34
46
|
|
|
35
47
|
return baseUrl;
|
|
36
48
|
}
|
|
37
49
|
|
|
38
|
-
async set(value)
|
|
50
|
+
async set(tenant, value)
|
|
39
51
|
{
|
|
40
|
-
|
|
52
|
+
this.#assertTenant(tenant);
|
|
41
53
|
|
|
42
54
|
this.#cache[tenant] = value;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
|
-
async delete()
|
|
57
|
+
async delete(tenant)
|
|
46
58
|
{
|
|
47
|
-
|
|
59
|
+
this.#assertTenant(tenant);
|
|
48
60
|
|
|
49
61
|
delete this.#cache[tenant];
|
|
50
62
|
}
|
package/modules/offlineToken.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const HttpError = require('#backend/utils/httpError.js');
|
|
2
|
+
|
|
1
3
|
class OfflineToken
|
|
2
4
|
{
|
|
3
5
|
constructor(app, filename = 'offlineToken.db')
|
|
@@ -15,13 +17,29 @@ class OfflineToken
|
|
|
15
17
|
this.database = await JSONFilePreset(this.filename, {});
|
|
16
18
|
}
|
|
17
19
|
|
|
20
|
+
#assertTenant(tenant)
|
|
21
|
+
{
|
|
22
|
+
if (!tenant)
|
|
23
|
+
{
|
|
24
|
+
throw new HttpError(
|
|
25
|
+
'MISSING_TENANT',
|
|
26
|
+
400,
|
|
27
|
+
'backend/modules/offlineToken',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
async get(tenant)
|
|
19
33
|
{
|
|
34
|
+
this.#assertTenant(tenant);
|
|
35
|
+
|
|
20
36
|
return this.database.data[tenant];
|
|
21
37
|
}
|
|
22
38
|
|
|
23
39
|
async set(tenant, offlineToken)
|
|
24
40
|
{
|
|
41
|
+
this.#assertTenant(tenant);
|
|
42
|
+
|
|
25
43
|
this.app.accessToken.delete(tenant);
|
|
26
44
|
|
|
27
45
|
return this.database.update(tokens =>
|
|
@@ -32,6 +50,8 @@ class OfflineToken
|
|
|
32
50
|
|
|
33
51
|
async delete(tenant)
|
|
34
52
|
{
|
|
53
|
+
this.#assertTenant(tenant);
|
|
54
|
+
|
|
35
55
|
this.app.accessToken.delete(tenant);
|
|
36
56
|
|
|
37
57
|
return this.database.update(tokens =>
|
package/package.json
CHANGED