@rshval/back-kit 1.1.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.
Files changed (70) hide show
  1. package/README.md +538 -0
  2. package/dist/api.d.ts +17 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +51 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/audit-log.d.ts +36 -0
  7. package/dist/audit-log.d.ts.map +1 -0
  8. package/dist/audit-log.js +45 -0
  9. package/dist/audit-log.js.map +1 -0
  10. package/dist/cache-middleware.d.ts +27 -0
  11. package/dist/cache-middleware.d.ts.map +1 -0
  12. package/dist/cache-middleware.js +72 -0
  13. package/dist/cache-middleware.js.map +1 -0
  14. package/dist/cache.d.ts +24 -0
  15. package/dist/cache.d.ts.map +1 -0
  16. package/dist/cache.js +76 -0
  17. package/dist/cache.js.map +1 -0
  18. package/dist/database.d.ts +24 -0
  19. package/dist/database.d.ts.map +1 -0
  20. package/dist/database.js +90 -0
  21. package/dist/database.js.map +1 -0
  22. package/dist/email.d.ts +34 -0
  23. package/dist/email.d.ts.map +1 -0
  24. package/dist/email.js +26 -0
  25. package/dist/email.js.map +1 -0
  26. package/dist/global.d.ts +24 -0
  27. package/dist/global.d.ts.map +1 -0
  28. package/dist/global.js +20 -0
  29. package/dist/global.js.map +1 -0
  30. package/dist/helpers.d.ts +16 -0
  31. package/dist/helpers.d.ts.map +1 -0
  32. package/dist/helpers.js +19 -0
  33. package/dist/helpers.js.map +1 -0
  34. package/dist/index.d.ts +19 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +17 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/logger.d.ts +6 -0
  39. package/dist/logger.d.ts.map +1 -0
  40. package/dist/logger.js +17 -0
  41. package/dist/logger.js.map +1 -0
  42. package/dist/mail-template.d.ts +41 -0
  43. package/dist/mail-template.d.ts.map +1 -0
  44. package/dist/mail-template.js +51 -0
  45. package/dist/mail-template.js.map +1 -0
  46. package/dist/paymaster.d.ts +51 -0
  47. package/dist/paymaster.d.ts.map +1 -0
  48. package/dist/paymaster.js +127 -0
  49. package/dist/paymaster.js.map +1 -0
  50. package/dist/seed.d.ts +15 -0
  51. package/dist/seed.d.ts.map +1 -0
  52. package/dist/seed.js +57 -0
  53. package/dist/seed.js.map +1 -0
  54. package/dist/site-helpers.d.ts +15 -0
  55. package/dist/site-helpers.d.ts.map +1 -0
  56. package/dist/site-helpers.js +21 -0
  57. package/dist/site-helpers.js.map +1 -0
  58. package/dist/socket-client.d.ts +19 -0
  59. package/dist/socket-client.d.ts.map +1 -0
  60. package/dist/socket-client.js +66 -0
  61. package/dist/socket-client.js.map +1 -0
  62. package/dist/utils.d.ts +3 -0
  63. package/dist/utils.d.ts.map +1 -0
  64. package/dist/utils.js +6 -0
  65. package/dist/utils.js.map +1 -0
  66. package/dist/validation.d.ts +7 -0
  67. package/dist/validation.d.ts.map +1 -0
  68. package/dist/validation.js +26 -0
  69. package/dist/validation.js.map +1 -0
  70. package/package.json +64 -0
