@botfabrik/engine-webclient 4.121.7 → 4.122.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/README.md CHANGED
@@ -135,18 +135,27 @@ Beispiel
135
135
 
136
136
  Eine Liste von projektspezifischen Einträgen, die im Chat-Menü angezeigt werden sollen.
137
137
 
138
- ### requestUserInfos (optional)
138
+ ### enrichUserInfo (optional)
139
139
 
140
140
  Diese Funktion wird beim Erzeugen eines neuen Session Records aufgerufen. Man hat damit die Möglichkeit, die Session Daten unter `session.getSessionInfo().user` projektspezifisch anzupassen.
141
141
 
142
- Die Funktion erhält als Parameter sämtliche Querystrings in Form eines JSON Objektes. Als Resultat wird wiederum ein JSON Objekt erwartet. Alle Werte unter `session.getSessionInfo().user` werden damit überschrieben.
142
+ Die Funktion erhält als Parameter ein Objekt mit folgenden Eigenschaften:
143
+
144
+ - `user` – Das aktuelle Session-User-Objekt, aufgebaut aus Authentifizierung und Query-Parametern.
145
+ - `querystrings` – Die geparsten Query-Parameter der Client-URL.
146
+ - `headers` – Die eingehenden HTTP-Request-Header.
147
+ - `ip` – Die IP-Adresse des Clients (oder `undefined`).
148
+
149
+ Als Resultat wird ein partielles User-Info-Objekt erwartet. Dessen Eigenschaften werden in die bestehenden User-Daten eingemergt.
143
150
 
144
151
  Beispiel:
145
152
 
