cyberia 2.99.8 → 3.0.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.
Files changed (81) hide show
  1. package/.env.production +1 -0
  2. package/.github/workflows/engine-cyberia.cd.yml +1 -0
  3. package/.github/workflows/gitlab.ci.yml +20 -0
  4. package/.github/workflows/publish.ci.yml +18 -38
  5. package/.github/workflows/publish.cyberia.ci.yml +18 -38
  6. package/.vscode/extensions.json +8 -50
  7. package/.vscode/settings.json +0 -77
  8. package/CHANGELOG.md +171 -1
  9. package/{cli.md → CLI-HELP.md} +49 -44
  10. package/README.md +139 -0
  11. package/bin/build.js +7 -15
  12. package/bin/cyberia.js +385 -71
  13. package/bin/deploy.js +14 -151
  14. package/bin/file.js +13 -8
  15. package/bin/index.js +385 -71
  16. package/bin/zed.js +63 -2
  17. package/conf.js +32 -3
  18. package/deployment.yaml +2 -2
  19. package/jsdoc.json +1 -2
  20. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  21. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  22. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  23. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  24. package/manifests/deployment/fastapi/initial_data.sh +4 -52
  25. package/manifests/ipfs/configmap.yaml +64 -0
  26. package/manifests/ipfs/headless-service.yaml +35 -0
  27. package/manifests/ipfs/kustomization.yaml +8 -0
  28. package/manifests/ipfs/statefulset.yaml +149 -0
  29. package/manifests/ipfs/storage-class.yaml +9 -0
  30. package/package.json +15 -11
  31. package/scripts/k3s-node-setup.sh +89 -0
  32. package/scripts/lxd-vm-setup.sh +23 -0
  33. package/scripts/rocky-setup.sh +1 -13
  34. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +2 -0
  35. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +7 -0
  36. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +93 -2
  37. package/src/api/file/file.controller.js +3 -13
  38. package/src/api/file/file.ref.json +0 -21
  39. package/src/api/ipfs/ipfs.controller.js +104 -0
  40. package/src/api/ipfs/ipfs.model.js +71 -0
  41. package/src/api/ipfs/ipfs.router.js +31 -0
  42. package/src/api/ipfs/ipfs.service.js +193 -0
  43. package/src/api/object-layer/README.md +139 -0
  44. package/src/api/object-layer/object-layer.controller.js +3 -0
  45. package/src/api/object-layer/object-layer.model.js +15 -1
  46. package/src/api/object-layer/object-layer.router.js +6 -10
  47. package/src/api/object-layer/object-layer.service.js +311 -182
  48. package/src/api/user/user.router.js +0 -47
  49. package/src/cli/baremetal.js +7 -9
  50. package/src/cli/cluster.js +95 -152
  51. package/src/cli/deploy.js +8 -5
  52. package/src/cli/index.js +31 -31
  53. package/src/cli/ipfs.js +184 -0
  54. package/src/cli/lxd.js +192 -237
  55. package/src/cli/repository.js +4 -1
  56. package/src/cli/run.js +17 -2
  57. package/src/client/components/core/Docs.js +92 -6
  58. package/src/client/components/core/LoadingAnimation.js +2 -3
  59. package/src/client/components/core/Modal.js +1 -1
  60. package/src/client/components/core/VanillaJs.js +36 -25
  61. package/src/client/components/cyberia/ObjectLayerEngineModal.js +4 -5
  62. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +280 -29
  63. package/src/client/services/ipfs/ipfs.service.js +144 -0
  64. package/src/client/services/object-layer/object-layer.management.js +161 -8
  65. package/src/client/services/user/user.management.js +0 -5
  66. package/src/client/services/user/user.service.js +1 -1
  67. package/src/index.js +12 -1
  68. package/src/runtime/express/Express.js +4 -3
  69. package/src/server/auth.js +18 -18
  70. package/src/server/client-build-docs.js +178 -41
  71. package/src/server/conf.js +1 -1
  72. package/src/server/ipfs-client.js +433 -0
  73. package/src/server/logger.js +22 -10
  74. package/src/server/object-layer.js +649 -18
  75. package/src/server/semantic-layer-generator.js +1083 -0
  76. package/src/server/shape-generator.js +952 -0
  77. package/test/shape-generator.test.js +457 -0
  78. package/.vscode/zed.keymap.json +0 -39
  79. package/.vscode/zed.settings.json +0 -20
  80. package/bin/ssl.js +0 -63
  81. package/manifests/lxd/underpost-setup.sh +0 -163