package/README.md ADDED
@@ -0,0 +1,538 @@
1
+ # @rshval/back-kit
2
+
3
+ Публичный npm-пакет `@rshval/back-kit` с серверными утилитами для Node.js/TypeScript проектов.
4
+
5
+ > Репозиторий используется для внутренних проектов автора. Любое использование третьими лицами выполняется на их страх и риск.
6
+
7
+ ## Установка
8
+
9
+ ```bash
10
+ npm i @rshval/back-kit
11
+ ```
12
+
13
+ Пакет ESM-only (`"type": "module"`). Для CommonJS используйте динамический `import()`.
14
+
15
+ ---
16
+
17
+ ## Все экспортируемые функции и примеры использования
18
+
19
+ Ниже перечислены все публичные экспорты из пакета (через `src/index.ts`) и короткие примеры.
20
+
21
+ ### 1) Email
22
+
23
+ #### `createMailOptions({ from, to, subject, text })`
24
+ Создаёт объект параметров письма для nodemailer. Поддерживает `to` как строку (обычная отправка) или объект формы (`MailOptionsBody`) для заявок/обратной связи.
25
+
26
+ ```ts
27
+ import { createMailOptions } from '@rshval/back-kit'
28
+
29
+ const options = createMailOptions({
30
+ from: 'robot@example.com',
31
+ to: 'user@example.com',
32
+ subject: 'Hello',
33
+ text: 'Welcome!'
34
+ })
35
+ ```
36
+
37
+ #### `sendEmailWithConfig({ nodemailerConfig, to, subject, text })`
38
+ Создаёт transport через nodemailer, отправляет письмо и возвращает строку с результатом отправки.
39
+
40
+ ```ts
41
+ import { sendEmailWithConfig } from '@rshval/back-kit'
42
+
43
+ await sendEmailWithConfig({
44
+ nodemailerConfig: {
45
+ host: 'smtp.example.com',
46
+ port: 465,
47
+ secure: true,
48
+ auth: { user: 'robot@example.com', pass: '***' }
49
+ },
50
+ to: 'user@example.com',
51
+ subject: 'Reset password',
52
+ text: 'Code: 123456'
53
+ })
54
+ ```
55
+
56
+ ---
57
+
58
+ ### 2) Конфиг и JWT
59
+
60
+ #### `getBaseUrlByConfig(config, baseUrl?)`
61
+ Строит base URL по конфигу приложения. В `development` добавляет `:port`.
62
+
63
+ ```ts
64
+ import { getBaseUrlByConfig } from '@rshval/back-kit'
65
+
66
+ const base = getBaseUrlByConfig(
67
+ {
68
+ NODE_ENV: 'development',
69
+ server: { domain: 'localhost', port: 3000 },
70
+ jwt: {}
71
+ },
72
+ '/api'
73
+ )
74
+ // //localhost:3000/api
75
+ ```
76
+
77
+ #### `createTokenByConfig({ config, user, expiresIn })`
78
+ Создаёт JWT-токен на основе `config.jwt.JWT_KEY` (или `JWT_KEY_NO_ENV`).
79
+
80
+ ```ts
81
+ import { createTokenByConfig } from '@rshval/back-kit'
82
+
83
+ const token = createTokenByConfig({
84
+ config: {
85
+ server: { domain: 'example.com' },
86
+ jwt: { JWT_KEY: 'super-secret' }
87
+ },
88
+ user: { _id: '64a...' },
89
+ expiresIn: '7d'
90
+ })
91
+ ```
92
+
93
+ ---
94
+
95
+ ### 3) Вспомогательные утилиты
96
+
97
+ #### `createPinCode(min?, max?)`
98
+ Генерирует случайный числовой PIN в диапазоне (`10000..99990` по умолчанию).
99
+
100
+ ```ts
101
+ import { createPinCode } from '@rshval/back-kit'
102
+
103
+ const pin = createPinCode()
104
+ ```
105
+
106
+ #### `getIp(req)`
107
+ Пытается определить IP клиента из `req.ip`, сокетов и `x-forwarded-for`.
108
+
109
+ ```ts
110
+ import { getIp } from '@rshval/back-kit'
111
+
112
+ const ip = await getIp(req)
113
+ ```
114
+
115
+ #### `translitUrl(str)`
116
+ Транслитерирует строку в URL-friendly slug (`-`, lower-case).
117
+
118
+ ```ts
119
+ import { translitUrl } from '@rshval/back-kit'
120
+
121
+ const slug = translitUrl('Пример страницы')
122
+ // primer-stranicy
123
+ ```
124
+
125
+ ---
126
+
127
+ ### 4) Валидация
128
+
129
+ #### `patternEmail()`
130
+ Возвращает RegExp для проверки email.
131
+
132
+ #### `patternPassword()`
133
+ Возвращает RegExp для пароля (минимум 8 символов, буквы + цифры).
134
+
135
+ #### `isValidEmail(val)`
136
+ Проверяет корректность email.
137
+
138
+ #### `isValidPhoneNumber(val)`
139
+ Проверяет номер телефона через `libphonenumber-js`.
140
+
141
+ #### `isValidCode(code, length)`
142
+ Проверяет длину кода.
143
+
144
+ #### `isEmpty(val)`
145
+ Проверяет пустую строку (`trim`) или пустой объект.
146
+
147
+ ```ts
148
+ import {
149
+ patternEmail,
150
+ patternPassword,
151
+ isValidEmail,
152
+ isValidPhoneNumber,
153
+ isValidCode,
154
+ isEmpty
155
+ } from '@rshval/back-kit'
156
+
157
+ const emailRegex = patternEmail()
158
+ const passwordRegex = patternPassword()
159
+
160
+ isValidEmail('user@example.com') // true
161
+ isValidPhoneNumber('+79991234567') // true/false
162
+ isValidCode('123456', 6) // true
163
+ isEmpty(' ') // true
164
+ isEmpty({}) // true
165
+ ```
166
+
167
+ ---
168
+
169
+ ### 5) Билдеры (привязка к конфигу)
170
+
171
+ #### `buildGetBaseUrl(config)`
172
+ Возвращает функцию `(baseUrl?) => string`.
173
+
174
+ #### `buildCreateToken(config)`
175
+ Возвращает функцию создания JWT с уже «зашитым» конфигом.
176
+
177
+ #### `buildSendEmail(nodemailerConfig)`
178
+ Возвращает функцию отправки email с уже «зашитым» SMTP-конфигом.
179
+
180
+ ```ts
181
+ import {
182
+ buildGetBaseUrl,
183
+ buildCreateToken,
184
+ buildSendEmail
185
+ } from '@rshval/back-kit'
186
+
187
+ const getBaseUrl = buildGetBaseUrl({
188
+ NODE_ENV: 'production',
189
+ server: { domain: 'example.com' },
190
+ jwt: { JWT_KEY: 'secret' }
191
+ })
192
+
193
+ const createToken = buildCreateToken({
194
+ server: { domain: 'example.com' },
195
+ jwt: { JWT_KEY: 'secret' }
196
+ })
197
+
198
+ const sendEmail = buildSendEmail({
199
+ host: 'smtp.example.com',
200
+ port: 465,
201
+ secure: true,
202
+ auth: { user: 'robot@example.com', pass: '***' }
203
+ })
204
+ ```
205
+
206
+ ---
207
+
208
+ ### 6) База данных
209
+
210
+ #### `startMongoDatabase(options)`
211
+ Запускает подключение к MongoDB через mongoose, логирует статусы и умеет ретраить подключение.
212
+
213
+ ```ts
214
+ import { startMongoDatabase } from '@rshval/back-kit'
215
+
216
+ await startMongoDatabase({
217
+ config: {
218
+ name: 'main',
219
+ connect: process.env.MONGO_URI,
220
+ params: {}
221
+ },
222
+ logger: console
223
+ })
224
+ ```
225
+
226
+ ---
227
+
228
+ ### 7) Кэш
229
+
230
+ #### `createCacheService(options?)`
231
+ Создаёт LRU cache-сервис с методами:
232
+ - `get(key)`
233
+ - `set(key, data, compareKey?, compareValue?, ttlMs?)`
234
+ - `delete(key)`
235
+ - `getId(val)`
236
+ - `keys()`
237
+ - `clear()`
238
+ - `entries()` *(если `exposeEntries: true`)*
239
+ - `values()` *(если `exposeValues: true`)*
240
+
241
+ ```ts
242
+ import { createCacheService } from '@rshval/back-kit'
243
+
244
+ const cache = createCacheService({ ttl: 60_000 })
245
+ const key = cache.getId({ service: 'users', page: 1 })
246
+ await cache.set(key, [{ s: 'state', data: [1, 2, 3] }])
247
+ const value = await cache.get(key)
248
+ ```
249
+
250
+ #### `createCacheMiddleware({ cache, ... })`
251
+ Создаёт middleware-обёртку над cache-сервисом с методами:
252
+ - `get(id)`
253
+ - `set(id, data, expDataTime)`
254
+ - `del(id)`
255
+ - `delByPrefix(prefix)` *(если `includeDelByPrefix: true`)*
256
+
257
+ ```ts
258
+ import { createCacheService, createCacheMiddleware } from '@rshval/back-kit'
259
+
260
+ const cache = createCacheService({ supportTtlInSet: true })
261
+ const cm = createCacheMiddleware({
262
+ cache,
263
+ passTtlToCacheSet: true,
264
+ includeDelByPrefix: true
265
+ })
266
+
267
+ await cm.set('users:list', [{ id: 1 }], 60_000)
268
+ const cached = await cm.get('users:list')
269
+ await cm.delByPrefix?.('users:')
270
+ ```
271
+
272
+ ---
273
+
274
+ ### 8) Логирование
275
+
276
+ #### `createLoggerService({ rootdir? })`
277
+ Возвращает фабрику логгеров. Для каждого namespace (`std`) пишет:
278
+ - `logs/<std>/stdout.log`
279
+ - `logs/<std>/stderr.log`
280
+
281
+ ```ts
282
+ import { createLoggerService } from '@rshval/back-kit'
283
+
284
+ const makeLogger = createLoggerService({ rootdir: process.cwd() })
285
+ const logger = makeLogger('api')
286
+ logger.log('started')
287
+ ```
288
+
289
+ ---
290
+
291
+ ### 9) WebSocket клиент
292
+
293
+ #### `createSocketClientService(options)`
294
+ Создаёт сервис клиента `socket.io` с методами:
295
+ - `doSocketClient()` — старт подключения
296
+ - `getSocketClient()` — вернуть текущий инстанс
297
+
298
+ ```ts
299
+ import { createSocketClientService } from '@rshval/back-kit'
300
+
301
+ const ws = createSocketClientService({
302
+ wsName: 'worker',
303
+ socketBase: 'https://example.com',
304
+ logger: console,
305
+ runWorkers: () => {
306
+ // jobs
307
+ }
308
+ })
309
+
310
+ ws.doSocketClient()
311
+ ```
312
+
313
+ ---
314
+
315
+ ### 10) Paymaster
316
+
317
+ #### `createPaymasterService({ paymaster, clientServer, serverBaseUrl })`
318
+ Создаёт сервис с методами:
319
+ - `createPaymentLink({...})` — сформировать ссылку на оплату
320
+ - `validateCallback(payload, rawBody?)` — проверить подпись callback
321
+ - `parseCallback(payload)` — нормализовать callback в удобный объект
322
+
323
+ ```ts
324
+ import { createPaymasterService } from '@rshval/back-kit'
325
+
326
+ const paymaster = createPaymasterService({
327
+ paymaster: {
328
+ merchantId: 'merchant-id',
329
+ secretKey: 'secret',
330
+ checkoutUrl: 'https://paymaster.ru/Payment/Init',
331
+ checkSignature: true
332
+ },
333
+ clientServer: 'https://site.example.com',
334
+ serverBaseUrl: 'https://api.example.com'
335
+ })
336
+
337
+ const link = paymaster.createPaymentLink({
338
+ requestId: 'order_123',
339
+ amount: 1499,
340
+ description: 'Order #123'
341
+ })
342
+ ```
343
+
344
+ ---
345
+
346
+ ### 11) Почтовые шаблоны
347
+
348
+ #### `createMailTemplateService({ findTemplate, sendEmail })`
349
+ Создаёт сервис шаблонов email с методами:
350
+ - `extractVariables(template)` — список `{{variables}}`
351
+ - `renderTemplate(template, context?)` — рендер subject/body
352
+ - `sendByKey({ key, to, context? })` — найти шаблон, отрендерить и отправить
353
+
354
+ ```ts
355
+ import { createMailTemplateService } from '@rshval/back-kit'
356
+
357
+ const mailTemplates = createMailTemplateService({
358
+ findTemplate: async ({ key }) =>
359
+ key === 'welcome'
360
+ ? { subject: 'Hi, {{user.name}}', bodyText: 'Hello {{user.name}}!' }
361
+ : null,
362
+ sendEmail: async (to, subject, text) => {
363
+ console.log('send', to, subject, text)
364
+ }
365
+ })
366
+
367
+ await mailTemplates.sendByKey({
368
+ key: 'welcome',
369
+ to: 'user@example.com',
370
+ context: { user: { name: 'Alex' } }
371
+ })
372
+ ```
373
+
374
+ ---
375
+
376
+ ### 12) Аудит изменений
377
+
378
+ #### `buildAuditChanges({ beforeData, afterData, fieldsToCheck, protectedFields? })`
379
+ Сравнивает данные «до/после» и возвращает массив изменений по выбранным полям.
380
+
381
+ #### `createAuditLog({ ..., save })`
382
+ Создаёт запись аудита через переданную функцию `save`. Если изменений нет — возвращает `null`.
383
+
384
+ ```ts
385
+ import { buildAuditChanges, createAuditLog } from '@rshval/back-kit'
386
+
387
+ const changes = buildAuditChanges({
388
+ beforeData: { name: 'Old', role: 'user' },
389
+ afterData: { name: 'New', role: 'user' },
390
+ fieldsToCheck: ['name', 'role'],
391
+ protectedFields: ['role']
392
+ })
393
+
394
+ await createAuditLog({
395
+ entityType: 'user',
396
+ entityId: '507f1f77bcf86cd799439011',
397
+ action: 'update',
398
+ changedBy: '507f191e810c19729de860ea',
399
+ changes,
400
+ save: async (payload) => payload
401
+ })
402
+ ```
403
+
404
+ ---
405
+
406
+ ### 13) Seed-функции
407
+
408
+ #### `createSeedFunctions({ cache, retryDelayMs? })`
409
+ Возвращает методы:
410
+ - `checkDatabaseIsConnected()` — проверяет состояние БД через кэш
411
+ - `setSeedData(SeedModel, arr)` — добавляет seed-данные после подтверждения подключения
412
+
413
+ ```ts
414
+ import { createSeedFunctions, createCacheService } from '@rshval/back-kit'
415
+
416
+ const cache = createCacheService()
417
+ const { setSeedData } = createSeedFunctions({ cache })
418
+
419
+ // await setSeedData(UserModel, [{ name: 'Admin' }])
420
+ ```
421
+
422
+ ---
423
+
424
+ ### 14) API-клиент
425
+
426
+ #### `createApiService({ userAgent, logger? })`
427
+ Возвращает HTTP-клиент с методами:
428
+ - `get(path, token?)`
429
+ - `getXml(path, token?)`
430
+ - `del(path, token?)`
431
+ - `post(path, data, token?, xml?, contentType?)`
432
+ - `put(path, data, token)`
433
+
434
+ ```ts
435
+ import { createApiService } from '@rshval/back-kit'
436
+
437
+ const api = createApiService({ userAgent: 'my-service/1.0.0', logger: console })
438
+
439
+ const users = await api.get('https://api.example.com/users')
440
+ const created = await api.post('https://api.example.com/users', { name: 'Alex' })
441
+ ```
442
+
443
+ Также экспортируется тип токена:
444
+
445
+ ```ts
446
+ import type { Token } from '@rshval/back-kit'
447
+ ```
448
+
449
+ ---
450
+
451
+ ### 15) Прочие экспортированные утилиты
452
+
453
+ #### `add(a, b)`
454
+ Возвращает сумму двух чисел.
455
+
456
+ #### `test2()`
457
+ Логирует `99` и возвращает `8`.
458
+
459
+ ```ts
460
+ import { add, test2 } from '@rshval/back-kit'
461
+
462
+ add(2, 3) // 5
463
+ test2() // 8
464
+ ```
465
+
466
+ ---
467
+
468
+ ## Подготовка к публикации в npm
469
+
470
+ ### 1) Требования
471
+
472
+ - Node.js 18+
473
+ - npm 9+
474
+ - доступ к npm-аккаунту, имеющему право публиковать пакет `@rshval/back-kit`
475
+
476
+ ### 2) Сборка и проверка содержимого публикации
477
+
478
+ ```bash
479
+ npm install
480
+ npm run build
481
+ npm run pack:dry
482
+ ```
483
+
484
+ `npm run pack:dry` показывает, какие файлы реально попадут в npm-пакет.
485
+
486
+ ### 3) Логин в npm
487
+
488
+ ```bash
489
+ npm login
490
+ ```
491
+
492
+ Проверить текущего пользователя:
493
+
494
+ ```bash
495
+ npm whoami
496
+ ```
497
+
498
+ ### 4) Публикация
499
+
500
+ 1. Обновите версию:
501
+
502
+ ```bash
503
+ npm version patch
504
+ # или minor / major
505
+ ```
506
+
507
+ 2. Опубликуйте пакет:
508
+
509
+ ```bash
510
+ npm publish
511
+ ```
512
+
513
+ > Пакет публикуется как `public` (см. `publishConfig.access`).
514
+
515
+
516
+ > Если при `npm publish` появляется ошибка `E403` с текстом
517
+ > `Two-factor authentication or granular access token with bypass 2fa enabled is required`,
518
+ > значит текущий токен не подходит для публикации.
519
+ >
520
+ > Варианты решения:
521
+ > - выполните `npm login --auth-type=web` и подтвердите OTP при публикации;
522
+ > - или создайте **granular access token** на npmjs.com с правами `read and write`
523
+ > для пакета и включённым `bypass 2FA for publishing`, затем обновите токен локально
524
+ > через `npm logout && npm login` (или настройкой в `.npmrc`).
525
+
526
+ ---
527
+
528
+ ## Разработка (standalone)
529
+
530
+ ```bash
531
+ npm install
532
+ npm run build
533
+ ```
534
+
535
+ ## Экспорты
536
+
537
+ - ESM: `dist/index.js`
538
+ - Типы: `dist/index.d.ts`
package/dist/api.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ interface Token {
2
+ type: 'Token' | 'Bearer';
3
+ key: string;
4
+ }
5
+ interface CreateApiServiceOptions {
6
+ userAgent: string;
7
+ logger?: Pick<Console, 'error'>;
8
+ }
9
+ export declare const createApiService: ({ userAgent, logger, }: CreateApiServiceOptions) => {
10
+ get: (path: string, token?: Token) => Promise<any>;
11
+ getXml: (path: string, token?: Token) => Promise<any>;
12
+ del: (path: string, token?: Token) => Promise<any>;
13
+ post: (path: string, data: Record<string, unknown>, token?: Token, xml?: boolean, contentType?: string) => Promise<any>;
14
+ put: (path: string, data: Record<string, unknown>, token: Token) => Promise<any>;
15
+ };
16
+ export type { Token };
17
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,UAAU,KAAK;IACb,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,uBAAuB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;CACjC;AAWD,eAAO,MAAM,gBAAgB,GAAI,wBAG9B,uBAAuB;gBA4DV,MAAM,UAAU,KAAK;mBAClB,MAAM,UAAU,KAAK;gBAExB,MAAM,UAAU,KAAK;iBAGzB,MAAM,QACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,UACrB,KAAK,QACP,OAAO,gBACC,MAAM;gBAEV,MAAM,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,KAAK;CAGlE,CAAC;AAEF,YAAY,EAAE,KAAK,EAAE,CAAC"}
package/dist/api.js ADDED
@@ -0,0 +1,51 @@
1
+ export const createApiService = ({ userAgent, logger, }) => {
2
+ const send = async ({ method, path, data, token, xml, contentType, }) => {
3
+ const requestHeaders = new Headers();
4
+ requestHeaders.set('User-Agent', userAgent);
5
+ if (token) {
6
+ requestHeaders.set('Authorization', ` ${token.type} ${token.key}`);
7
+ }
8
+ let body;
9
+ if (data) {
10
+ requestHeaders.set('Content-Type', contentType ? contentType : 'application/json');
11
+ if (contentType === 'application/x-www-form-urlencoded') {
12
+ body = new URLSearchParams(data);
13
+ }
14
+ else {
15
+ body = JSON.stringify(data);
16
+ }
17
+ }
18
+ const opts = {
19
+ method,
20
+ headers: requestHeaders,
21
+ body,
22
+ };
23
+ try {
24
+ const res = await fetch(path, opts);
25
+ if (res.ok || res.status === 200 || res.status === 422) {
26
+ const text = await res.text();
27
+ if (xml) {
28
+ return text;
29
+ }
30
+ return text ? JSON.parse(text) : {};
31
+ }
32
+ logger?.error('[' + new Date() + '] api error', res.status);
33
+ return null;
34
+ }
35
+ catch (error) {
36
+ logger?.error('[' + new Date() + '] error in API, options: ' + opts);
37
+ logger?.error(error);
38
+ if (error instanceof Error && error.name === 'AbortError') {
39
+ console.log('request was aborted');
40
+ }
41
+ }
42
+ };
43
+ return {
44
+ get: (path, token) => send({ method: 'GET', path, token }),
45
+ getXml: (path, token) => send({ method: 'GET', path, token, xml: true }),
46
+ del: (path, token) => send({ method: 'DELETE', path, token }),
47
+ post: (path, data, token, xml, contentType) => send({ method: 'POST', path, data, token, xml, contentType }),
48
+ put: (path, data, token) => send({ method: 'PUT', path, data, token }),
49
+ };
50
+ };
51
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,EAC/B,SAAS,EACT,MAAM,GACkB,EAAE,EAAE;IAC5B,MAAM,IAAI,GAAG,KAAK,EAAE,EAClB,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,GAAG,EACH,WAAW,GACC,EAAE,EAAE;QAChB,MAAM,cAAc,GAAG,IAAI,OAAO,EAAE,CAAC;QACrC,cAAc,CAAC,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAE5C,IAAI,KAAK,EAAE,CAAC;YACV,cAAc,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,IAAI,IAA0C,CAAC;QAE/C,IAAI,IAAI,EAAE,CAAC;YACT,cAAc,CAAC,GAAG,CAChB,cAAc,EACd,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAC/C,CAAC;YACF,IAAI,WAAW,KAAK,mCAAmC,EAAE,CAAC;gBACxD,IAAI,GAAG,IAAI,eAAe,CAAC,IAA8B,CAAC,CAAC;YAC7D,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAgB;YACxB,MAAM;YACN,OAAO,EAAE,cAAc;YACvB,IAAI;SACL,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACpC,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC9B,IAAI,GAAG,EAAE,CAAC;oBACR,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,CAAC;YAED,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,GAAG,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5D,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,GAAG,2BAA2B,GAAG,IAAI,CAAC,CAAC;YACrE,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAErB,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC1D,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,GAAG,EAAE,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC1E,MAAM,EAAE,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CACtC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;QACjD,GAAG,EAAE,CAAC,IAAY,EAAE,KAAa,EAAE,EAAE,CACnC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QACzC,IAAI,EAAE,CACJ,IAAY,EACZ,IAA6B,EAC7B,KAAa,EACb,GAAa,EACb,WAAoB,EACpB,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;QAClE,GAAG,EAAE,CAAC,IAAY,EAAE,IAA6B,EAAE,KAAY,EAAE,EAAE,CACjE,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;KAC7C,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import mongoose from 'mongoose';
2
+ export declare const buildAuditChanges: ({ beforeData, afterData, fieldsToCheck, protectedFields, }: {
3
+ beforeData: Record<string, unknown>;
4
+ afterData: Record<string, unknown>;
5
+ fieldsToCheck: string[];
6
+ protectedFields?: string[];
7
+ }) => {
8
+ field: string;
9
+ before: unknown;
10
+ after: unknown;
11
+ }[];
12
+ export declare const createAuditLog: ({ entityType, entityId, action, changedBy, changes, meta, save, }: {
13
+ entityType: string;
14
+ entityId: mongoose.Types.ObjectId | string;
15
+ action: string;
16
+ changedBy?: mongoose.Types.ObjectId | string;
17
+ changes: Array<{
18
+ field: string;
19
+ before: unknown;
20
+ after: unknown;
21
+ }>;
22
+ meta?: Record<string, unknown>;
23
+ save: (payload: {
24
+ entityType: string;
25
+ entityId: mongoose.Types.ObjectId | string;
26
+ action: string;
27
+ changedBy?: mongoose.Types.ObjectId | string;
28
+ changes: Array<{
29
+ field: string;
30
+ before: unknown;
31
+ after: unknown;
32
+ }>;
33
+ meta?: Record<string, unknown>;
34
+ }) => Promise<unknown>;
35
+ }) => Promise<unknown>;
36
+ //# sourceMappingURL=audit-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-log.d.ts","sourceRoot":"","sources":["../src/audit-log.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AA6BhC,eAAO,MAAM,iBAAiB,GAAI,4DAK/B;IACD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;;;;GAWA,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,mEAQlC;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC;IAC7C,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACnE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,CAAC,OAAO,EAAE;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC;QAC3C,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC;QAC7C,OAAO,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QACnE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB,qBAaA,CAAC"}
@@ -0,0 +1,45 @@
1
+ import mongoose from 'mongoose';
2
+ const normalizeValue = (value) => {
3
+ if (value instanceof mongoose.Types.ObjectId) {
4
+ return value.toString();
5
+ }
6
+ if (Array.isArray(value)) {
7
+ return value.map((item) => normalizeValue(item));
8
+ }
9
+ if (value && typeof value === 'object') {
10
+ const doc = value;
11
+ if (typeof doc.toObject === 'function') {
12
+ return normalizeValue(doc.toObject());
13
+ }
14
+ }
15
+ return value;
16
+ };
17
+ const valuesAreEqual = (left, right) => {
18
+ return (JSON.stringify(normalizeValue(left)) ===
19
+ JSON.stringify(normalizeValue(right)));
20
+ };
21
+ export const buildAuditChanges = ({ beforeData, afterData, fieldsToCheck, protectedFields = [], }) => {
22
+ const protectedSet = new Set(protectedFields);
23
+ return fieldsToCheck
24
+ .filter((field) => !protectedSet.has(field))
25
+ .filter((field) => !valuesAreEqual(beforeData[field], afterData[field]))
26
+ .map((field) => ({
27
+ field,
28
+ before: normalizeValue(beforeData[field]),
29
+ after: normalizeValue(afterData[field]),
30
+ }));
31
+ };
32
+ export const createAuditLog = async ({ entityType, entityId, action, changedBy, changes, meta, save, }) => {
33
+ if (!changes.length) {
34
+ return null;
35
+ }
36
+ return save({
37
+ entityType,
38
+ entityId,
39
+ action,
40
+ changedBy,
41
+ changes,
42
+ meta,
43
+ });
44
+ };
45
+ //# sourceMappingURL=audit-log.js.map