@andrey4emk/npm-app-back-b24 3.4.0 → 3.5.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
@@ -26,6 +26,24 @@
26
26
  npm install @andrey4emk/npm-app-back-b24
27
27
  ```
28
28
 
29
+ ### Точки входа
30
+
31
+ | Импорт | Что отдаёт | Поднимает OAuth-модуль |
32
+ | --------------------------------------------- | ----------------------------- | ---------------------- |
33
+ | `@andrey4emk/npm-app-back-b24` | всё | да |
34
+ | `@andrey4emk/npm-app-back-b24/logs` | `logs` | нет |
35
+ | `@andrey4emk/npm-app-back-b24/fetchRetry` | `fetchRetry`, `isNetworkError`, `maskUrl` | нет |
36
+
37
+ Корневой импорт — barrel: он тянет модуль OAuth, который при загрузке читает `authB24.json` и, если приложение авторизовано, запускает таймер проактивного обновления токена. Проектам, которые ходят в B24 по **входящему вебхуку** или берут из пакета только логгер и `fetchRetry`, удобнее импортировать по подпутям — тогда OAuth-модуль не загружается вообще.
38
+
39
+ ```js
40
+ // Проект без OAuth: ничего лишнего не поднимается
41
+ import { logs } from "@andrey4emk/npm-app-back-b24/logs";
42
+ import { fetchRetry, maskUrl } from "@andrey4emk/npm-app-back-b24/fetchRetry";
43
+ ```
44
+
45
+ Корневой импорт при этом остаётся безопасным: если `APP_B24_CLIENT_ID` и `APP_B24_CLIENT_SECRET` не заданы, пакет считает, что OAuth в проекте не используется, молча выставляет `$b24 = null` и пишет об этом только в `debug`.
46
+
29
47
  ## Использование
30
48
 
31
49
  ```js
@@ -46,6 +64,7 @@ import {
46
64
  logs,
47
65
  fetchRetry,
48
66
  isNetworkError,
67
+ maskUrl,
49
68
  } from "@andrey4emk/npm-app-back-b24";
50
69
 
51
70
  // $b24 — готовый экземпляр B24OAuth (или null, если токены не настроены)
