@botfabrik/engine-webclient 4.101.2 → 4.101.3-alpha.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.
- package/dist/auth/auth-pages.js +3 -8
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.js +35 -39
- package/dist/auth/relay-state.js +6 -17
- package/dist/auth/ttl-cache.js +1 -5
- package/dist/constants.js +1 -4
- package/dist/createSessionInfo.d.ts +2 -2
- package/dist/createSessionInfo.js +3 -5
- package/dist/createSessionInfo.test.d.ts +1 -0
- package/dist/createSessionInfo.test.js +40 -45
- package/dist/extractLocale.js +3 -5
- package/dist/extractLocale.test.d.ts +1 -0
- package/dist/extractLocale.test.js +13 -18
- package/dist/getSupportedClientLocale.js +1 -3
- package/dist/getSupportedClientLocale.test.d.ts +1 -0
- package/dist/getSupportedClientLocale.test.js +15 -20
- package/dist/index.d.ts +2 -2
- package/dist/index.js +45 -64
- package/dist/loadPreviousConversation.js +5 -7
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +20 -25
- package/dist/requestSessionData.d.ts +1 -1
- package/dist/requestSessionData.js +5 -7
- package/dist/requestSessionData.test.d.ts +1 -0
- package/dist/requestSessionData.test.js +42 -52
- package/dist/setTranslations.js +4 -6
- package/dist/speechToText.d.ts +1 -1
- package/dist/speechToText.js +3 -5
- package/dist/types.js +2 -5
- package/dist/version.d.ts +1 -0
- package/dist/version.js +2 -0
- package/dist/views/index.js +1 -3
- package/package.json +21 -10
package/dist/auth/auth-pages.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setAuthPageHeaders = setAuthPageHeaders;
|
|
4
|
-
exports.renderAuthSuccessPage = renderAuthSuccessPage;
|
|
5
|
-
exports.renderAuthErrorPage = renderAuthErrorPage;
|
|
6
1
|
const STRINGS = {
|
|
7
2
|
de: {
|
|
8
3
|
successTitle: 'Login erfolgreich',
|
|
@@ -41,13 +36,13 @@ const STRINGS = {
|
|
|
41
36
|
closeWindow: 'Chiudi finestra',
|
|
42
37
|
},
|
|
43
38
|
};
|
|
44
|
-
function setAuthPageHeaders(res) {
|
|
39
|
+
export function setAuthPageHeaders(res) {
|
|
45
40
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
46
41
|
res.setHeader('Content-Security-Policy', "default-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'; img-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'");
|
|
47
42
|
res.setHeader('Referrer-Policy', 'no-referrer');
|
|
48
43
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
49
44
|
}
|
|
50
|
-
function renderAuthSuccessPage(lang) {
|
|
45
|
+
export function renderAuthSuccessPage(lang) {
|
|
51
46
|
const t = STRINGS[lang];
|
|
52
47
|
return `<!doctype html>
|
|
53
48
|
<html lang="${lang}">
|
|
@@ -74,7 +69,7 @@ function renderAuthSuccessPage(lang) {
|
|
|
74
69
|
</body>
|
|
75
70
|
</html>`;
|
|
76
71
|
}
|
|
77
|
-
function renderAuthErrorPage(lang) {
|
|
72
|
+
export function renderAuthErrorPage(lang) {
|
|
78
73
|
const t = STRINGS[lang];
|
|
79
74
|
return `<!doctype html>
|
|
80
75
|
<html lang="${lang}">
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BotInstance, Logger } from '@botfabrik/engine-domain';
|
|
2
2
|
import type { Namespace } from 'socket.io';
|
|
3
|
-
import type { Auth } from '../types';
|
|
3
|
+
import type { Auth } from '../types.js';
|
|
4
4
|
export type AuthenticatedUser = {
|
|
5
5
|
email: string;
|
|
6
6
|
firstName: string | undefined;
|
|
@@ -8,4 +8,4 @@ export type AuthenticatedUser = {
|
|
|
8
8
|
};
|
|
9
9
|
export declare function setUpSamlAuth(bot: BotInstance, auth: Auth, clientName: string, nsp: Namespace): void;
|
|
10
10
|
export declare function storeLoginRequestToken(loginRequestToken: string, socketId: string): void;
|
|
11
|
-
export declare function verifyLoginToken(token: string | undefined, auth: Auth | undefined, logger: Logger): AuthenticatedUser | undefined
|
|
11
|
+
export declare function verifyLoginToken(token: string | undefined, auth: Auth | undefined, logger: Logger): Promise<AuthenticatedUser | undefined>;
|
package/dist/auth/index.js
CHANGED
|
@@ -1,68 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
exports.verifyLoginToken = verifyLoginToken;
|
|
9
|
-
const passport_saml_1 = require("@node-saml/passport-saml");
|
|
10
|
-
const express_1 = __importDefault(require("express"));
|
|
11
|
-
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
12
|
-
const passport_1 = __importDefault(require("passport"));
|
|
13
|
-
const auth_pages_1 = require("./auth-pages");
|
|
14
|
-
const relay_state_1 = require("./relay-state");
|
|
15
|
-
const ttl_cache_1 = require("./ttl-cache");
|
|
1
|
+
import { Strategy, } from '@node-saml/passport-saml';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import { jwtVerify, SignJWT } from 'jose';
|
|
4
|
+
import passport from 'passport';
|
|
5
|
+
import { renderAuthErrorPage, renderAuthSuccessPage, setAuthPageHeaders, } from './auth-pages.js';
|
|
6
|
+
import { signRelayState, verifyRelayState } from './relay-state.js';
|
|
7
|
+
import { TtlCache } from './ttl-cache.js';
|
|
16
8
|
const E_MAIL_CLAIM = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress';
|
|
17
9
|
const FIRST_NAME_CLAIM = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname';
|
|
18
10
|
const LAST_NAME_CLAIM = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname';
|
|
19
|
-
const loginTokenCache = new
|
|
11
|
+
const loginTokenCache = new TtlCache({
|
|
20
12
|
defaultTtlMs: 5 * 60 * 1000, // 5 minutes
|
|
21
13
|
});
|
|
22
|
-
function setUpSamlAuth(bot, auth, clientName, nsp) {
|
|
14
|
+
export function setUpSamlAuth(bot, auth, clientName, nsp) {
|
|
23
15
|
bot.logger.info(`Setting up SAML authentication for ${clientName}`);
|
|
24
16
|
const strategyName = `${clientName}-saml`;
|
|
25
17
|
const callbackUrl = `/${clientName}/auth/saml/login/callback`;
|
|
26
|
-
const samlStrategy = new
|
|
18
|
+
const samlStrategy = new Strategy({
|
|
27
19
|
callbackUrl: `${bot.webserver.baseUrl}${callbackUrl}`,
|
|
28
20
|
entryPoint: auth.saml.entryPoint,
|
|
29
21
|
issuer: auth.saml.issuer,
|
|
30
22
|
idpCert: fixUnwantedEscapeCharacters(auth.saml.idpCert),
|
|
31
23
|
...(auth.saml.options || {}),
|
|
32
24
|
}, signonVerify, logoutVerify);
|
|
33
|
-
|
|
25
|
+
passport.use(strategyName, samlStrategy);
|
|
34
26
|
bot.webserver.express.get(`/${clientName}/auth/login`, (req, res, next) => {
|
|
35
27
|
const { loginRequestToken } = req.query;
|
|
36
|
-
const relayState =
|
|
28
|
+
const relayState = signRelayState(String(loginRequestToken), auth.jwtSecret);
|
|
37
29
|
const options = {
|
|
38
30
|
session: false,
|
|
39
31
|
additionalParams: { RelayState: relayState },
|
|
40
32
|
};
|
|
41
|
-
const authenticateFn =
|
|
33
|
+
const authenticateFn = passport.authenticate(strategyName, options);
|
|
42
34
|
authenticateFn(req, res, next);
|
|
43
35
|
});
|
|
44
|
-
bot.webserver.express.post(callbackUrl,
|
|
45
|
-
const authenticatorFn =
|
|
36
|
+
bot.webserver.express.post(callbackUrl, express.urlencoded({ extended: false }), (req, res, next) => {
|
|
37
|
+
const authenticatorFn = passport.authenticate(strategyName, { session: false }, async (err, user) => {
|
|
46
38
|
const lang = getLang(req);
|
|
47
|
-
|
|
39
|
+
setAuthPageHeaders(res);
|
|
48
40
|
try {
|
|
49
41
|
if (err) {
|
|
50
42
|
bot.logger.error('SAML callback error:', err);
|
|
51
|
-
return res.status(500).send(
|
|
43
|
+
return res.status(500).send(renderAuthErrorPage(lang));
|
|
52
44
|
}
|
|
53
45
|
const relayState = req.body?.RelayState;
|
|
54
46
|
if (!relayState) {
|
|
55
47
|
bot.logger.warn('Missing RelayState');
|
|
56
|
-
return res.status(400).send(
|
|
48
|
+
return res.status(400).send(renderAuthErrorPage(lang));
|
|
57
49
|
}
|
|
58
|
-
const loginRequestToken =
|
|
50
|
+
const loginRequestToken = verifyRelayState(relayState, auth.jwtSecret);
|
|
59
51
|
const { socketId } = consumeLoginRequestToken(loginRequestToken);
|
|
60
52
|
if (!user) {
|
|
61
|
-
return res.status(401).send(
|
|
53
|
+
return res.status(401).send(renderAuthErrorPage(lang));
|
|
62
54
|
}
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
const secret = new TextEncoder().encode(auth.jwtSecret);
|
|
56
|
+
const loginToken = await new SignJWT(user)
|
|
57
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
58
|
+
.setExpirationTime('1m')
|
|
59
|
+
.sign(secret);
|
|
66
60
|
const socket = nsp.sockets.get(socketId);
|
|
67
61
|
if (socket) {
|
|
68
62
|
socket.emit('login-success', { loginToken });
|
|
@@ -70,17 +64,17 @@ function setUpSamlAuth(bot, auth, clientName, nsp) {
|
|
|
70
64
|
else {
|
|
71
65
|
bot.logger.warn('Target socket not found', { socketId });
|
|
72
66
|
}
|
|
73
|
-
return res.send(
|
|
67
|
+
return res.send(renderAuthSuccessPage(lang));
|
|
74
68
|
}
|
|
75
69
|
catch (e) {
|
|
76
70
|
bot.logger.error('Callback handling failure:', e);
|
|
77
|
-
return res.status(500).send(
|
|
71
|
+
return res.status(500).send(renderAuthErrorPage(lang));
|
|
78
72
|
}
|
|
79
73
|
});
|
|
80
74
|
authenticatorFn(req, res, next);
|
|
81
75
|
});
|
|
82
76
|
}
|
|
83
|
-
function storeLoginRequestToken(loginRequestToken, socketId) {
|
|
77
|
+
export function storeLoginRequestToken(loginRequestToken, socketId) {
|
|
84
78
|
// associate socketId with loginRequestToken and store it in memory cache
|
|
85
79
|
loginTokenCache.set(loginRequestToken, { loginRequestToken, socketId });
|
|
86
80
|
}
|
|
@@ -97,14 +91,16 @@ function consumeLoginRequestToken(loginRequestToken) {
|
|
|
97
91
|
loginTokenCache.delete(loginRequestToken);
|
|
98
92
|
return { socketId: cachedLoginRequest.socketId };
|
|
99
93
|
}
|
|
100
|
-
function verifyLoginToken(token, auth, logger) {
|
|
94
|
+
export async function verifyLoginToken(token, auth, logger) {
|
|
101
95
|
try {
|
|
102
96
|
if (auth) {
|
|
103
|
-
const
|
|
97
|
+
const secret = new TextEncoder().encode(auth.jwtSecret);
|
|
98
|
+
const { payload } = await jwtVerify(token ?? '', secret);
|
|
99
|
+
const p = payload;
|
|
104
100
|
return {
|
|
105
|
-
email:
|
|
106
|
-
firstName:
|
|
107
|
-
lastName:
|
|
101
|
+
email: p.email ?? '',
|
|
102
|
+
firstName: p.firstName,
|
|
103
|
+
lastName: p.lastName,
|
|
108
104
|
};
|
|
109
105
|
}
|
|
110
106
|
else {
|
package/dist/auth/relay-state.js
CHANGED
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.signRelayState = signRelayState;
|
|
7
|
-
exports.verifyRelayState = verifyRelayState;
|
|
8
|
-
const crypto_1 = __importDefault(require("crypto"));
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
9
2
|
const STATE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
10
|
-
function signRelayState(rawToken, secret) {
|
|
3
|
+
export function signRelayState(rawToken, secret) {
|
|
11
4
|
if (!/^[\w-]{16,128}$/.test(rawToken))
|
|
12
5
|
throw new Error('bad token format');
|
|
13
6
|
const exp = Date.now() + STATE_TTL_MS;
|
|
14
7
|
const payload = `${rawToken}.${exp}`;
|
|
15
|
-
const mac =
|
|
16
|
-
.createHmac('sha256', secret)
|
|
17
|
-
.update(payload)
|
|
18
|
-
.digest('base64url');
|
|
8
|
+
const mac = createHmac('sha256', secret).update(payload).digest('base64url');
|
|
19
9
|
return Buffer.from(`${payload}.${mac}`, 'utf8').toString('base64url');
|
|
20
10
|
}
|
|
21
|
-
function verifyRelayState(stateB64, secret) {
|
|
11
|
+
export function verifyRelayState(stateB64, secret) {
|
|
22
12
|
const raw = Buffer.from(stateB64, 'base64url').toString('utf8');
|
|
23
13
|
const [token, expStr, mac] = raw.split('.');
|
|
24
14
|
if (!token || !expStr || !mac) {
|
|
@@ -28,11 +18,10 @@ function verifyRelayState(stateB64, secret) {
|
|
|
28
18
|
if (!Number.isFinite(exp) || exp < Date.now()) {
|
|
29
19
|
throw new Error('state expired');
|
|
30
20
|
}
|
|
31
|
-
const expected =
|
|
32
|
-
.createHmac('sha256', secret)
|
|
21
|
+
const expected = createHmac('sha256', secret)
|
|
33
22
|
.update(`${token}.${exp}`)
|
|
34
23
|
.digest('base64url');
|
|
35
|
-
if (!
|
|
24
|
+
if (!timingSafeEqual(Buffer.from(mac), Buffer.from(expected))) {
|
|
36
25
|
throw new Error('state tampered');
|
|
37
26
|
}
|
|
38
27
|
return token;
|
package/dist/auth/ttl-cache.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TtlCache = void 0;
|
|
4
|
-
class TtlCache {
|
|
1
|
+
export class TtlCache {
|
|
5
2
|
data = new Map();
|
|
6
3
|
timers = new Map();
|
|
7
4
|
defaultTtlMs;
|
|
@@ -49,4 +46,3 @@ class TtlCache {
|
|
|
49
46
|
this.data.clear();
|
|
50
47
|
}
|
|
51
48
|
}
|
|
52
|
-
exports.TtlCache = TtlCache;
|
package/dist/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Environment, SessionInfo } from '@botfabrik/engine-domain';
|
|
2
2
|
import type { ParsedUrlQuery } from 'node:querystring';
|
|
3
3
|
import type { Socket } from 'socket.io';
|
|
4
|
-
import { AuthenticatedUser } from './auth';
|
|
5
|
-
import type { SessionInfoClientPayload, SessionInfoUserPayload, WebClientProps } from './types';
|
|
4
|
+
import { AuthenticatedUser } from './auth/index.js';
|
|
5
|
+
import type { SessionInfoClientPayload, SessionInfoUserPayload, WebClientProps } from './types.js';
|
|
6
6
|
declare const createSessionInfo: (socket: Socket, clientName: string, environment: Environment, sessionInfo: SessionInfo<SessionInfoClientPayload, SessionInfoUserPayload>, userId: string, locale: string | undefined, querystrings: ParsedUrlQuery, authenticatedUser: AuthenticatedUser | undefined, props: WebClientProps) => () => Promise<SessionInfo<SessionInfoClientPayload, SessionInfoUserPayload>>;
|
|
7
7
|
export default createSessionInfo;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const constants_1 = require("./constants");
|
|
1
|
+
import { CLIENT_TYPE } from './constants.js';
|
|
4
2
|
const createSessionInfoBase = async (userId, querystrings, headers, clientName, environment, sessionInfo, locale, authenticatedUser, props) => {
|
|
5
3
|
const client = {
|
|
6
4
|
...sessionInfo.client,
|
|
7
5
|
name: clientName,
|
|
8
|
-
type:
|
|
6
|
+
type: CLIENT_TYPE,
|
|
9
7
|
payload: {
|
|
10
8
|
...sessionInfo.client.payload,
|
|
11
9
|
referrer: decodeURIComponent(querystrings['referrer'] || headers['referer'] || 'unknown'),
|
|
@@ -63,4 +61,4 @@ const capitalize = (s) => {
|
|
|
63
61
|
return '';
|
|
64
62
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
65
63
|
};
|
|
66
|
-
|
|
64
|
+
export default createSessionInfo;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const globals_1 = require("@jest/globals");
|
|
7
|
-
const createSessionInfo_1 = __importDefault(require("./createSessionInfo"));
|
|
8
|
-
(0, globals_1.describe)('create session info', () => {
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import createSessionInfo from './createSessionInfo.js';
|
|
3
|
+
describe('create session info', () => {
|
|
9
4
|
const querystrings = {};
|
|
10
5
|
const headers = {
|
|
11
6
|
host: 'fluance-chatbot.scapp.io',
|
|
@@ -42,19 +37,19 @@ const createSessionInfo_1 = __importDefault(require("./createSessionInfo"));
|
|
|
42
37
|
contexts: [],
|
|
43
38
|
environment: 'PROD',
|
|
44
39
|
};
|
|
45
|
-
|
|
46
|
-
const sessionInfo = await (
|
|
40
|
+
it('without user payload', async () => {
|
|
41
|
+
const sessionInfo = await createSessionInfo(socket, 'my-client', 'TEST', defaultSessionInfo, 'my-user-id', 'de_DE', { email: 'hans@example.com' }, undefined, {})();
|
|
47
42
|
// client
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
expect(sessionInfo.client.name).toBe('my-client');
|
|
44
|
+
expect(sessionInfo.client.type).toBe('webclient');
|
|
45
|
+
expect(sessionInfo.client.payload.querystrings.email).toBe('hans@example.com');
|
|
51
46
|
// user
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
expect(sessionInfo.user.id).toBe('my-user-id');
|
|
48
|
+
expect(sessionInfo.user.displayName).not.toBeDefined();
|
|
49
|
+
expect(sessionInfo.user.locale).toBe('de_DE');
|
|
50
|
+
expect(sessionInfo.user.payload).toStrictEqual({});
|
|
56
51
|
});
|
|
57
|
-
|
|
52
|
+
it('with enhanced user data', async () => {
|
|
58
53
|
const requestUserInfos = async () => {
|
|
59
54
|
const userProfile = {
|
|
60
55
|
username: 'hans.muster',
|
|
@@ -71,49 +66,49 @@ const createSessionInfo_1 = __importDefault(require("./createSessionInfo"));
|
|
|
71
66
|
const props = {
|
|
72
67
|
requestUserInfos,
|
|
73
68
|
};
|
|
74
|
-
const sessionInfo = await (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
69
|
+
const sessionInfo = await createSessionInfo(socket, 'my-client', 'TEST', defaultSessionInfo, 'my-user-id', 'de_DE', {}, undefined, props)();
|
|
70
|
+
expect(sessionInfo.user.id).toBe('hans.muster@PRIMARY');
|
|
71
|
+
expect(sessionInfo.user.displayName).toBe('Hans Muster');
|
|
72
|
+
expect(sessionInfo.user.payload.username).toBe('hans.muster');
|
|
73
|
+
expect(sessionInfo.user.payload.firstName).toBe('Hans');
|
|
74
|
+
expect(sessionInfo.user.payload.lastName).toBe('Muster');
|
|
80
75
|
});
|
|
81
|
-
|
|
76
|
+
it('with undefined enhanced user data', async () => {
|
|
82
77
|
const requestUserInfos = async () => {
|
|
83
78
|
return {};
|
|
84
79
|
};
|
|
85
80
|
const props = {
|
|
86
81
|
requestUserInfos,
|
|
87
82
|
};
|
|
88
|
-
const sessionInfo = await (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
83
|
+
const sessionInfo = await createSessionInfo(socket, 'my-client', 'TEST', defaultSessionInfo, 'my-user-id', 'de_DE', {}, undefined, props)();
|
|
84
|
+
expect(sessionInfo.user.id).toBe('my-user-id');
|
|
85
|
+
expect(sessionInfo.user.displayName).not.toBeDefined();
|
|
86
|
+
expect(sessionInfo.user.locale).toBe('de_DE');
|
|
87
|
+
expect(sessionInfo.user.payload).toStrictEqual({});
|
|
88
|
+
expect(sessionInfo.environment).toBe('TEST');
|
|
94
89
|
});
|
|
95
|
-
|
|
96
|
-
const sessionInfo = await (
|
|
90
|
+
it('with authenticated user', async () => {
|
|
91
|
+
const sessionInfo = await createSessionInfo(socket, 'my-client', 'TEST', defaultSessionInfo, 'my-user-id', 'de_DE', {}, {
|
|
97
92
|
email: 'hans.muster@example.com',
|
|
98
93
|
firstName: 'Hans',
|
|
99
94
|
lastName: 'Muster',
|
|
100
95
|
}, {})();
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
96
|
+
expect(sessionInfo.user.id).toBe('my-user-id');
|
|
97
|
+
expect(sessionInfo.user.displayName).toBe('Hans Muster');
|
|
98
|
+
expect(sessionInfo.user.firstName).toBe('Hans');
|
|
99
|
+
expect(sessionInfo.user.lastName).toBe('Muster');
|
|
100
|
+
expect(sessionInfo.user.email).toBe('hans.muster@example.com');
|
|
106
101
|
});
|
|
107
|
-
|
|
108
|
-
const sessionInfo = await (
|
|
102
|
+
it('with authenticated user and guessed name', async () => {
|
|
103
|
+
const sessionInfo = await createSessionInfo(socket, 'my-client', 'TEST', defaultSessionInfo, 'my-user-id', 'de_DE', {}, {
|
|
109
104
|
email: 'hans.muster@example.com',
|
|
110
105
|
firstName: undefined,
|
|
111
106
|
lastName: undefined,
|
|
112
107
|
}, {})();
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
expect(sessionInfo.user.id).toBe('my-user-id');
|
|
109
|
+
expect(sessionInfo.user.displayName).toBe('Hans Muster');
|
|
110
|
+
expect(sessionInfo.user.firstName).toBe('Hans');
|
|
111
|
+
expect(sessionInfo.user.lastName).toBe('Muster');
|
|
112
|
+
expect(sessionInfo.user.email).toBe('hans.muster@example.com');
|
|
118
113
|
});
|
|
119
114
|
});
|
package/dist/extractLocale.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const accept_language_parser_1 = require("accept-language-parser");
|
|
1
|
+
import { parse } from 'accept-language-parser';
|
|
4
2
|
const extractLocale = (parsedUrlQuery, acceptLanguage = '') => {
|
|
5
3
|
let locale = undefined;
|
|
6
4
|
if (parsedUrlQuery['lang']) {
|
|
7
5
|
locale = parsedUrlQuery['lang'];
|
|
8
6
|
}
|
|
9
7
|
else {
|
|
10
|
-
const languages =
|
|
8
|
+
const languages = parse(acceptLanguage);
|
|
11
9
|
if (languages !== undefined && languages[0]) {
|
|
12
10
|
const code = languages[0].code;
|
|
13
11
|
const region = languages[0].region;
|
|
@@ -16,4 +14,4 @@ const extractLocale = (parsedUrlQuery, acceptLanguage = '') => {
|
|
|
16
14
|
}
|
|
17
15
|
return locale;
|
|
18
16
|
};
|
|
19
|
-
|
|
17
|
+
export default extractLocale;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,25 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const globals_1 = require("@jest/globals");
|
|
7
|
-
const extractLocale_1 = __importDefault(require("./extractLocale"));
|
|
8
|
-
(0, globals_1.describe)('should extract locale from accept-language header', () => {
|
|
9
|
-
(0, globals_1.it)('undefined header value', () => {
|
|
10
|
-
(0, globals_1.expect)((0, extractLocale_1.default)({}, undefined)).toBe(undefined);
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import extractLocale from './extractLocale.js';
|
|
3
|
+
describe('should extract locale from accept-language header', () => {
|
|
4
|
+
it('undefined header value', () => {
|
|
5
|
+
expect(extractLocale({}, undefined)).toBe(undefined);
|
|
11
6
|
});
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
it('lang via query parameer', () => {
|
|
8
|
+
expect(extractLocale({ lang: 'de' }, '')).toBe('de');
|
|
14
9
|
});
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
it('empty header value', () => {
|
|
11
|
+
expect(extractLocale({}, '')).toBe(undefined);
|
|
17
12
|
});
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
it('header value with one value', () => {
|
|
14
|
+
expect(extractLocale({}, 'de-CH')).toBe('de_CH');
|
|
20
15
|
});
|
|
21
|
-
|
|
16
|
+
it('header value with several values', () => {
|
|
22
17
|
const header = 'de-CH,de;q=0.8,en-US;q=0.6,en;q=0.4';
|
|
23
|
-
|
|
18
|
+
expect(extractLocale({}, header)).toBe('de_CH');
|
|
24
19
|
});
|
|
25
20
|
});
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
1
|
const getSupportedClientLocale = (userLocale) => {
|
|
4
2
|
// Prüfe, ob der Webclient für diese Sprache eine Übersetzung hat
|
|
5
3
|
const supportedLocales = ['de', 'en', 'fr', 'it'];
|
|
@@ -15,4 +13,4 @@ const getSupportedClientLocale = (userLocale) => {
|
|
|
15
13
|
// default locale
|
|
16
14
|
return 'de';
|
|
17
15
|
};
|
|
18
|
-
|
|
16
|
+
export default getSupportedClientLocale;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,23 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
(0, globals_1.expect)((0, getSupportedClientLocale_1.default)('en')).toBe('en');
|
|
13
|
-
(0, globals_1.expect)((0, getSupportedClientLocale_1.default)('en-us')).toBe('en');
|
|
14
|
-
(0, globals_1.expect)((0, getSupportedClientLocale_1.default)('fr')).toBe('fr');
|
|
15
|
-
(0, globals_1.expect)((0, getSupportedClientLocale_1.default)('fr-ch')).toBe('fr');
|
|
16
|
-
(0, globals_1.expect)((0, getSupportedClientLocale_1.default)('it')).toBe('it');
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import getSupportedClientLocale from './getSupportedClientLocale.js';
|
|
3
|
+
describe('get client locale', () => {
|
|
4
|
+
it('should return supported client language', () => {
|
|
5
|
+
expect(getSupportedClientLocale('de')).toBe('de');
|
|
6
|
+
expect(getSupportedClientLocale('de-ch')).toBe('de');
|
|
7
|
+
expect(getSupportedClientLocale('en')).toBe('en');
|
|
8
|
+
expect(getSupportedClientLocale('en-us')).toBe('en');
|
|
9
|
+
expect(getSupportedClientLocale('fr')).toBe('fr');
|
|
10
|
+
expect(getSupportedClientLocale('fr-ch')).toBe('fr');
|
|
11
|
+
expect(getSupportedClientLocale('it')).toBe('it');
|
|
17
12
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
it('should return "de" as default locale', () => {
|
|
14
|
+
expect(getSupportedClientLocale('ru')).toBe('de');
|
|
15
|
+
expect(getSupportedClientLocale('ru-de')).toBe('de');
|
|
16
|
+
expect(getSupportedClientLocale('sp')).toBe('de');
|
|
22
17
|
});
|
|
23
18
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Client, type Environment } from '@botfabrik/engine-domain';
|
|
2
|
-
import { type WebclientMiddlewareState, type WebClientProps } from './types';
|
|
3
|
-
export * from './types';
|
|
2
|
+
import { type WebclientMiddlewareState, type WebClientProps } from './types.js';
|
|
3
|
+
export * from './types.js';
|
|
4
4
|
declare const _default: (clientName: string, environment: Environment, props: WebClientProps) => Client<WebclientMiddlewareState>;
|
|
5
5
|
export default _default;
|