146
153
  ```typescript
147
- export default async (querystrings: any): Promise<any> => {
154
+ const enrichUserInfo: EnrichUserInfo = async ({ querystrings }) => {
148
155
  const accessToken = querystrings.accessToken;
149
- const userProfile: UserProfile = await fetchUserProfile(accessToken);
156
+ const userProfile: UserProfile = await fetchUserProfile(
157
+ accessToken as string
158
+ );
150
159
 
151
160
  return {
152
161
  id: userProfile.username + '@' + userProfile.domain,
@@ -1,12 +1,16 @@
1
1
  import { CLIENT_TYPE } from './constants.js';
2
- const createSessionInfoBase = async (userId, querystrings, headers, clientName, environment, sessionInfo, locale, authenticatedUser, props) => {
2
+ const createSessionInfo = (socket, clientName, environment, sessionInfo, userId, locale, querystrings, authenticatedUser, props) => async () => {
3
+ const headers = socket.request.headers;
4
+ const ip = extractIpFromSocket(socket);
3
5
  const client = {
4
6
  ...sessionInfo.client,
5
7
  name: clientName,
6
8
  type: CLIENT_TYPE,
7
9
  payload: {
8
10
  ...sessionInfo.client.payload,
9
- referrer: decodeURIComponent(querystrings['referrer'] || headers['referer'] || 'unknown'),
11
+ referrer: decodeURIComponent(querystrings['referrer'] ||
12
+ headers['referer'] ||
13
+ 'unknown'),
10
14
  querystrings,
11
15
  },
12
16
  };
@@ -29,17 +33,17 @@ const createSessionInfoBase = async (userId, querystrings, headers, clientName,
29
33
  locale,
30
34
  };
31
35
  const contexts = sessionInfo.contexts;
32
- // enhance user infos
33
- if (props.requestUserInfos) {
34
- const userInfos = await props.requestUserInfos(querystrings);
35
- user = Object.assign({}, user, userInfos);
36
+ if (props.enrichUserInfo) {
37
+ const userInfos = await props.enrichUserInfo({
38
+ user,
39
+ querystrings,
40
+ headers,
41
+ ip,
42
+ });
43
+ user = { ...user, ...userInfos };
36
44
  }
37
45
  return { client, user, contexts, environment };
38
46
  };
39
- const createSessionInfo = (socket, clientName, environment, sessionInfo, userId, locale, querystrings, authenticatedUser, props) => async () => {
40
- const request = socket.request;
41
- return await createSessionInfoBase(userId, querystrings, request.headers, clientName, environment, sessionInfo, locale, authenticatedUser, props);
42
- };
43
47
  const guestNamesFromEMail = (email) => {
44
48
  try {
45
49
  const namePart = email?.split('@')[0];
@@ -61,4 +65,15 @@ const capitalize = (s) => {
61
65
  return '';
62
66
  return s.charAt(0).toUpperCase() + s.slice(1);
63
67
  };
68
+ const extractIpFromSocket = (socket) => {
69
+ const headers = socket.handshake.headers;
70
+ const forwarded = headers['x-forwarded-for'];
71
+ if (typeof forwarded === 'string') {
72
+ return forwarded.split(',')[0]?.trim();
73
+ }
74
+ if (Array.isArray(forwarded) && forwarded.length > 0) {
75
+ return forwarded[0];
76
+ }
77
+ return socket.handshake.address;
78
+ };
64
79
  export default createSessionInfo;
@@ -22,6 +22,7 @@ describe('create session info', () => {
22
22
  },
23
23
  handshake: {
24
24
  query: querystrings,
25
+ headers,
25
26
  },
26
27
  };
27
28
  const webClientProps = {
@@ -54,7 +55,7 @@ describe('create session info', () => {
54
55
  expect(sessionInfo.user.payload).toStrictEqual({});
55
56
  });
56
57
  it('with enhanced user data', async () => {
57
- const requestUserInfos = async () => {
58
+ const enrichUserInfo = async () => {
58
59
  const userProfile = {
59
60
  username: 'hans.muster',
60
61
  domain: 'PRIMARY',
@@ -69,7 +70,7 @@ describe('create session info', () => {
69
70
  };
70
71
  const props = {
71
72
  ...webClientProps,
72
- requestUserInfos,
73
+ enrichUserInfo,
73
74
  };
74
75
  const sessionInfo = await createSessionInfo(socket, 'my-client', 'TEST', defaultSessionInfo, 'my-user-id', 'de_DE', {}, undefined, props)();
75
76
  expect(sessionInfo.user.id).toBe('hans.muster@PRIMARY');
package/dist/types.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import type { Action, SessionInfoUser } from '@botfabrik/engine-domain';
2
+ import { IncomingHttpHeaders } from 'node:http';
3
+ import { ParsedUrlQuery } from 'node:querystring';
2
4
  export type SessionInfoClientPayload = {
3
5
  querystrings: any;
4
6
  referrer: string;
@@ -10,9 +12,26 @@ export type ConversationRating = {
10
12
  };
11
13
  export type SessionInfoUserPayload = object;
12
14
  export type WebclientMiddlewareState = object;
13
- export type RequestUserInfos = (querystrings: any) => Promise<Partial<SessionInfoUser<SessionInfoUserPayload>>>;
15
+ /**
16
+ * Callback to enrich the user's session info with additional data from an external source.
17
+ *
18
+ * Called during session initialization with the current user, query strings, and HTTP headers.
19
+ * The returned partial object is merged into the existing user info.
20
+ *
21
+ * @param props.user - The current session user object built from authentication and query parameters.
22
+ * @param props.querystrings - The parsed query string parameters from the client URL.
23
+ * @param props.headers - The incoming HTTP request headers.
24
+ * @param props.ip - The client's IP address, extracted from the socket connection.
25
+ * @returns A promise resolving to a partial user info object whose properties will be merged into the session user.
26
+ */
27
+ export type EnrichUserInfo = (props: {
28
+ user: SessionInfoUser<SessionInfoUserPayload>;
29
+ querystrings: ParsedUrlQuery;
30
+ headers: IncomingHttpHeaders;
31
+ ip: string | undefined;
32
+ }) => Promise<Partial<SessionInfoUser<SessionInfoUserPayload>>>;
14
33
  export type RequestSessionRecordQuery = (params: {
15
- querystrings: any;
34
+ querystrings: ParsedUrlQuery;
16
35
  sessionId: string;
17
36
  }) => Promise<Record<string, unknown>>;
18
37
  export type MenuItemType = 'link' | 'action';
@@ -87,7 +106,11 @@ export type Auth = {
87
106
  };
88
107
  export interface WebClientProps {
89
108
  getStartedAction?: Action;
90
- requestUserInfos?: RequestUserInfos;
109
+ /**
110
+ * Optional callback to enrich the user's session info with additional data.
111
+ * See {@link EnrichUserInfo} for details.
112
+ */
113
+ enrichUserInfo?: EnrichUserInfo;
91
114
  requestSessionRecordQuery?: RequestSessionRecordQuery;
92
115
  speech?: SpeechToTextProps | undefined;
93
116
  expandChatWindowAtStart?: Devices;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botfabrik/engine-webclient",
3
- "version": "4.121.7",
3
+ "version": "4.122.0",
4
4
  "description": "Webclient for Botfabriks Bot Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -55,5 +55,5 @@
55
55
  "tsx": "^4.22.3",
56
56
  "typescript": "6.0.3"
57
57
  },
58
- "gitHead": "35ddae6f6e337b775ebde3567f67d3878fde1dc1"
58
+ "gitHead": "a44d1eec77571e783f80f244474873f286a3e83b"
59
59
  }