@@ -116,6 +135,7 @@ const response = await fetchRetry("https://api.example.com/data", { method: "GET
116
135
  **Особенности:**
117
136
  - При обновлении токенов SDK автоматически сохраняет их в файл через callback `setCallbackRefreshAuth`.
118
137
  - Окружение (`DEV`/`PROD`) определяется переменной `APP_ENV` — используется как ключ секции в конфиге авторизации.
138
+ - Признак того, что проект использует OAuth, — заданные `APP_B24_CLIENT_ID` и `APP_B24_CLIENT_SECRET`. Если их нет, `$b24` молча становится `null` (уровень `debug`). Если они заданы, но токенов в `authB24.json` не хватает, пишется `error` — это уже настоящая проблема конфигурации.
119
139
 
120
140
  **Retry при сетевых ошибках:**
121
141
 
@@ -633,6 +653,15 @@ const response = await fetchRetry("https://api.example.com/data", { method: "GET
633
653
 
634
654
  - **`fetchRetry(url, options?, retries?, delay?)`** — обёртка над `fetch` с повторными попытками при сетевых ошибках. HTTP-ошибки (4xx, 5xx) **не** вызывают повторных попыток — повторяются только сетевые сбои (TypeError, ECONNRESET, ETIMEDOUT и т.д.).
635
655
 
656
+ - **`maskUrl(url)`** — маскирует секреты в адресе перед записью в лог. URL входящего вебхука B24 содержит секрет прямо в пути (`/rest/1/<секрет>/method.json`), а токен авторизации может приходить в query-параметрах (`auth`, `access_token`, `refresh_token`, `token`). Хост и имя метода сохраняются, тело секрета заменяется на `***`. Используется внутри `fetchRetry`; применяйте в своём коде везде, где логируете адреса запросов к B24.
657
+
658
+ ```js
659
+ import { maskUrl } from "@andrey4emk/npm-app-back-b24";
660
+
661
+ logs.add(`Запрос не прошёл — ${maskUrl(url)}`, "warn");
662
+ // https://portal.bitrix24.ru/rest/1/***/crm.deal.get.json
663
+ ```
664
+
636
665
  - **`isNetworkError(error)`** — проверяет, является ли ошибка сетевой (стоит повторить запрос). Используется внутри `fetchRetry` и retry-обёртки `$b24`. Можно использовать в своём коде для аналогичных проверок.
637
666
 
638
667
  ```js
@@ -661,7 +690,7 @@ const response = await fetchRetry("https://api.example.com/data", { method: "GET
661
690
  - **Обрабатываемые сетевые ошибки:**
662
691
  `ECONNRESET`, `ECONNREFUSED`, `ETIMEDOUT`, `ENOTFOUND`, `ENETUNREACH`, `EAI_AGAIN`, `UND_ERR_CONNECT_TIMEOUT`, `UND_ERR_SOCKET`, `TypeError`, а также ошибки с сообщениями `fetch failed`, `network`, `socket`.
663
692
 
664
- - **Логирование:** при каждой неудачной попытке записывает сообщение через `logs.add()` с уровнем `error`.
693
+ - **Логирование:** при каждой неудачной попытке записывает сообщение через `logs.add()` с уровнем `warn`. Адрес запроса перед записью пропускается через `maskUrl()`, поэтому секрет вебхука в лог не попадает.
665
694
 
666
695
  ```js
667
696
  import { fetchRetry } from "@andrey4emk/npm-app-back-b24";
package/bitrix24/b24.ts CHANGED
@@ -86,11 +86,12 @@ export function getResultData<T = any>(response: AjaxResult, methodName: string)
86
86
  // ==================== Создание экземпляра B24OAuth ====================
87
87
 
88
88
  function createB24Instance(): B24OAuth | null {
89
- const store = confAuthB24.store as Record<string, AuthData>;
90
- const authConfig = store[APP_ENV];
91
-
92
- if (!authConfig?.domain || !authConfig?.access_token || !authConfig?.refresh_token) {
93
- logs.add( конфиге authB24 не хватает данных для авторизации. Сохрани токены через клиент и перезагрузи докер", "error");
89
+ // Проект вообще не использует OAuth: ходит в B24 по входящему вебхуку либо берёт
90
+ // из пакета только logs/fetchRetry. Отсутствие авторизации для него — норма, а не
91
+ // ошибка, поэтому уровень debug: иначе ложная строка попадает в мониторинг.
92
+ // Признак «OAuth задуман» наличие credentials приложения в .env.
93
+ if (!CLIENT_ID && !CLIENT_SECRET) {
94
+ logs.add("OAuth Bitrix24 не настроен (APP_B24_CLIENT_ID и APP_B24_CLIENT_SECRET не заданы), $b24 = null", "debug");
94
95
  return null;
95
96
  }
96
97
 
@@ -99,6 +100,14 @@ function createB24Instance(): B24OAuth | null {
99
100
  return null;
100
101
  }
101
102
 
103
+ const store = confAuthB24.store as Record<string, AuthData>;
104
+ const authConfig = store[APP_ENV];
105
+
106
+ if (!authConfig?.domain || !authConfig?.access_token || !authConfig?.refresh_token) {
107
+ logs.add("В конфиге authB24 не хватает данных для авторизации. Сохрани токены через клиент и перезагрузи докер", "error");
108
+ return null;
109
+ }
110
+
102
111
  const domain = cleanDomain(authConfig.domain);
103
112
 
104
113
  const authParams: B24OAuthParams = {
@@ -410,7 +419,7 @@ export async function reinitializeB24(): Promise<SaveResult> {
410
419
  $b24 = _b24Raw ? wrapB24WithRetry(_b24Raw) : null;
411
420
 
412
421
  if (!_b24Raw || !$b24) {
413
- return { error: true, message: "Не удалось пересоздать $b24 — проверь authB24.json" };
422
+ return { error: true, message: "Не удалось пересоздать $b24 — проверь authB24.json и APP_B24_CLIENT_ID/APP_B24_CLIENT_SECRET" };
414
423
  }
415
424
 
416
425
  setupRefreshCallback(_b24Raw);
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@andrey4emk/npm-app-back-b24",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Bitrix24 OAuth helpers for Node.js projects",
5
5
  "main": "index.ts",
6
6
  "type": "module",
7
7
  "exports": {
8
- ".": "./index.ts"
8
+ ".": "./index.ts",
9
+ "./logs": "./logs/logs.ts",
10
+ "./fetchRetry": "./utils/fetchRetry.ts"
9
11
  },
10
12
  "scripts": {
11
13
  "test": "echo \"No tests\" && exit 0",
@@ -32,6 +32,29 @@ export function isNetworkError(error: unknown): boolean {
32
32
  return false;
33
33
  }
34
34
 
35
+ /**
36
+ * Маскирует секреты в URL перед записью в лог.
37
+ *
38
+ * URL входящего вебхука B24 содержит секрет прямо в пути
39
+ * (`https://портал.bitrix24.ru/rest/1/<секрет>/method.json`), а токен авторизации
40
+ * может приходить в query-параметрах. Хост и имя метода сохраняются — они нужны
41
+ * для диагностики, тело секрета заменяется на `***`.
42
+ *
43
+ * @param url — адрес запроса в любом виде, принимаемом fetch
44
+ * @returns строка URL, пригодная для логирования
45
+ */
46
+ export function maskUrl(url: string | URL | Request): string {
47
+ const raw = typeof url === "string" ? url : url instanceof URL ? url.href : url.url;
48
+
49
+ return (
50
+ raw
51
+ // Секрет входящего вебхука: /rest/<id пользователя>/<секрет>/
52
+ .replace(/(\/rest\/\d+\/)[^/?#]+/gi, "$1***")
53
+ // Токены в query-параметрах
54
+ .replace(/([?&](?:auth|access_token|refresh_token|token)=)[^&#]+/gi, "$1***")
55
+ );
56
+ }
57
+
35
58
  /**
36
59
  * Обёртка над fetch с повторными попытками при сетевых ошибках.
37
60
  * Повторяет запрос только при проблемах с сетью (TypeError, ECONNRESET и т.д.),
@@ -63,7 +86,7 @@ export async function fetchRetry(
63
86
  }
64
87
 
65
88
  logs.add(
66
- `fetchRetry: попытка ${attempt}/${retries} не удалась (${lastError.message}), повтор через ${delay}мс — ${String(url)}`,
89
+ `fetchRetry: попытка ${attempt}/${retries} не удалась (${lastError.message}), повтор через ${delay}мс — ${maskUrl(url)}`,
67
90
  "warn"
68
91
  );
69
92