@andrey4emk/npm-app-front-b24 0.2.0 → 0.3.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/ConfigLogs.vue +87 -22
- package/README.md +91 -7
- package/Subscriptions.vue +831 -279
- package/index.d.ts +39 -0
- package/package.json +6 -4
package/ConfigLogs.vue
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="config-logs">
|
|
3
|
-
<h2>Уровни логирования</h2>
|
|
3
|
+
<h2 class="config-logs__title">Уровни логирования</h2>
|
|
4
4
|
|
|
5
5
|
<div v-if="loading" class="config-logs__loading">Загрузка...</div>
|
|
6
6
|
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
</table>
|
|
48
48
|
|
|
49
49
|
<div class="config-logs__actions">
|
|
50
|
-
<button @click="saveConfig" :disabled="saving">
|
|
50
|
+
<button @click="saveConfig" :disabled="loading || saving">
|
|
51
51
|
{{ saving ? "Сохранение..." : "Сохранить" }}
|
|
52
52
|
</button>
|
|
53
|
-
<button @click="loadConfig" :disabled="loading">Обновить</button>
|
|
53
|
+
<button @click="loadConfig" :disabled="loading || saving">Обновить</button>
|
|
54
54
|
</div>
|
|
55
55
|
|
|
56
56
|
<div v-if="message" :class="['config-logs__message', messageType === 'ok' ? 'config-logs__message--success' : 'config-logs__message--error']">
|
|
@@ -113,34 +113,88 @@ function getUrl() {
|
|
|
113
113
|
return `${base}/api/config/${props.configName}`;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// Разбирает тело ответа: JSON — объект, любой другой Content-Type — null.
|
|
117
|
+
// Отдельная функция нужна, потому что сервер на неизвестный маршрут отвечает
|
|
118
|
+
// HTML-страницей с кодом 200, и слепой res.json() падает с невнятной ошибкой
|
|
119
|
+
async function parseJsonBody(res) {
|
|
120
|
+
const contentType = res.headers.get("content-type") || "";
|
|
121
|
+
if (!contentType.includes("application/json")) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
return await res.json();
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Текст ошибки по HTTP-статусу. Если сервер прислал message — он попадает в текст
|
|
132
|
+
// при любом коде, включая 401/403 и 5xx: иначе причина отказа теряется
|
|
133
|
+
function httpErrorText(res, json) {
|
|
134
|
+
if (res.status === 401 || res.status === 403) {
|
|
135
|
+
return json?.message
|
|
136
|
+
? `Нет доступа (${res.status}): ${json.message}`
|
|
137
|
+
: "Нет доступа, проверьте secretKey";
|
|
138
|
+
}
|
|
139
|
+
if (res.status >= 500) {
|
|
140
|
+
return json?.message
|
|
141
|
+
? `Ошибка на сервере (${res.status}): ${json.message}`
|
|
142
|
+
: `Ошибка на сервере (${res.status})`;
|
|
143
|
+
}
|
|
144
|
+
const detail = json?.message ? `: ${json.message}` : "";
|
|
145
|
+
return `Ошибка запроса (${res.status})${detail}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Раскладывает пришедший конфиг по реактивным уровням
|
|
149
|
+
function applyConfig(data) {
|
|
150
|
+
for (const level of levels) {
|
|
151
|
+
if (!data[level.key]) continue;
|
|
152
|
+
config[level.key].enabled = data[level.key].enabled ?? false;
|
|
153
|
+
config[level.key].json = data[level.key].json ?? true;
|
|
154
|
+
config[level.key].color = data[level.key].color ?? "";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
116
158
|
async function loadConfig() {
|
|
159
|
+
// Пока идёт загрузка или сохранение, повторный запуск запрещён:
|
|
160
|
+
// иначе ответы двух запросов затирают друг друга
|
|
161
|
+
if (loading.value || saving.value) return;
|
|
162
|
+
|
|
117
163
|
loading.value = true;
|
|
118
164
|
error.value = "";
|
|
119
165
|
message.value = "";
|
|
120
166
|
|
|
121
167
|
try {
|
|
122
168
|
const res = await fetch(getUrl(), { headers: getHeaders() });
|
|
123
|
-
const json = await res
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
169
|
+
const json = await parseJsonBody(res);
|
|
170
|
+
|
|
171
|
+
if (!res.ok) {
|
|
172
|
+
error.value = httpErrorText(res, json);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (!json) {
|
|
176
|
+
error.value = "Неожиданный ответ сервера: тело не JSON. Проверьте apiBase";
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (json.status !== "ok" || !json.data) {
|
|
134
180
|
error.value = json.message || "Не удалось загрузить конфиг";
|
|
181
|
+
return;
|
|
135
182
|
}
|
|
183
|
+
|
|
184
|
+
applyConfig(json.data);
|
|
136
185
|
} catch (e) {
|
|
137
|
-
|
|
186
|
+
// Сюда попадает только сбой сети: fetch не бросает на 4xx и 5xx
|
|
187
|
+
error.value = `Сервер недоступен: ${e.message}`;
|
|
138
188
|
} finally {
|
|
139
189
|
loading.value = false;
|
|
140
190
|
}
|
|
141
191
|
}
|
|
142
192
|
|
|
143
193
|
async function saveConfig() {
|
|
194
|
+
// Пока идёт загрузка или сохранение, повторный запуск запрещён:
|
|
195
|
+
// иначе ответы двух запросов затирают друг друга
|
|
196
|
+
if (loading.value || saving.value) return;
|
|
197
|
+
|
|
144
198
|
saving.value = true;
|
|
145
199
|
message.value = "";
|
|
146
200
|
|
|
@@ -159,17 +213,28 @@ async function saveConfig() {
|
|
|
159
213
|
headers: getHeaders(),
|
|
160
214
|
body: JSON.stringify(payload),
|
|
161
215
|
});
|
|
162
|
-
const json = await res
|
|
216
|
+
const json = await parseJsonBody(res);
|
|
163
217
|
|
|
164
|
-
if (
|
|
165
|
-
message.value = json
|
|
166
|
-
messageType.value = "
|
|
167
|
-
|
|
218
|
+
if (!res.ok) {
|
|
219
|
+
message.value = httpErrorText(res, json);
|
|
220
|
+
messageType.value = "error";
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (!json) {
|
|
224
|
+
message.value = "Неожиданный ответ сервера: тело не JSON. Проверьте apiBase";
|
|
225
|
+
messageType.value = "error";
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (json.status !== "ok") {
|
|
168
229
|
message.value = json.message || "Ошибка сохранения";
|
|
169
230
|
messageType.value = "error";
|
|
231
|
+
return;
|
|
170
232
|
}
|
|
233
|
+
|
|
234
|
+
message.value = json.message || "Конфиг сохранён";
|
|
235
|
+
messageType.value = "ok";
|
|
171
236
|
} catch (e) {
|
|
172
|
-
message.value =
|
|
237
|
+
message.value = `Сервер недоступен: ${e.message}`;
|
|
173
238
|
messageType.value = "error";
|
|
174
239
|
} finally {
|
|
175
240
|
saving.value = false;
|
|
@@ -190,7 +255,7 @@ onMounted(() => {
|
|
|
190
255
|
background: #fafafa;
|
|
191
256
|
}
|
|
192
257
|
|
|
193
|
-
.config-
|
|
258
|
+
.config-logs__title {
|
|
194
259
|
margin: 0 0 16px 0;
|
|
195
260
|
font-size: 18px;
|
|
196
261
|
}
|
package/README.md
CHANGED
|
@@ -12,7 +12,9 @@ npm install @andrey4emk/npm-app-front-b24
|
|
|
12
12
|
|
|
13
13
|
### Subscriptions
|
|
14
14
|
|
|
15
|
-
Управление подписками на события и
|
|
15
|
+
Управление подписками на события и оффлайн-очередью событий Bitrix24: список подписок,
|
|
16
|
+
подписка и отписка, просмотр и очистка оффлайн-сущностей, повторная запись сущности
|
|
17
|
+
с последующим снятием из очереди.
|
|
16
18
|
|
|
17
19
|
```vue
|
|
18
20
|
<script setup>
|
|
@@ -24,11 +26,27 @@ import Subscriptions from "@andrey4emk/npm-app-front-b24/Subscriptions.vue";
|
|
|
24
26
|
</template>
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
**Props:**
|
|
30
|
+
|
|
31
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
32
|
+
|------|-----|-------------|----------|
|
|
33
|
+
| `b24` | Object | `null` | Клиент Bitrix24 REST API (`B24Frame` из `@bitrix24/b24jssdk`) |
|
|
34
|
+
| `authConnector` | String | `""` | Значение `auth_connector` для методов работы с событиями |
|
|
35
|
+
| `portalDomain` | String | `""` | Адрес портала для ссылок на сущности, `portal.bitrix24.ru` или с `https://` |
|
|
36
|
+
|
|
37
|
+
Клиент ищется в таком порядке: prop `b24`, затем `inject("$b24")` (если приложение сделало
|
|
38
|
+
`app.provide("$b24", client)`), затем глобальный `window.$b24`. Без клиента компонент показывает
|
|
39
|
+
сообщение «Клиент Bitrix24 не инициализирован» и не падает.
|
|
40
|
+
|
|
41
|
+
Адрес портала без `portalDomain` берётся из `client.getTargetOrigin()`, а если его нет, из
|
|
42
|
+
`client.auth.getAuthData().domain`.
|
|
43
|
+
|
|
44
|
+
Состояние вкладок хранится внутри компонента, глобальный миксин с `tabs` и `selectTab` больше
|
|
45
|
+
не нужен.
|
|
28
46
|
|
|
29
47
|
### ConfigLogs
|
|
30
48
|
|
|
31
|
-
Управление уровнями логирования через `config/log.json
|
|
49
|
+
Управление уровнями логирования через `config/log.json` на сервере.
|
|
32
50
|
|
|
33
51
|
```vue
|
|
34
52
|
<script setup>
|
|
@@ -48,17 +66,66 @@ import ConfigLogs from "@andrey4emk/npm-app-front-b24/ConfigLogs.vue";
|
|
|
48
66
|
| `secretKey` | String | `""` | Ключ авторизации (заголовок `secret`) |
|
|
49
67
|
| `configName` | String | `"log"` | Имя конфига (без `.json`) |
|
|
50
68
|
|
|
69
|
+
Компонент ходит на `GET` и `POST` `${apiBase}/api/config/${configName}` и ждёт JSON вида
|
|
70
|
+
`{ status: "ok", data: {...} }`. Ошибки сети, ответы 401/403 и 5xx и не-JSON тела показываются
|
|
71
|
+
пользователю с кодом и текстом сервера.
|
|
72
|
+
|
|
73
|
+
## Стили
|
|
74
|
+
|
|
75
|
+
Стили компонентов глобальные, без `scoped`, классы с префиксами `subscriptions__*` и
|
|
76
|
+
`config-logs__*`. Их можно переопределять из CSS приложения по имени класса.
|
|
77
|
+
|
|
78
|
+
В `Subscriptions` старые имена классов `tooltip`, `tooltipParent`, `switch`, `slider` остаются
|
|
79
|
+
на элементах вторым классом ради совместимости со стилями потребителей. Они будут сняты
|
|
80
|
+
в следующей minor-версии.
|
|
81
|
+
|
|
82
|
+
## Типы
|
|
83
|
+
|
|
84
|
+
Пакет содержит `index.d.ts` с объявлениями props обоих компонентов. Чтобы TypeScript
|
|
85
|
+
потребителя (Vite + vue-tsc) их подхватил, подключите объявления явно, иначе сработает общий
|
|
86
|
+
shim для `*.vue` и props станут `any`:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
// env.d.ts
|
|
90
|
+
/// <reference types="@andrey4emk/npm-app-front-b24" />
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
или в `tsconfig.json`:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"compilerOptions": {
|
|
98
|
+
"types": ["@andrey4emk/npm-app-front-b24"]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
51
103
|
## Требования
|
|
52
104
|
|
|
53
105
|
- Vue 3 (`^3.0.0`) — peer dependency
|
|
54
106
|
- Проект-потребитель должен поддерживать компиляцию `.vue` файлов (Vite / webpack + vue-loader)
|
|
55
|
-
- `luxon` (`^3.7.2`) — используется в Subscriptions для форматирования дат
|
|
56
107
|
|
|
57
|
-
|
|
108
|
+
Других зависимостей нет: даты форматируются через `Intl.DateTimeFormat`.
|
|
109
|
+
|
|
110
|
+
## История версий
|
|
111
|
+
|
|
112
|
+
### 0.3.0
|
|
113
|
+
|
|
114
|
+
- `Subscriptions`: состояние вкладок внутри компонента, props `b24`, `authConnector`,
|
|
115
|
+
`portalDomain`, адрес портала из клиента вместо захардкоженного домена, ссылки на
|
|
116
|
+
смарт-процессы
|
|
117
|
+
- `Subscriptions`: починены «Изменить» для смарт-процессов и «Удалить все», автообновление
|
|
118
|
+
останавливается при уходе со страницы, ошибки показываются на экране, подтверждение
|
|
119
|
+
удаления вторым кликом вместо `confirm()`
|
|
120
|
+
- `ConfigLogs`: разбор ответа сервера с проверкой `res.ok` и Content-Type
|
|
121
|
+
- Классы стилей с префиксами, добавлены стили тумблера
|
|
122
|
+
- Снята зависимость `luxon`
|
|
123
|
+
- Добавлен `index.d.ts` с типами props
|
|
58
124
|
|
|
59
|
-
|
|
125
|
+
### 0.2.0
|
|
60
126
|
|
|
61
|
-
|
|
127
|
+
Пакет поставляет исходные `.vue` файлы вместо скомпилированного бандла. Компиляция
|
|
128
|
+
происходит на стороне проекта-потребителя. При обновлении с `0.1.x` измените импорты:
|
|
62
129
|
|
|
63
130
|
```js
|
|
64
131
|
// Было (0.1.x)
|
|
@@ -68,3 +135,20 @@ import "@andrey4emk/npm-app-front-b24/style.css";
|
|
|
68
135
|
// Стало (0.2.0+)
|
|
69
136
|
import Subscriptions from "@andrey4emk/npm-app-front-b24/Subscriptions.vue";
|
|
70
137
|
```
|
|
138
|
+
|
|
139
|
+
## Публикация
|
|
140
|
+
|
|
141
|
+
Версия по semver: `patch` для исправлений, `minor` для новых функций, `major` для ломающих
|
|
142
|
+
изменений.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
git add .
|
|
146
|
+
git commit -m "upd(client): описание"
|
|
147
|
+
npm version minor # или patch / major
|
|
148
|
+
git push origin main --follow-tags
|
|
149
|
+
npm publish --access public
|
|
150
|
+
npm info @andrey4emk/npm-app-front-b24 version
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Если `npm publish` отвечает 403: проверить `npm whoami`, при необходимости `npm login`,
|
|
154
|
+
для scoped-пакета флаг `--access public` обязателен.
|