@c-rex/services 0.1.3 → 0.1.4

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/index.mjs CHANGED
@@ -2,6 +2,14 @@
2
2
  import axios from "axios";
3
3
 
4
4
  // ../constants/src/index.ts
5
+ var ALL = "*";
6
+ var LOG_LEVELS = {
7
+ critical: 2,
8
+ error: 3,
9
+ warning: 4,
10
+ info: 6,
11
+ debug: 7
12
+ };
5
13
  var API = {
6
14
  MAX_RETRY: 3,
7
15
  API_TIMEOUT: 1e4,
@@ -9,6 +17,7 @@ var API = {
9
17
  "content-Type": "application/json"
10
18
  }
11
19
  };
20
+ var SDK_CONFIG_KEY = "crex-sdk-config";
12
21
  var FLAGS_BY_LANG = {
13
22
  "en": "US",
14
23
  "de": "DE"
@@ -25,187 +34,142 @@ var RESULT_TYPES = {
25
34
  var DEFAULT_COOKIE_LIMIT = 7 * 24 * 60 * 60 * 1e3;
26
35
  var CREX_TOKEN_HEADER_KEY = "crex-token";
27
36
 
28
- // ../utils/src/utils.ts
29
- var call = async (method, params) => {
30
- try {
31
- const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/rpc`, {
32
- method: "POST",
33
- headers: { "Content-Type": "application/json" },
34
- body: JSON.stringify({ method, params }),
35
- credentials: "include"
36
- });
37
- const json = await res.json();
38
- if (!res.ok) throw new Error(json.error || "Unknown error");
39
- return json.data;
40
- } catch (error) {
41
- console.error(error);
42
- return null;
43
- }
44
- };
45
- var getCountryCodeByLang = (lang) => {
46
- const mappedKeys = Object.keys(FLAGS_BY_LANG);
47
- if (!mappedKeys.includes(lang)) {
48
- return lang;
49
- }
50
- const country = FLAGS_BY_LANG[lang];
51
- return country;
52
- };
37
+ // ../core/src/requests.ts
38
+ import { cookies } from "next/headers";
39
+
40
+ // ../core/src/logger.ts
41
+ import winston from "winston";
53
42
 
54
- // ../utils/src/memory.ts
55
- var getCookie = async (key) => {
56
- const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/cookies?key=${key}`);
57
- if (!res.ok) {
58
- return { key, value: null };
43
+ // ../core/src/transports/matomo.ts
44
+ import Transport from "winston-transport";
45
+ var MatomoTransport = class extends Transport {
46
+ matomoTransport;
47
+ configs;
48
+ /**
49
+ * Creates a new instance of MatomoTransport.
50
+ *
51
+ * @param configs - The application configuration containing logging settings
52
+ */
53
+ constructor(configs) {
54
+ super({
55
+ level: configs.logs.matomo.minimumLevel,
56
+ silent: configs.logs.matomo.silent
57
+ });
58
+ this.matomoTransport = new Transport();
59
+ this.configs = configs;
59
60
  }
60
- const json = await res.json();
61
- return json;
62
- };
63
- var setCookie = async (key, value, maxAge) => {
64
- try {
65
- if (maxAge === void 0) {
66
- maxAge = DEFAULT_COOKIE_LIMIT;
61
+ /**
62
+ * Logs a message to Matomo if the message category is included in the configured categories.
63
+ *
64
+ * @param info - The log information including level, message, and category
65
+ * @param callback - Callback function to execute after logging
66
+ */
67
+ log(info, callback) {
68
+ const matomoCategory = this.configs.logs.matomo.categoriesLevel;
69
+ if (matomoCategory.includes(info.category) || matomoCategory.includes(ALL)) {
70
+ this.matomoTransport.log(info, callback);
67
71
  }
68
- await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/cookies`, {
69
- method: "POST",
70
- credentials: "include",
71
- body: JSON.stringify({
72
- key,
73
- value,
74
- maxAge
75
- })
76
- });
77
- } catch (error) {
78
- call("CrexLogger.log", {
79
- level: "error",
80
- message: `utils.setCookie error: ${error}`
81
- });
82
72
  }
83
73
  };
84
74
 
85
- // ../utils/src/classMerge.ts
86
- import { clsx } from "clsx";
87
- import { twMerge } from "tailwind-merge";
88
-
89
- // ../utils/src/params.ts
90
- var createParams = (fieldsList, key = "Fields") => fieldsList.map((item) => ({
91
- key,
92
- value: item
93
- }));
94
- var generateQueryParams = (params) => {
95
- const queryParams = params.map(
96
- (param) => `${encodeURIComponent(param.key)}=${encodeURIComponent(param.value)}`
97
- ).join("&");
98
- return queryParams;
99
- };
100
-
101
- // ../utils/src/token.ts
102
- var updateToken = async () => {
103
- try {
104
- const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/token`, {
105
- method: "POST",
106
- credentials: "include"
75
+ // ../core/src/transports/graylog.ts
76
+ import Transport2 from "winston-transport";
77
+ import Graylog2Transport from "winston-graylog2";
78
+ var GraylogTransport = class extends Transport2 {
79
+ graylogTransport;
80
+ configs;
81
+ /**
82
+ * Creates a new instance of GraylogTransport.
83
+ *
84
+ * @param configs - The application configuration containing logging settings
85
+ */
86
+ constructor(configs) {
87
+ super({
88
+ level: configs.logs.graylog.minimumLevel,
89
+ silent: configs.logs.graylog.silent
107
90
  });
108
- const cookies = response.headers.get("set-cookie");
109
- if (cookies === null) return null;
110
- const aux = cookies.split(`${CREX_TOKEN_HEADER_KEY}=`);
111
- if (aux.length == 0) return null;
112
- const token = aux[1].split(";")[0];
113
- if (token === void 0) throw new Error("Token is undefined");
114
- return token;
115
- } catch (error) {
116
- call("CrexLogger.log", {
117
- level: "error",
118
- message: `utils.updateToken error: ${error}`
91
+ this.configs = configs;
92
+ this.graylogTransport = new Graylog2Transport({
93
+ name: "crex.net.documentation",
94
+ //name: "crex.net.blog",
95
+ silent: false,
96
+ handleExceptions: false,
97
+ graylog: {
98
+ servers: [
99
+ { host: "localhost", port: 12201 },
100
+ { host: "https://log.c-rex.net", port: 12202 }
101
+ //TODO: check the URL => https://log.c-rex.net:12202/gelf" GELF??
102
+ ]
103
+ }
119
104
  });
120
- return null;
121
105
  }
122
- };
123
- var manageToken = async () => {
124
- try {
125
- const hasToken = await getCookie(CREX_TOKEN_HEADER_KEY);
126
- let token = hasToken.value;
127
- if (hasToken.value === null) {
128
- const tokenResult = await updateToken();
129
- if (tokenResult === null) throw new Error("Token is undefined");
130
- token = tokenResult;
106
+ /**
107
+ * Logs a message to Graylog if the message category is included in the configured categories.
108
+ *
109
+ * @param info - The log information including level, message, and category
110
+ * @param callback - Callback function to execute after logging
111
+ */
112
+ log(info, callback) {
113
+ const graylogCategory = this.configs.logs.graylog.categoriesLevel;
114
+ if (graylogCategory.includes(info.category) || graylogCategory.includes(ALL)) {
115
+ this.graylogTransport.log(info, callback);
131
116
  }
132
- return token;
133
- } catch (error) {
134
- call("CrexLogger.log", {
135
- level: "error",
136
- message: `utils.manageToken error: ${error}`
137
- });
138
- return null;
139
117
  }
140
118
  };
141
119
 
142
- // ../core/src/requests.ts
120
+ // ../core/src/logger.ts
143
121
  import { getConfigs } from "@c-rex/utils/next-cookies";
144
-
145
- // ../core/src/cache.ts
146
- var CrexCache = class {
147
- /**
148
- * Retrieves a value from the cache by key.
149
- *
150
- * @param key - The cache key to retrieve
151
- * @returns The cached value as a string, or null if not found
152
- */
153
- async get(key) {
154
- const cookie = await getCookie(key);
155
- return cookie.value;
156
- }
122
+ var CrexLogger = class {
123
+ customerConfig;
124
+ logger;
157
125
  /**
158
- * Stores a value in the cache with the specified key.
159
- * Checks if the value size is within cookie size limits (4KB).
126
+ * Initializes the logger instance if it hasn't been initialized yet.
127
+ * Loads customer configuration and creates the logger with appropriate transports.
160
128
  *
161
- * @param key - The cache key
162
- * @param value - The value to store
129
+ * @private
163
130
  */
164
- async set(key, value) {
131
+ async initLogger() {
165
132
  try {
166
- const byteLength = new TextEncoder().encode(value).length;
167
- if (byteLength <= 4096) {
168
- await setCookie(key, value);
169
- } else {
170
- console.warn(`Cookie ${key} value is too large to be stored in a single cookie`);
133
+ if (!this.customerConfig) {
134
+ this.customerConfig = await getConfigs();
135
+ }
136
+ if (!this.logger) {
137
+ this.logger = this.createLogger();
171
138
  }
172
139
  } catch (error) {
173
- call("CrexLogger.log", {
174
- level: "error",
175
- message: `CrexCache.set error: ${error}`
176
- });
140
+ console.error("Error initializing logger:", error);
177
141
  }
178
142
  }
179
143
  /**
180
- * Generates a cache key based on request parameters.
181
- * Combines URL, method, and optionally params, body, and headers into a single string key.
144
+ * Logs a message with the specified level and optional category.
182
145
  *
183
- * @param options - Request options to generate key from
184
- * @param options.url - The request URL
185
- * @param options.method - The HTTP method
186
- * @param options.body - Optional request body
187
- * @param options.params - Optional query parameters
188
- * @param options.headers - Optional request headers
189
- * @returns A string cache key
146
+ * @param options - Logging options
147
+ * @param options.level - The log level (error, warn, info, etc.)
148
+ * @param options.message - The message to log
149
+ * @param options.category - Optional category for the log message
190
150
  */
191
- generateCacheKey({
192
- url,
193
- method,
194
- body,
195
- params,
196
- headers
197
- }) {
198
- let cacheKey = `${url}-${method}`;
199
- if (params !== void 0 && Object.keys(params).length > 0) {
200
- cacheKey += `-${JSON.stringify(params)}`;
201
- }
202
- if (body !== void 0 && Object.keys(body).length > 0) {
203
- cacheKey += `-${JSON.stringify(body)}`;
204
- }
205
- if (headers !== void 0 && Object.keys(headers).length > 0) {
206
- cacheKey += `-${JSON.stringify(headers)}`;
207
- }
208
- return cacheKey;
151
+ async log({ level, message, category }) {
152
+ await this.initLogger();
153
+ this.logger.log(level, message, category);
154
+ }
155
+ /**
156
+ * Creates a new Winston logger instance with configured transports.
157
+ *
158
+ * @private
159
+ * @returns A configured Winston logger instance
160
+ */
161
+ createLogger() {
162
+ return winston.createLogger({
163
+ levels: LOG_LEVELS,
164
+ transports: [
165
+ new winston.transports.Console({
166
+ level: this.customerConfig.logs.console.minimumLevel,
167
+ silent: this.customerConfig.logs.console.silent
168
+ }),
169
+ new MatomoTransport(this.customerConfig),
170
+ new GraylogTransport(this.customerConfig)
171
+ ]
172
+ });
209
173
  }
210
174
  };
211
175
 
@@ -213,24 +177,67 @@ var CrexCache = class {
213
177
  var CrexApi = class {
214
178
  customerConfig;
215
179
  apiClient;
216
- cache;
180
+ logger;
217
181
  /**
218
182
  * Initializes the API client if it hasn't been initialized yet.
219
- * Loads customer configuration, creates the axios instance, and initializes the cache.
183
+ * Loads customer configuration, creates the axios instance, and initializes the logger.
220
184
  *
221
185
  * @private
222
186
  */
223
187
  async initAPI() {
188
+ this.logger = new CrexLogger();
224
189
  if (!this.customerConfig) {
225
- this.customerConfig = await getConfigs();
190
+ const aux = cookies().get(SDK_CONFIG_KEY);
191
+ if (aux != void 0) {
192
+ this.customerConfig = JSON.parse(aux.value);
193
+ } else {
194
+ this.logger.log({
195
+ level: "error",
196
+ message: `utils.initAPI error: Config cookie not available`
197
+ });
198
+ throw new Error("Config cookie not available");
199
+ }
226
200
  }
227
201
  if (!this.apiClient) {
228
202
  this.apiClient = axios.create({
229
203
  baseURL: this.customerConfig.baseUrl
230
204
  });
231
205
  }
232
- if (!this.cache) {
233
- this.cache = new CrexCache();
206
+ }
207
+ async getToken() {
208
+ try {
209
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/token`, {
210
+ method: "POST",
211
+ credentials: "include"
212
+ });
213
+ const { token } = await response.json();
214
+ return token;
215
+ } catch (error) {
216
+ this.logger.log({
217
+ level: "error",
218
+ message: `utils.getToken error: ${error}`
219
+ });
220
+ throw error;
221
+ }
222
+ }
223
+ async manageToken() {
224
+ try {
225
+ let token = "";
226
+ const hasToken = cookies().get(CREX_TOKEN_HEADER_KEY);
227
+ if (hasToken == void 0 || hasToken.value === null) {
228
+ const tokenResult = await this.getToken();
229
+ if (tokenResult === null) throw new Error("Token is undefined");
230
+ token = tokenResult;
231
+ } else {
232
+ token = hasToken.value;
233
+ }
234
+ return token;
235
+ } catch (error) {
236
+ this.logger.log({
237
+ level: "error",
238
+ message: `utils.manageToken error: ${error}`
239
+ });
240
+ throw error;
234
241
  }
235
242
  }
236
243
  /**
@@ -255,7 +262,7 @@ var CrexApi = class {
255
262
  await this.initAPI();
256
263
  let response = void 0;
257
264
  if (this.customerConfig.OIDC.client.enabled) {
258
- const token = await manageToken();
265
+ const token = await this.manageToken();
259
266
  headers = {
260
267
  ...headers,
261
268
  Authorization: `Bearer ${token}`
@@ -273,7 +280,7 @@ var CrexApi = class {
273
280
  });
274
281
  break;
275
282
  } catch (error) {
276
- call("CrexLogger.log", {
283
+ this.logger.log({
277
284
  level: "error",
278
285
  message: `API.execute ${retry + 1}\xBA error when request ${url}. Error: ${error}`
279
286
  });
@@ -289,6 +296,106 @@ var CrexApi = class {
289
296
  }
290
297
  };
291
298
 
299
+ // ../utils/src/utils.ts
300
+ var _generateShaKey = async (input) => {
301
+ const encoder = new TextEncoder();
302
+ const data = encoder.encode(input);
303
+ const hashBuffer = await crypto.subtle.digest("SHA-1", data);
304
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
305
+ const base64url = btoa(String.fromCharCode(...hashArray)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
306
+ return base64url.slice(0, 12);
307
+ };
308
+ var getCountryCodeByLang = (lang) => {
309
+ const mappedKeys = Object.keys(FLAGS_BY_LANG);
310
+ if (!mappedKeys.includes(lang)) {
311
+ return lang;
312
+ }
313
+ const country = FLAGS_BY_LANG[lang];
314
+ return country;
315
+ };
316
+
317
+ // ../utils/src/call.ts
318
+ var call = async (method, params) => {
319
+ const shaKey = await _generateShaKey(JSON.stringify({ method, params }));
320
+ const cache = localStorage.getItem(shaKey);
321
+ if (cache !== null) {
322
+ const { data, expireDate } = JSON.parse(cache);
323
+ if (new Date(expireDate) > /* @__PURE__ */ new Date()) {
324
+ return JSON.parse(data);
325
+ } else {
326
+ localStorage.removeItem(shaKey);
327
+ }
328
+ }
329
+ const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/rpc`, {
330
+ method: "POST",
331
+ headers: { "Content-Type": "application/json" },
332
+ body: JSON.stringify({ method, params }),
333
+ credentials: "include"
334
+ });
335
+ const json = await res.json();
336
+ if (!res.ok) throw new Error(json.error || "Unknown error");
337
+ const today = /* @__PURE__ */ new Date();
338
+ const result = {
339
+ data: JSON.stringify(json.data),
340
+ expireDate: new Date(today.getTime() + 1e3 * 60 * 60)
341
+ };
342
+ localStorage.setItem(shaKey, JSON.stringify(result));
343
+ return json.data;
344
+ };
345
+
346
+ // ../utils/src/classMerge.ts
347
+ import { clsx } from "clsx";
348
+ import { twMerge } from "tailwind-merge";
349
+
350
+ // ../utils/src/params.ts
351
+ var createParams = (fieldsList, key = "Fields") => fieldsList.map((item) => ({
352
+ key,
353
+ value: item
354
+ }));
355
+ var generateQueryParams = (params) => {
356
+ const queryParams = params.map(
357
+ (param) => `${encodeURIComponent(param.key)}=${encodeURIComponent(param.value)}`
358
+ ).join("&");
359
+ return queryParams;
360
+ };
361
+
362
+ // ../utils/src/renditions.ts
363
+ var getFileRenditions = ({ renditions }) => {
364
+ if (renditions == void 0 || renditions.length == 0) {
365
+ return {
366
+ filesToDownload: [],
367
+ filesToOpen: []
368
+ };
369
+ }
370
+ const filteredRenditions = renditions.filter(
371
+ (item) => item.format != "application/xhtml+xml" && item.format != "application/json" && item.format != "application/llm+xml"
372
+ );
373
+ if (filteredRenditions.length == 0 || filteredRenditions[0] == void 0) {
374
+ return {
375
+ filesToDownload: [],
376
+ filesToOpen: []
377
+ };
378
+ }
379
+ const filesToDownload = filteredRenditions.map((item) => {
380
+ const filteredLinks = item.links.filter((item2) => item2.rel == "download");
381
+ return {
382
+ format: item.format,
383
+ link: filteredLinks[0].href
384
+ };
385
+ });
386
+ const filesToOpen = filteredRenditions.map((item) => {
387
+ const filteredLinks = item.links.filter((item2) => item2.rel == "view");
388
+ return {
389
+ format: item.format,
390
+ link: filteredLinks[0].href
391
+ };
392
+ });
393
+ return {
394
+ filesToDownload,
395
+ filesToOpen
396
+ };
397
+ };
398
+
292
399
  // src/baseService.ts
293
400
  var BaseService = class {
294
401
  api;
@@ -373,48 +480,6 @@ var RenditionsService = class extends BaseService {
373
480
  });
374
481
  return response;
375
482
  }
376
- /**
377
- * Processes a list of renditions and categorizes them into files to download and files to open.
378
- * Excludes renditions with formats 'application/xhtml+xml', 'application/json', and 'application/llm+xml'.
379
- *
380
- * @param renditions - Array of rendition objects to process
381
- * @returns An object containing arrays of file renditions categorized as 'filesToDownload' and 'filesToOpen'
382
- */
383
- getFileRenditions = ({ renditions }) => {
384
- if (renditions == void 0 || renditions.length == 0) {
385
- return {
386
- filesToDownload: [],
387
- filesToOpen: []
388
- };
389
- }
390
- const filteredRenditions = renditions.filter(
391
- (item) => item.format != "application/xhtml+xml" && item.format != "application/json" && item.format != "application/llm+xml"
392
- );
393
- if (filteredRenditions.length == 0 || filteredRenditions[0] == void 0) {
394
- return {
395
- filesToDownload: [],
396
- filesToOpen: []
397
- };
398
- }
399
- const filesToDownload = filteredRenditions.map((item) => {
400
- const filteredLinks = item.links.filter((item2) => item2.rel == "download");
401
- return {
402
- format: item.format,
403
- link: filteredLinks[0].href
404
- };
405
- });
406
- const filesToOpen = filteredRenditions.map((item) => {
407
- const filteredLinks = item.links.filter((item2) => item2.rel == "view");
408
- return {
409
- format: item.format,
410
- link: filteredLinks[0].href
411
- };
412
- });
413
- return {
414
- filesToDownload,
415
- filesToOpen
416
- };
417
- };
418
483
  };
419
484
 
420
485
  // src/directoryNodes.ts
@@ -497,11 +562,10 @@ var DocumentTypesService = class extends BaseService {
497
562
  // src/transforms/information.ts
498
563
  import { getConfigs as getConfigs2 } from "@c-rex/utils/next-cookies";
499
564
  var transformInformationUnits = async (data) => {
500
- const config = getConfigs2();
565
+ const config = await getConfigs2();
501
566
  const items = await Promise.all(data.items.map(async (item) => {
502
567
  const type = item.class.labels.filter((item2) => item2.language === EN_LANG)[0].value.toUpperCase();
503
- const service = new RenditionsService();
504
- const { filesToOpen, filesToDownload } = service.getFileRenditions({ renditions: item?.renditions });
568
+ const { filesToOpen, filesToDownload } = getFileRenditions({ renditions: item?.renditions });
505
569
  let link = `/topics/${item.shortId}`;
506
570
  if (config.results.articlePageLayout == "BLOG") {
507
571
  link = `/blog/${item.shortId}`;
@@ -635,16 +699,6 @@ var LanguageService = class extends BaseService {
635
699
  const configs = getConfigs3();
636
700
  super(configs.languageSwitcher.endpoint);
637
701
  }
638
- /*
639
- public static async getInstance(): Promise<LanguageService> {
640
- const customerConfig = await getConfigs()
641
-
642
- if (!LanguageService.instance) {
643
- LanguageService.instance = new LanguageService(customerConfig.languageSwitcher.endpoint);
644
- }
645
- return LanguageService.instance;
646
- }
647
- */
648
702
  /**
649
703
  * Retrieves a list of available languages and their associated countries.
650
704
  * Transforms the API response to include language code, country code, and original value.