@@ -0,0 +1,433 @@
1
+ /**
2
+ * Lightweight IPFS HTTP client for communicating with a Kubo (go-ipfs) node
3
+ * and an IPFS Cluster daemon running side-by-side in the same StatefulSet.
4
+ *
5
+ * Kubo API (port 5001) – add / pin / cat content.
6
+ * Cluster API (port 9094) – replicate pins across the cluster.
7
+ *
8
+ * Uses native `FormData` + `Blob` (Node ≥ 18) for reliable multipart encoding.
9
+ *
10
+ * @module src/server/ipfs-client.js
11
+ * @namespace IpfsClient
12
+ */
13
+
14
+ import stringify from 'fast-json-stable-stringify';
15
+ import { loggerFactory } from './logger.js';
16
+
17
+ const logger = loggerFactory(import.meta);
18
+
19
+ // ─────────────────────────────────────────────────────────
20
+ // URL helpers
21
+ // ─────────────────────────────────────────────────────────
22
+
23
+ /**
24
+ * Base URL of the Kubo RPC API (port 5001).
25
+ * @returns {string}
26
+ */
27
+ const getIpfsApiUrl = () =>
28
+ process.env.IPFS_API_URL || `http://${process.env.NODE_ENV === 'development' ? 'localhost' : 'ipfs-cluster'}:5001`;
29
+
30
+ /**
31
+ * Base URL of the IPFS Cluster REST API (port 9094).
32
+ * @returns {string}
33
+ */
34
+ const getClusterApiUrl = () =>
35
+ process.env.IPFS_CLUSTER_API_URL ||
36
+ `http://${process.env.NODE_ENV === 'development' ? 'localhost' : 'ipfs-cluster'}:9094`;
37
+
38
+ /**
39
+ * Base URL of the IPFS HTTP Gateway (port 8080).
40
+ * @returns {string}
41
+ */
42
+ const getGatewayUrl = () =>
43
+ process.env.IPFS_GATEWAY_URL ||
44
+ `http://${process.env.NODE_ENV === 'development' ? 'localhost' : 'ipfs-cluster'}:8080`;
45
+
46
+ // ─────────────────────────────────────────────────────────
47
+ // Core: add content
48
+ // ─────────────────────────────────────────────────────────
49
+
50
+ /**
51
+ * @typedef {Object} IpfsAddResult
52
+ * @property {string} cid – CID (Content Identifier) returned by the node.
53
+ * @property {number} size – Cumulative DAG size reported by the node.
54
+ */
55
+
56
+ /**
57
+ * Add arbitrary bytes to the Kubo node AND pin them on the IPFS Cluster.
58
+ *
59
+ * 1. `POST /api/v0/add?pin=true` to Kubo (5001) – stores + locally pins.
60
+ * 2. `POST /pins/<CID>` to the Cluster REST API (9094) – replicates the pin
61
+ * across every peer so `GET /pins` on the cluster shows the content.
62
+ * 3. Copies into MFS so the Web UI "Files" section shows the file.
63
+ *
64
+ * @param {Buffer|string} content – raw bytes or a UTF-8 string to store.
65
+ * @param {string} [filename='data'] – logical filename for the upload.
66
+ * @param {string} [mfsPath] – optional full MFS path.
67
+ * When omitted defaults to `/pinned/<filename>`.
68
+ * @returns {Promise<IpfsAddResult|null>} `null` when the node is unreachable.
69
+ */
70
+ const addToIpfs = async (content, filename = 'data', mfsPath) => {
71
+ const kuboUrl = getIpfsApiUrl();
72
+ const clusterUrl = getClusterApiUrl();
73
+
74
+ // Build multipart body using native FormData + Blob (Node ≥ 18).
75
+ const buf = Buffer.isBuffer(content) ? content : Buffer.from(content, 'utf-8');
76
+ const formData = new FormData();
77
+ formData.append('file', new Blob([buf]), filename);
78
+
79
+ // ── Step 1: add to Kubo ──────────────────────────────
80
+ let cid;
81
+ let size;
82
+ try {
83
+ const res = await fetch(`${kuboUrl}/api/v0/add?pin=true&cid-version=1`, {
84
+ method: 'POST',
85
+ body: formData,
86
+ });
87
+
88
+ if (!res.ok) {
89
+ const text = await res.text();
90
+ logger.error(`IPFS Kubo add failed (${res.status}): ${text}`);
91
+ return null;
92
+ }
93
+
94
+ const json = await res.json();
95
+ cid = json.Hash;
96
+ size = Number(json.Size);
97
+ logger.info(`IPFS Kubo add OK – CID: ${cid}, size: ${size}`);
98
+ } catch (err) {
99
+ logger.warn(`IPFS Kubo node unreachable at ${kuboUrl}: ${err.message}`);
100
+ return null;
101
+ }
102
+
103
+ // ── Step 2: pin to the Cluster ───────────────────────
104
+ try {
105
+ const clusterRes = await fetch(`${clusterUrl}/pins/${encodeURIComponent(cid)}`, {
106
+ method: 'POST',
107
+ });
108
+
109
+ if (!clusterRes.ok) {
110
+ const text = await clusterRes.text();
111
+ logger.warn(`IPFS Cluster pin failed (${clusterRes.status}): ${text}`);
112
+ } else {
113
+ logger.info(`IPFS Cluster pin OK – CID: ${cid}`);
114
+ }
115
+ } catch (err) {
116
+ logger.warn(`IPFS Cluster unreachable at ${clusterUrl}: ${err.message}`);
117
+ }
118
+
119
+ // ── Step 3: copy into MFS so the Web UI "Files" section shows it ─
120
+ const destPath = mfsPath || `/pinned/${filename}`;
121
+ const destDir = destPath.substring(0, destPath.lastIndexOf('/')) || '/';
122
+ try {
123
+ // Ensure parent directory exists in MFS
124
+ await fetch(`${kuboUrl}/api/v0/files/mkdir?arg=${encodeURIComponent(destDir)}&parents=true`, { method: 'POST' });
125
+
126
+ // Remove existing entry if present (cp fails on duplicates)
127
+ await fetch(`${kuboUrl}/api/v0/files/rm?arg=${encodeURIComponent(destPath)}&force=true`, {
128
+ method: 'POST',
129
+ });
130
+
131
+ // Copy the CID into MFS
132
+ const cpRes = await fetch(
133
+ `${kuboUrl}/api/v0/files/cp?arg=/ipfs/${encodeURIComponent(cid)}&arg=${encodeURIComponent(destPath)}`,
134
+ { method: 'POST' },
135
+ );
136
+
137
+ if (!cpRes.ok) {
138
+ const text = await cpRes.text();
139
+ logger.warn(`IPFS MFS cp failed (${cpRes.status}): ${text}`);
140
+ } else {
141
+ logger.info(`IPFS MFS cp OK – ${destPath} → ${cid}`);
142
+ }
143
+ } catch (err) {
144
+ logger.warn(`IPFS MFS cp unreachable: ${err.message}`);
145
+ }
146
+
147
+ return { cid, size };
148
+ };
149
+
150
+ // ─────────────────────────────────────────────────────────
151
+ // Convenience wrappers
152
+ // ─────────────────────────────────────────────────────────
153
+
154
+ /**
155
+ * Add a JSON-serialisable object to IPFS.
156
+ *
157
+ * @param {any} obj – value to serialise.
158
+ * @param {string} [filename='data.json']
159
+ * @param {string} [mfsPath] – optional full MFS destination path.
160
+ * @returns {Promise<IpfsAddResult|null>}
161
+ */
162
+ const addJsonToIpfs = async (obj, filename = 'data.json', mfsPath) => {
163
+ const payload = stringify(obj);
164
+ return addToIpfs(Buffer.from(payload, 'utf-8'), filename, mfsPath);
165
+ };
166
+
167
+ /**
168
+ * Add a binary buffer (e.g. a PNG image) to IPFS.
169
+ *
170
+ * @param {Buffer} buffer – raw image / file bytes.
171
+ * @param {string} filename – e.g. `"atlas.png"`.
172
+ * @param {string} [mfsPath] – optional full MFS destination path.
173
+ * @returns {Promise<IpfsAddResult|null>}
174
+ */
175
+ const addBufferToIpfs = async (buffer, filename, mfsPath) => {
176
+ return addToIpfs(buffer, filename, mfsPath);
177
+ };
178
+
179
+ // ─────────────────────────────────────────────────────────
180
+ // Pin management
181
+ // ─────────────────────────────────────────────────────────
182
+
183
+ /**
184
+ * Explicitly pin an existing CID on both the Kubo node and the Cluster.
185
+ *
186
+ * @param {string} cid
187
+ * @param {string} [type='recursive'] – `'recursive'` | `'direct'`
188
+ * @returns {Promise<boolean>} `true` when at least the Kubo pin succeeded.
189
+ */
190
+ const pinCid = async (cid, type = 'recursive') => {
191
+ const kuboUrl = getIpfsApiUrl();
192
+ const clusterUrl = getClusterApiUrl();
193
+ let kuboOk = false;
194
+
195
+ // Kubo pin
196
+ try {
197
+ const res = await fetch(`${kuboUrl}/api/v0/pin/add?arg=${encodeURIComponent(cid)}&type=${type}`, {
198
+ method: 'POST',
199
+ });
200
+ if (!res.ok) {
201
+ const text = await res.text();
202
+ logger.error(`IPFS Kubo pin/add failed (${res.status}): ${text}`);
203
+ } else {
204
+ kuboOk = true;
205
+ logger.info(`IPFS Kubo pin OK – CID: ${cid} (${type})`);
206
+ }
207
+ } catch (err) {
208
+ logger.warn(`IPFS Kubo pin unreachable: ${err.message}`);
209
+ }
210
+
211
+ // Cluster pin
212
+ try {
213
+ const clusterRes = await fetch(`${clusterUrl}/pins/${encodeURIComponent(cid)}`, {
214
+ method: 'POST',
215
+ });
216
+ if (!clusterRes.ok) {
217
+ const text = await clusterRes.text();
218
+ logger.warn(`IPFS Cluster pin failed (${clusterRes.status}): ${text}`);
219
+ } else {
220
+ logger.info(`IPFS Cluster pin OK – CID: ${cid}`);
221
+ }
222
+ } catch (err) {
223
+ logger.warn(`IPFS Cluster pin unreachable: ${err.message}`);
224
+ }
225
+
226
+ return kuboOk;
227
+ };
228
+
229
+ /**
230
+ * Unpin a CID from both the Kubo node and the Cluster.
231
+ *
232
+ * @param {string} cid
233
+ * @returns {Promise<boolean>}
234
+ */
235
+ const unpinCid = async (cid) => {
236
+ const kuboUrl = getIpfsApiUrl();
237
+ const clusterUrl = getClusterApiUrl();
238
+ let kuboOk = false;
239
+
240
+ // Cluster unpin
241
+ try {
242
+ const clusterRes = await fetch(`${clusterUrl}/pins/${encodeURIComponent(cid)}`, {
243
+ method: 'DELETE',
244
+ });
245
+ if (!clusterRes.ok) {
246
+ const text = await clusterRes.text();
247
+ logger.warn(`IPFS Cluster unpin failed (${clusterRes.status}): ${text}`);
248
+ } else {
249
+ logger.info(`IPFS Cluster unpin OK – CID: ${cid}`);
250
+ }
251
+ } catch (err) {
252
+ logger.warn(`IPFS Cluster unpin unreachable: ${err.message}`);
253
+ }
254
+
255
+ // Kubo unpin
256
+ try {
257
+ const res = await fetch(`${kuboUrl}/api/v0/pin/rm?arg=${encodeURIComponent(cid)}`, {
258
+ method: 'POST',
259
+ });
260
+ if (!res.ok) {
261
+ const text = await res.text();
262
+ // "not pinned or pinned indirectly" means the CID is already unpinned – treat as success
263
+ if (text.includes('not pinned')) {
264
+ kuboOk = true;
265
+ logger.info(`IPFS Kubo unpin – CID already not pinned: ${cid}`);
266
+ } else {
267
+ logger.warn(`IPFS Kubo pin/rm failed (${res.status}): ${text}`);
268
+ }
269
+ } else {
270
+ kuboOk = true;
271
+ logger.info(`IPFS Kubo unpin OK – CID: ${cid}`);
272
+ }
273
+ } catch (err) {
274
+ logger.warn(`IPFS Kubo unpin unreachable: ${err.message}`);
275
+ }
276
+
277
+ return kuboOk;
278
+ };
279
+
280
+ // ─────────────────────────────────────────────────────────
281
+ // Retrieval
282
+ // ─────────────────────────────────────────────────────────
283
+
284
+ /**
285
+ * Retrieve raw bytes for a CID from the IPFS HTTP Gateway (port 8080).
286
+ *
287
+ * @param {string} cid
288
+ * @returns {Promise<Buffer|null>}
289
+ */
290
+ const getFromIpfs = async (cid) => {
291
+ const url = getGatewayUrl();
292
+ try {
293
+ const res = await fetch(`${url}/ipfs/${encodeURIComponent(cid)}`);
294
+ if (!res.ok) {
295
+ logger.error(`IPFS gateway GET failed (${res.status}) for ${cid}`);
296
+ return null;
297
+ }
298
+ const arrayBuffer = await res.arrayBuffer();
299
+ return Buffer.from(arrayBuffer);
300
+ } catch (err) {
301
+ logger.warn(`IPFS gateway unreachable at ${url}: ${err.message}`);
302
+ return null;
303
+ }
304
+ };
305
+
306
+ // ─────────────────────────────────────────────────────────
307
+ // Diagnostics
308
+ // ─────────────────────────────────────────────────────────
309
+
310
+ /**
311
+ * List all pins tracked by the IPFS Cluster (port 9094).
312
+ * Each line in the response is a JSON object with at least a `cid` field.
313
+ *
314
+ * @returns {Promise<Array<{ cid: string, name: string, peer_map: object }>>}
315
+ */
316
+ const listClusterPins = async () => {
317
+ const clusterUrl = getClusterApiUrl();
318
+ try {
319
+ const res = await fetch(`${clusterUrl}/pins`);
320
+ if (res.status === 204) {
321
+ // 204 No Content → the cluster has no pins at all.
322
+ return [];
323
+ }
324
+ if (!res.ok) {
325
+ const text = await res.text();
326
+ logger.error(`IPFS Cluster list pins failed (${res.status}): ${text}`);
327
+ return [];
328
+ }
329
+ const text = await res.text();
330
+ // The cluster streams one JSON object per line (NDJSON).
331
+ return text
332
+ .split('\n')
333
+ .filter((line) => line.trim())
334
+ .map((line) => {
335
+ try {
336
+ return JSON.parse(line);
337
+ } catch {
338
+ return null;
339
+ }
340
+ })
341
+ .filter(Boolean);
342
+ } catch (err) {
343
+ logger.warn(`IPFS Cluster unreachable at ${clusterUrl}: ${err.message}`);
344
+ return [];
345
+ }
346
+ };
347
+
348
+ /**
349
+ * List pins tracked by the local Kubo node (port 5001).
350
+ *
351
+ * @param {string} [type='recursive'] – `'all'` | `'recursive'` | `'direct'` | `'indirect'`
352
+ * @returns {Promise<Object<string, { Type: string }>>} Map of CID → pin info.
353
+ */
354
+ const listKuboPins = async (type = 'recursive') => {
355
+ const kuboUrl = getIpfsApiUrl();
356
+ try {
357
+ const res = await fetch(`${kuboUrl}/api/v0/pin/ls?type=${type}`, {
358
+ method: 'POST',
359
+ });
360
+ if (!res.ok) {
361
+ const text = await res.text();
362
+ logger.error(`IPFS Kubo pin/ls failed (${res.status}): ${text}`);
363
+ return {};
364
+ }
365
+ const json = await res.json();
366
+ return json.Keys || {};
367
+ } catch (err) {
368
+ logger.warn(`IPFS Kubo pin/ls unreachable: ${err.message}`);
369
+ return {};
370
+ }
371
+ };
372
+
373
+ // ─────────────────────────────────────────────────────────
374
+ // MFS management
375
+ // ─────────────────────────────────────────────────────────
376
+
377
+ /**
378
+ * Remove a file or directory from the Kubo MFS (Mutable File System).
379
+ * This cleans up entries visible in the IPFS Web UI "Files" section.
380
+ *
381
+ * @param {string} mfsPath – Full MFS path to remove, e.g. `/pinned/myfile.json`
382
+ * or `/object-layer/itemId`.
383
+ * @param {boolean} [recursive=true] – When `true`, removes directories recursively.
384
+ * @returns {Promise<boolean>} `true` when the removal succeeded or the path didn't exist.
385
+ */
386
+ const removeMfsPath = async (mfsPath, recursive = true) => {
387
+ const kuboUrl = getIpfsApiUrl();
388
+ try {
389
+ // First check if the path exists via stat; if it doesn't we can return early.
390
+ const statRes = await fetch(`${kuboUrl}/api/v0/files/stat?arg=${encodeURIComponent(mfsPath)}`, { method: 'POST' });
391
+ if (!statRes.ok) {
392
+ // Path doesn't exist – nothing to remove.
393
+ logger.info(`IPFS MFS rm – path does not exist, skipping: ${mfsPath}`);
394
+ return true;
395
+ }
396
+
397
+ const rmRes = await fetch(
398
+ `${kuboUrl}/api/v0/files/rm?arg=${encodeURIComponent(mfsPath)}&force=true${recursive ? '&recursive=true' : ''}`,
399
+ { method: 'POST' },
400
+ );
401
+ if (!rmRes.ok) {
402
+ const text = await rmRes.text();
403
+ logger.warn(`IPFS MFS rm failed (${rmRes.status}): ${text}`);
404
+ return false;
405
+ }
406
+ logger.info(`IPFS MFS rm OK – ${mfsPath}`);
407
+ return true;
408
+ } catch (err) {
409
+ logger.warn(`IPFS MFS rm unreachable: ${err.message}`);
410
+ return false;
411
+ }
412
+ };
413
+
414
+ // ─────────────────────────────────────────────────────────
415
+ // Export
416
+ // ─────────────────────────────────────────────────────────
417
+
418
+ const IpfsClient = {
419
+ getIpfsApiUrl,
420
+ getClusterApiUrl,
421
+ getGatewayUrl,
422
+ addToIpfs,
423
+ addJsonToIpfs,
424
+ addBufferToIpfs,
425
+ pinCid,
426
+ unpinCid,
427
+ getFromIpfs,
428
+ listClusterPins,
429
+ listKuboPins,
430
+ removeMfsPath,
431
+ };
432
+
433
+ export { IpfsClient };
@@ -101,27 +101,37 @@ const setUpInfo = async (logger = new winston.Logger()) => {
101
101
  * @param meta - The `meta` parameter in the `loggerFactory` function is used to extract the last part
102
102
  * of a URL and use it to create log files in a specific directory.
103
103
  * @param logLevel - Specify the logging level for the logger instance. e.g., 'error', 'warn', 'info', 'debug'.
104
+ * @param enableFileLogs - Whether to write logs to files. Defaults to the value of the `ENABLE_FILE_LOGS` environment variable.
104
105
  * @returns {underpostLogger} The `loggerFactory` function returns a logger instance created using Winston logger
105
106
  * library. The logger instance is configured with various transports for printing out messages to
106
107
  * different destinations such as the terminal, error.log file, and all.log file. The logger instance
107
108
  * also has a method `setUpInfo` attached to it for setting up additional information.
108
109
  * @memberof Logger
109
110
  */
110
- const loggerFactory = (meta = { url: '' }, logLevel = '') => {
111
+ const loggerFactory = (
112
+ meta = { url: '' },
113
+ logLevel = '',
114
+ enableFileLogs = process.env.ENABLE_FILE_LOGS === 'true' || process.env.ENABLE_FILE_LOGS === true,
115
+ ) => {
111
116
  meta = meta.url.split('/').pop();
112
117
  // Define which transports the logger must use to print out messages.
113
118
  // In this example, we are using three different transports
114
119
  const transports = [
115
120
  // Allow the use the terminal to print the messages
116
121
  new winston.transports.Console(),
117
- // Allow to print all the error level messages inside the error.log file
118
- // new winston.transports.File({
119
- // filename: `logs/${meta}/error.log`,
120
- // level: 'error',
121
- // }),
122
- // Allow to print all the error message inside the all.log file
123
- // (also the error log that are also printed inside the error.log(
124
- new winston.transports.File({ filename: `logs/${meta}/all.log` }),
122
+ // Optionally write log files when enableFileLogs is true
123
+ ...(enableFileLogs
124
+ ? [
125
+ // Allow to print all the error level messages inside the error.log file
126
+ new winston.transports.File({
127
+ filename: `logs/${meta}/error.log`,
128
+ level: 'error',
129
+ }),
130
+ // Allow to print all the error messages inside the all.log file
131
+ // (also includes error logs that are also printed inside error.log)
132
+ new winston.transports.File({ filename: `logs/${meta}/all.log` }),
133
+ ]
134
+ : []),
125
135
  ];
126
136
 
127
137
  // Create the logger instance that has to be exported
@@ -154,6 +164,7 @@ const loggerFactory = (meta = { url: '' }, logLevel = '') => {
154
164
  * @param {Object} meta - An object containing metadata, such as the URL, to be used in the logger.
155
165
  * @param {string} logLevel - The logging level to be used for the logger (e.g., 'error', 'warn', 'info', 'debug').
156
166
  * @param {Function} skip - A function to determine whether to skip logging for a particular request.
167
+ * @param {boolean} enableFileLogs - Whether to write logs to files. Defaults to false.
157
168
  * @returns {Function} A middleware function that can be used in an Express application to log HTTP requests.
158
169
  * @memberof Logger
159
170
  */
@@ -161,10 +172,11 @@ const loggerMiddleware = (
161
172
  meta = { url: '' },
162
173
  logLevel = 'info',
163
174
  skip = (req, res) => process.env.NODE_ENV === 'production',
175
+ enableFileLogs = process.env.ENABLE_FILE_LOGS === 'true' || process.env.ENABLE_FILE_LOGS === true,
164
176
  ) => {
165
177
  const stream = {
166
178
  // Use the http severity
167
- write: (message) => loggerFactory(meta, logLevel).http(message),
179
+ write: (message) => loggerFactory(meta, logLevel, enableFileLogs).http(message),
168
180
  };
169
181
 
170
182
  morgan.token('host', function (req, res) {