@andrey4emk/npm-app-back-b24 3.1.0 → 3.2.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/logs/logs.ts +50 -46
- package/package.json +1 -1
package/logs/logs.ts
CHANGED
|
@@ -19,6 +19,17 @@ interface LogLevelConfig {
|
|
|
19
19
|
/** Полная конфигурация логирования */
|
|
20
20
|
type LogConfig = Record<LogLevel, LogLevelConfig>;
|
|
21
21
|
|
|
22
|
+
// ==================== Константы B24 Chat ====================
|
|
23
|
+
|
|
24
|
+
/** ID бота в B24 для отправки error-логов */
|
|
25
|
+
const B24_BOT_ID = 66600;
|
|
26
|
+
|
|
27
|
+
/** CLIENT_ID бота в B24 */
|
|
28
|
+
const B24_CLIENT_ID = "6rf74zyu842h6op1z186b08k7aavqq7z";
|
|
29
|
+
|
|
30
|
+
/** Лимит длины сообщения для B24 чата (безопасный, с запасом на BB-обёртку) */
|
|
31
|
+
const B24_TEXT_LIMIT = 4000;
|
|
32
|
+
|
|
22
33
|
// ==================== Настройки ====================
|
|
23
34
|
|
|
24
35
|
const configDir: string = process.env.CONFIG_DIR || "../config";
|
|
@@ -62,19 +73,19 @@ class LogsAPI {
|
|
|
62
73
|
|
|
63
74
|
private readonly resetColor = "\x1b[0m";
|
|
64
75
|
|
|
65
|
-
/**
|
|
66
|
-
private readonly
|
|
76
|
+
/** Webhook URL для отправки error-логов в B24 чат */
|
|
77
|
+
private readonly b24WebhookUrl: string | undefined;
|
|
67
78
|
|
|
68
|
-
/** ID чата
|
|
69
|
-
private readonly
|
|
79
|
+
/** ID чата B24 для отправки error-логов (формат: chatXXXXXX) */
|
|
80
|
+
private readonly b24ChatId: string | undefined;
|
|
70
81
|
|
|
71
82
|
/** Имя приложения для заголовка сообщения */
|
|
72
83
|
private readonly appName: string;
|
|
73
84
|
|
|
74
85
|
constructor() {
|
|
75
86
|
this.initConfig();
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
87
|
+
this.b24WebhookUrl = process.env.LOG_B24_WEBHOOK_URL;
|
|
88
|
+
this.b24ChatId = process.env.LOG_B24_CHAT_ID;
|
|
78
89
|
this.appName = process.env.APP_NAME || "Unknown App";
|
|
79
90
|
}
|
|
80
91
|
|
|
@@ -89,6 +100,12 @@ class LogsAPI {
|
|
|
89
100
|
|
|
90
101
|
/**
|
|
91
102
|
* Добавляет сообщение в лог
|
|
103
|
+
*
|
|
104
|
+
* Важно: при уровне `error` сообщение отправляется в B24 чат (через webhook)
|
|
105
|
+
* независимо от настройки `error.enabled` в `log.json`. Настройка `enabled`
|
|
106
|
+
* управляет только выводом в консоль. Это сделано намеренно, чтобы критические
|
|
107
|
+
* ошибки всегда доходили до команды даже при отключённом консольном логировании.
|
|
108
|
+
*
|
|
92
109
|
* @param message - Текст сообщения или объект для вывода
|
|
93
110
|
* @param level - Уровень логирования (trace, debug, info, warn, error)
|
|
94
111
|
* @param jsonData - Дополнительные данные для вывода (опционально)
|
|
@@ -97,9 +114,9 @@ class LogsAPI {
|
|
|
97
114
|
const levelStr: LogLevel = level || "info";
|
|
98
115
|
const messageText = typeof message === "string" ? message : JSON.stringify(message);
|
|
99
116
|
|
|
100
|
-
// Отправляем error-логи в
|
|
117
|
+
// Отправляем error-логи в B24 чат независимо от настроек консольного вывода
|
|
101
118
|
if (levelStr === "error") {
|
|
102
|
-
this.
|
|
119
|
+
this.sendToB24Chat(messageText, jsonData);
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
// Проверяем конфиг, и если логирование отключено, то выходим
|
|
@@ -123,22 +140,26 @@ class LogsAPI {
|
|
|
123
140
|
}
|
|
124
141
|
}
|
|
125
142
|
|
|
126
|
-
/**
|
|
127
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Убирает BB-теги для plain text отправки (при обрезке текста)
|
|
145
|
+
*/
|
|
146
|
+
private stripBbCodes(text: string): string {
|
|
147
|
+
return text.replace(/\[\/?(b|code)\]/g, "");
|
|
148
|
+
}
|
|
128
149
|
|
|
129
150
|
/**
|
|
130
|
-
* Отправляет
|
|
151
|
+
* Отправляет error-сообщение в чат B24 через webhook (fire-and-forget)
|
|
131
152
|
* Использует нативный fetch, а не fetchRetry — во избежание циклической зависимости
|
|
132
153
|
* Ошибки не пробрасываются, только console.error
|
|
133
154
|
*/
|
|
134
|
-
private
|
|
135
|
-
if (!this.
|
|
155
|
+
private sendToB24Chat(message: string, jsonData?: unknown): void {
|
|
156
|
+
if (!this.b24WebhookUrl || !this.b24ChatId) return;
|
|
136
157
|
|
|
137
|
-
|
|
158
|
+
// Нормализуем URL — гарантируем trailing slash
|
|
159
|
+
const baseUrl = this.b24WebhookUrl.endsWith("/") ? this.b24WebhookUrl : `${this.b24WebhookUrl}/`;
|
|
160
|
+
const url = `${baseUrl}imbot.message.add.json`;
|
|
138
161
|
|
|
139
|
-
|
|
140
|
-
const escapedMessage = this.escapeHtml(message);
|
|
141
|
-
let text = `<b>ERROR</b> | ${this.escapeHtml(this.appName)}\n${escapedMessage}`;
|
|
162
|
+
let text = `[b]ERROR[/b] | ${this.appName}\n${message}`;
|
|
142
163
|
|
|
143
164
|
// Добавляем jsonData если есть
|
|
144
165
|
if (jsonData !== undefined) {
|
|
@@ -149,59 +170,42 @@ class LogsAPI {
|
|
|
149
170
|
} catch {
|
|
150
171
|
jsonStr = "[Не удалось сериализовать jsonData]";
|
|
151
172
|
}
|
|
152
|
-
text += `\n
|
|
173
|
+
text += `\n[code]${jsonStr}[/code]`;
|
|
153
174
|
}
|
|
154
175
|
|
|
155
|
-
// Обрезаем до лимита
|
|
176
|
+
// Обрезаем до лимита B24
|
|
156
177
|
let isTruncated = false;
|
|
157
|
-
if (text.length >
|
|
178
|
+
if (text.length > B24_TEXT_LIMIT) {
|
|
158
179
|
const truncatedSuffix = "\n...(обрезано)";
|
|
159
|
-
text = text.slice(0,
|
|
180
|
+
text = text.slice(0, B24_TEXT_LIMIT - truncatedSuffix.length) + truncatedSuffix;
|
|
160
181
|
isTruncated = true;
|
|
161
182
|
}
|
|
162
183
|
|
|
163
|
-
// Если текст обрезан — отправляем
|
|
184
|
+
// Если текст обрезан — отправляем без BB-тегов, чтобы не разорвать [code]...[/code]
|
|
164
185
|
if (isTruncated) {
|
|
165
|
-
text = this.
|
|
186
|
+
text = this.stripBbCodes(text);
|
|
166
187
|
}
|
|
167
188
|
|
|
168
189
|
fetch(url, {
|
|
169
190
|
method: "POST",
|
|
170
191
|
headers: { "Content-Type": "application/json" },
|
|
171
192
|
body: JSON.stringify({
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
BOT_ID: B24_BOT_ID,
|
|
194
|
+
CLIENT_ID: B24_CLIENT_ID,
|
|
195
|
+
DIALOG_ID: this.b24ChatId,
|
|
196
|
+
MESSAGE: text,
|
|
175
197
|
}),
|
|
176
198
|
})
|
|
177
199
|
.then((response) => {
|
|
178
200
|
if (!response.ok) {
|
|
179
|
-
console.error(`Logs
|
|
201
|
+
console.error(`Logs B24 Chat: HTTP ${response.status}`);
|
|
180
202
|
}
|
|
181
203
|
})
|
|
182
204
|
.catch((error: unknown) => {
|
|
183
205
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
184
|
-
console.error(`Logs
|
|
206
|
+
console.error(`Logs B24 Chat: ошибка отправки — ${errMsg}`);
|
|
185
207
|
});
|
|
186
208
|
}
|
|
187
|
-
|
|
188
|
-
/** Экранирует HTML-спецсимволы для Telegram parse_mode HTML */
|
|
189
|
-
private escapeHtml(text: string): string {
|
|
190
|
-
return text
|
|
191
|
-
.replace(/&/g, "&")
|
|
192
|
-
.replace(/</g, "<")
|
|
193
|
-
.replace(/>/g, ">");
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** Убирает HTML-теги и декодирует HTML-entities для plain text отправки */
|
|
197
|
-
private stripHtml(text: string): string {
|
|
198
|
-
return text
|
|
199
|
-
.replace(/<\/?b>/g, "")
|
|
200
|
-
.replace(/<\/?pre>/g, "")
|
|
201
|
-
.replace(/</g, "<")
|
|
202
|
-
.replace(/>/g, ">")
|
|
203
|
-
.replace(/&/g, "&");
|
|
204
|
-
}
|
|
205
209
|
}
|
|
206
210
|
|
|
207
211
|
export const logs = new LogsAPI();
|