work-license-server 1.0.17 → 1.0.19
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/assets/admin.js +13 -9
- package/assets/landing.js +66 -10
- package/bin/server.mjs +1 -1
- package/package.json +1 -1
- package/src/admin.js +7 -7
- package/src/server.js +1 -1
- package/src/store.js +12 -12
package/assets/admin.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
}
|
|
36
36
|
catch { return readCookieSession(); }
|
|
37
37
|
}
|
|
38
|
-
function setStoredToken(tok, email = '') {
|
|
38
|
+
function setStoredToken(tok, email = '', userObj = null) {
|
|
39
39
|
adminToken = tok || '';
|
|
40
40
|
try {
|
|
41
41
|
if (tok) {
|
|
@@ -49,12 +49,16 @@
|
|
|
49
49
|
sessionStorage.removeItem(TOKEN_KEY);
|
|
50
50
|
localStorage.removeItem(TOKEN_KEY);
|
|
51
51
|
localStorage.removeItem('odant_work_session');
|
|
52
|
+
localStorage.removeItem('odant_work_user_cache');
|
|
52
53
|
sessionStorage.removeItem('odant_work_session');
|
|
53
54
|
document.cookie = 'odant_work_session=; path=/; max-age=0; SameSite=Lax';
|
|
54
55
|
}
|
|
55
56
|
if (email) {
|
|
56
57
|
localStorage.setItem('odant_work_email', email);
|
|
57
58
|
}
|
|
59
|
+
if (userObj && typeof userObj === 'object') {
|
|
60
|
+
localStorage.setItem('odant_work_user_cache', JSON.stringify(userObj));
|
|
61
|
+
}
|
|
58
62
|
}
|
|
59
63
|
catch { /* приватный режим */ }
|
|
60
64
|
}
|
|
@@ -121,7 +125,7 @@
|
|
|
121
125
|
ind.style.width = active.offsetWidth + 'px';
|
|
122
126
|
}
|
|
123
127
|
|
|
124
|
-
function showAdminApp(login = '
|
|
128
|
+
function showAdminApp(login = 'Администратор') {
|
|
125
129
|
$('#adminLoginScreen').style.display = 'none';
|
|
126
130
|
$('#adminAppShell').style.display = '';
|
|
127
131
|
if ($('#admUserLabel')) $('#admUserLabel').textContent = login;
|
|
@@ -187,9 +191,9 @@
|
|
|
187
191
|
throw new Error(user.error || 'Неверный код подтверждения');
|
|
188
192
|
}
|
|
189
193
|
if (!user.isAdmin) {
|
|
190
|
-
throw new Error(`Аккаунт ${user.email} не обладает правами администратора
|
|
194
|
+
throw new Error(`Аккаунт ${user.email} не обладает правами администратора сервера лицензирования.`);
|
|
191
195
|
}
|
|
192
|
-
setStoredToken(user.sessionKey, user.email);
|
|
196
|
+
setStoredToken(user.sessionKey, user.email, user);
|
|
193
197
|
resetAdmStep();
|
|
194
198
|
showAdminApp(user.email);
|
|
195
199
|
await refreshRegistry();
|
|
@@ -305,7 +309,7 @@
|
|
|
305
309
|
if ($('#navAdmServers')) $('#navAdmServers').textContent = `Серверы и лицензии (${licenses.length})`;
|
|
306
310
|
if ($('#navAdmFranchises')) $('#navAdmFranchises').textContent = `Франшизы (${activeFranchises.length})`;
|
|
307
311
|
if ($('#navAdmOrders')) $('#navAdmOrders').textContent = `Заказы и счета (${orders.length})`;
|
|
308
|
-
if ($('#navAdmUsers')) $('#navAdmUsers').textContent = `Клиенты и
|
|
312
|
+
if ($('#navAdmUsers')) $('#navAdmUsers').textContent = `Клиенты и администраторы (${users.length})`;
|
|
309
313
|
if ($('#navAdmTickets')) $('#navAdmTickets').textContent = `Тикеты поддержки (${openTickets.length})`;
|
|
310
314
|
|
|
311
315
|
renderServersTable(licenses);
|
|
@@ -347,7 +351,7 @@
|
|
|
347
351
|
const baseHtml = l.baseActivated
|
|
348
352
|
? `<span class="tag ok">✓ Активна (${l.baseSeats || 10} вечн. + ИИ)</span>
|
|
349
353
|
<div style="margin-top:4px"><button class="btn btn-ghost btn-sm" style="padding:3px 8px;font-size:11.5px;color:var(--danger)" data-adm-base="${esc(lid)}" data-activate="0">Отозвать базовую</button></div>`
|
|
350
|
-
: `<span class="tag">Не активирована (1 сессия
|
|
354
|
+
: `<span class="tag">Не активирована (1 сессия)</span>
|
|
351
355
|
<div style="margin-top:4px"><button class="btn btn-ok btn-sm" style="padding:3px 8px;font-size:11.5px" data-adm-base="${esc(lid)}" data-activate="1">Активировать базовую</button></div>`;
|
|
352
356
|
|
|
353
357
|
const hwidHtml = hwidBound
|
|
@@ -539,7 +543,7 @@
|
|
|
539
543
|
}).join('') || '<tr><td colspan="6" class="muted">Заказов по заданному фильтру не найдено</td></tr>';
|
|
540
544
|
}
|
|
541
545
|
|
|
542
|
-
/* 4. Таблица клиентов и
|
|
546
|
+
/* 4. Таблица клиентов и администраторов */
|
|
543
547
|
function renderUsersTable(users = []) {
|
|
544
548
|
const tbody = $('#admUsersBody');
|
|
545
549
|
if (!tbody) return;
|
|
@@ -558,8 +562,8 @@
|
|
|
558
562
|
<td><b>${esc(u.email)}</b><div class="small muted"><code>${esc(u.userId)}</code></div></td>
|
|
559
563
|
<td>
|
|
560
564
|
${u.isAdmin
|
|
561
|
-
? `<span class="tag ok"
|
|
562
|
-
: `<span class="tag">Клиент</span> <button class="btn btn-ghost btn-sm" style="margin-left:6px" data-toggle-admin="${esc(u.userId)}" data-make-admin="1"
|
|
565
|
+
? `<span class="tag ok">Администратор</span> <button class="btn btn-ghost btn-sm" style="margin-left:6px" data-toggle-admin="${esc(u.userId)}" data-make-admin="0">Снять права</button>`
|
|
566
|
+
: `<span class="tag">Клиент</span> <button class="btn btn-ghost btn-sm" style="margin-left:6px" data-toggle-admin="${esc(u.userId)}" data-make-admin="1">Сделать администратором</button>`}
|
|
563
567
|
</td>
|
|
564
568
|
<td><b>${esc(u.companyName || '—')}</b><div class="small muted">ИНН ${esc(u.inn || '—')}</div></td>
|
|
565
569
|
<td>Серверов: <b>${srvCount}</b> · Франшиз: <b>${frCount}</b></td>
|
package/assets/landing.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
let REGIONS = (D.regions || []).slice();
|
|
15
15
|
let currentUser = null;
|
|
16
16
|
let currentStage = 'product';
|
|
17
|
+
let requestedInitialStage = '';
|
|
17
18
|
let selectedCode = null;
|
|
18
19
|
let pendingAuthAction = null;
|
|
19
20
|
|
|
@@ -137,7 +138,7 @@
|
|
|
137
138
|
}
|
|
138
139
|
catch { return ''; }
|
|
139
140
|
}
|
|
140
|
-
function persistSessionInBrowser(sessionKey = '', email = '', isAdmin = false) {
|
|
141
|
+
function persistSessionInBrowser(sessionKey = '', email = '', isAdmin = false, userObj = null) {
|
|
141
142
|
try {
|
|
142
143
|
if (sessionKey) {
|
|
143
144
|
localStorage.setItem('odant_work_session', sessionKey);
|
|
@@ -151,6 +152,9 @@
|
|
|
151
152
|
if (email) {
|
|
152
153
|
localStorage.setItem('odant_work_email', email);
|
|
153
154
|
}
|
|
155
|
+
if (userObj && typeof userObj === 'object') {
|
|
156
|
+
localStorage.setItem('odant_work_user_cache', JSON.stringify(userObj));
|
|
157
|
+
}
|
|
154
158
|
}
|
|
155
159
|
catch { /* приватный режим */ }
|
|
156
160
|
}
|
|
@@ -158,12 +162,57 @@
|
|
|
158
162
|
try {
|
|
159
163
|
localStorage.removeItem('odant_work_session');
|
|
160
164
|
localStorage.removeItem('odant_work_admin_token');
|
|
165
|
+
localStorage.removeItem('odant_work_user_cache');
|
|
161
166
|
sessionStorage.removeItem('odant_work_session');
|
|
162
167
|
sessionStorage.removeItem('odant_work_admin_token');
|
|
163
168
|
document.cookie = 'odant_work_session=; path=/; max-age=0; SameSite=Lax';
|
|
164
169
|
}
|
|
165
170
|
catch { /* ignore */ }
|
|
166
171
|
}
|
|
172
|
+
function hydrateCachedUser() {
|
|
173
|
+
try {
|
|
174
|
+
const key = localStorage.getItem('odant_work_session')
|
|
175
|
+
|| sessionStorage.getItem('odant_work_session')
|
|
176
|
+
|| localStorage.getItem('odant_work_admin_token')
|
|
177
|
+
|| readCookieSession()
|
|
178
|
+
|| '';
|
|
179
|
+
if (!key) return;
|
|
180
|
+
const raw = localStorage.getItem('odant_work_user_cache');
|
|
181
|
+
if (raw) {
|
|
182
|
+
const cached = JSON.parse(raw);
|
|
183
|
+
if (cached?.email) {
|
|
184
|
+
currentUser = { ...cached, sessionKey: key, ofertaAccepted: cached.ofertaAccepted !== false };
|
|
185
|
+
onAuthenticated();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Если есть подписанный токен wks_1.<payload>.<sig>, декодируем payload синхронно до ответа /api/auth/me
|
|
190
|
+
if (key.startsWith('wks_1.')) {
|
|
191
|
+
const parts = key.split('.');
|
|
192
|
+
if (parts[1]) {
|
|
193
|
+
const b64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
194
|
+
const json = decodeURIComponent(escape(atob(b64)));
|
|
195
|
+
const payload = JSON.parse(json);
|
|
196
|
+
if (payload?.email) {
|
|
197
|
+
currentUser = {
|
|
198
|
+
userId: payload.userId || '',
|
|
199
|
+
email: payload.email,
|
|
200
|
+
name: payload.name || payload.email,
|
|
201
|
+
companyName: payload.companyName || '',
|
|
202
|
+
inn: payload.inn || '',
|
|
203
|
+
isAdmin: Boolean(payload.isAdmin),
|
|
204
|
+
ofertaAccepted: true,
|
|
205
|
+
sessionKey: key,
|
|
206
|
+
servers: [],
|
|
207
|
+
franchises: [],
|
|
208
|
+
};
|
|
209
|
+
onAuthenticated();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch { /* ignore */ }
|
|
215
|
+
}
|
|
167
216
|
|
|
168
217
|
function openAuth(hintMsg = '', intentTitle = '') {
|
|
169
218
|
if ($('#authErrorMsg')) $('#authErrorMsg').textContent = hintMsg || '';
|
|
@@ -187,7 +236,7 @@
|
|
|
187
236
|
if ($('#authLoggedEmail')) $('#authLoggedEmail').textContent = currentUser.email || '';
|
|
188
237
|
if ($('#authLoggedRoleBadge')) {
|
|
189
238
|
$('#authLoggedRoleBadge').innerHTML = currentUser.isAdmin
|
|
190
|
-
? '<span class="tag ok"
|
|
239
|
+
? '<span class="tag ok">Администратор портала</span>'
|
|
191
240
|
: '<span class="tag">Клиент ODANT-WORK</span>';
|
|
192
241
|
}
|
|
193
242
|
if ($('#btnGoAdminModal')) $('#btnGoAdminModal').style.display = currentUser.isAdmin ? '' : 'none';
|
|
@@ -286,7 +335,7 @@
|
|
|
286
335
|
try {
|
|
287
336
|
const data = await api('/api/auth/verify-code', { email, code });
|
|
288
337
|
currentUser = data;
|
|
289
|
-
persistSessionInBrowser(data.sessionKey, data.email, data.isAdmin);
|
|
338
|
+
persistSessionInBrowser(data.sessionKey, data.email, data.isAdmin, data);
|
|
290
339
|
resetAuthStep();
|
|
291
340
|
onAuthenticated();
|
|
292
341
|
closeAuth();
|
|
@@ -335,9 +384,12 @@
|
|
|
335
384
|
const data = await api('/api/auth/me?sessionKey=' + encodeURIComponent(key));
|
|
336
385
|
if (data.authenticated && data.user) {
|
|
337
386
|
currentUser = { ...data.user, sessionKey: data.user.sessionKey || key };
|
|
338
|
-
persistSessionInBrowser(currentUser.sessionKey, currentUser.email, currentUser.isAdmin);
|
|
387
|
+
persistSessionInBrowser(currentUser.sessionKey, currentUser.email, currentUser.isAdmin, currentUser);
|
|
339
388
|
onAuthenticated();
|
|
340
|
-
if (location.hash === '#addons' && isUserReadyForOrders())
|
|
389
|
+
if ((requestedInitialStage === 'addons' || location.hash === '#addons') && isUserReadyForOrders()) {
|
|
390
|
+
requestedInitialStage = '';
|
|
391
|
+
switchStage('addons', { scroll: false });
|
|
392
|
+
}
|
|
341
393
|
}
|
|
342
394
|
}
|
|
343
395
|
catch { /* сессия отозвана */ }
|
|
@@ -368,8 +420,8 @@
|
|
|
368
420
|
if ($('#authOfertaCheck')) $('#authOfertaCheck').checked = Boolean(currentUser.ofertaAccepted);
|
|
369
421
|
if ($('#userBadgeBox')) {
|
|
370
422
|
const orgInfo = currentUser.companyName ? `${esc(currentUser.companyName)}${currentUser.inn ? ' (ИНН ' + esc(currentUser.inn) + ')' : ''} · ` : '';
|
|
371
|
-
const
|
|
372
|
-
$('#userBadgeBox').innerHTML = `${
|
|
423
|
+
const adminBadge = currentUser.isAdmin ? `<span class="tag ok" style="margin-right:6px">Администратор</span> <a href="/admin" style="font-weight:700;margin-right:8px">Открыть Админ-панель →</a> · ` : '';
|
|
424
|
+
$('#userBadgeBox').innerHTML = `${adminBadge}${orgInfo}Аккаунт: <b>${esc(currentUser.email)}</b> · <a href="#" data-action="logout" style="color:var(--muted)">Выйти</a>`;
|
|
373
425
|
}
|
|
374
426
|
syncClientServerPicker();
|
|
375
427
|
renderCabinetServers();
|
|
@@ -454,7 +506,7 @@
|
|
|
454
506
|
|
|
455
507
|
const baseBadge = s.baseActivated
|
|
456
508
|
? `<span class="tag ok">✓ Базовая: ${s.baseSeats || 10} вечн. мест + ИИ</span>`
|
|
457
|
-
: `<span class="tag">Базовая не
|
|
509
|
+
: `<span class="tag">Базовая не активирована (1 сессия)</span>`;
|
|
458
510
|
|
|
459
511
|
const files = s.licenseFiles || [];
|
|
460
512
|
const activeAddons = s.activeAddons || [];
|
|
@@ -1834,14 +1886,18 @@
|
|
|
1834
1886
|
|
|
1835
1887
|
function start() {
|
|
1836
1888
|
applyTheme(document.documentElement.getAttribute('data-theme') || 'dark');
|
|
1889
|
+
requestedInitialStage = D.initialStage || location.hash.slice(1);
|
|
1890
|
+
hydrateCachedUser();
|
|
1837
1891
|
bind();
|
|
1838
1892
|
renderOrbit();
|
|
1839
1893
|
recalcBaseOrder();
|
|
1840
1894
|
recalcPartner();
|
|
1841
1895
|
renderRegionsTable();
|
|
1842
1896
|
initBackdrop();
|
|
1843
|
-
const
|
|
1844
|
-
|
|
1897
|
+
const targetStage = (requestedInitialStage === 'addons' && isUserReadyForOrders())
|
|
1898
|
+
? 'addons'
|
|
1899
|
+
: (requestedInitialStage && requestedInitialStage !== 'addons' && requestedInitialStage !== 'admin' ? requestedInitialStage : 'product');
|
|
1900
|
+
switchStage(targetStage, { scroll: false });
|
|
1845
1901
|
if (document.fonts?.ready) document.fonts.ready.then(moveIndicator);
|
|
1846
1902
|
initMap().then(() => selectRegion(selectedCode || 'RU-SVE'));
|
|
1847
1903
|
restoreSession();
|
package/bin/server.mjs
CHANGED
|
@@ -14,4 +14,4 @@ const server = await app.listen(port);
|
|
|
14
14
|
const addr = server.address();
|
|
15
15
|
const actualPort = typeof addr === 'object' && addr ? addr.port : port;
|
|
16
16
|
console.log(`[work.odant.org] Витрина ODANT-WORK: http://localhost:${actualPort}/`);
|
|
17
|
-
console.log(`[work.odant.org] Панель
|
|
17
|
+
console.log(`[work.odant.org] Панель администратора: http://localhost:${actualPort}/admin`);
|
package/package.json
CHANGED
package/src/admin.js
CHANGED
|
@@ -58,14 +58,14 @@ export function renderAdminPage({ publicKey = '', assetVersion = '' } = {}) {
|
|
|
58
58
|
<div class="modal-head" style="margin-bottom:18px">
|
|
59
59
|
<img src="/assets/odant-logo.png" alt="ODANT">
|
|
60
60
|
<div>
|
|
61
|
-
<span class="kicker">Защищённый
|
|
61
|
+
<span class="kicker">Защищённый контур</span>
|
|
62
62
|
<h1 class="h3" style="margin:4px 0 2px;font-size:21px">Админ-панель ODANT-WORK</h1>
|
|
63
63
|
<div class="small muted">Вход по вашему Email администратора с 6-значным кодом из письма</div>
|
|
64
64
|
</div>
|
|
65
65
|
</div>
|
|
66
66
|
<div id="admStepEmail">
|
|
67
67
|
<div class="field">
|
|
68
|
-
<label for="admLoginEmail">Email
|
|
68
|
+
<label for="admLoginEmail">Email администратора</label>
|
|
69
69
|
<input type="email" id="admLoginEmail" value="" placeholder="Введите ваш email…" autocomplete="email" required>
|
|
70
70
|
</div>
|
|
71
71
|
<button type="button" class="btn btn-primary btn-lg" style="width:100%" id="btnAdmSendCode" data-action="adm-send-code">
|
|
@@ -115,7 +115,7 @@ export function renderAdminPage({ publicKey = '', assetVersion = '' } = {}) {
|
|
|
115
115
|
|
|
116
116
|
<div class="top-actions">
|
|
117
117
|
<button class="icon-btn" id="btnTheme" title="Светлая / тёмная тема" aria-label="Переключить тему">${icon('sun')}</button>
|
|
118
|
-
<button class="btn btn-primary btn-sm" data-action="open-adm-profile" title="Аккаунт администратора">${icon('user')}<span id="admUserLabel"
|
|
118
|
+
<button class="btn btn-primary btn-sm" data-action="open-adm-profile" title="Аккаунт администратора">${icon('user')}<span id="admUserLabel">Администратор</span></button>
|
|
119
119
|
</div>
|
|
120
120
|
</header>
|
|
121
121
|
|
|
@@ -127,7 +127,7 @@ export function renderAdminPage({ publicKey = '', assetVersion = '' } = {}) {
|
|
|
127
127
|
<button type="button" class="sec-nav-item" data-adm-tab="franchises" id="navAdmFranchises">Франшизы (0)</button>
|
|
128
128
|
<div class="sec-nav-sep"></div>
|
|
129
129
|
<button type="button" class="sec-nav-item" data-adm-tab="orders" id="navAdmOrders">Заказы и счета (0)</button>
|
|
130
|
-
<button type="button" class="sec-nav-item" data-adm-tab="users" id="navAdmUsers">Клиенты и
|
|
130
|
+
<button type="button" class="sec-nav-item" data-adm-tab="users" id="navAdmUsers">Клиенты и администраторы (0)</button>
|
|
131
131
|
<button type="button" class="sec-nav-item" data-adm-tab="tickets" id="navAdmTickets">Тикеты поддержки (0)</button>
|
|
132
132
|
</aside>
|
|
133
133
|
|
|
@@ -223,10 +223,10 @@ export function renderAdminPage({ publicKey = '', assetVersion = '' } = {}) {
|
|
|
223
223
|
</div>
|
|
224
224
|
</div>
|
|
225
225
|
|
|
226
|
-
<!-- 4. КЛИЕНТЫ И
|
|
226
|
+
<!-- 4. КЛИЕНТЫ И АДМИНИСТРАТОРЫ -->
|
|
227
227
|
<div data-adm-sec="users" style="display:none">
|
|
228
228
|
<div class="sec-bar" style="margin-bottom:12px">
|
|
229
|
-
<h2 class="sec-title">Зарегистрированные клиенты и
|
|
229
|
+
<h2 class="sec-title">Зарегистрированные клиенты и администраторы</h2>
|
|
230
230
|
<div class="sec-controls">
|
|
231
231
|
<input type="search" id="inpFilterUsers" placeholder="Поиск по Email, организации, ИНН…">
|
|
232
232
|
</div>
|
|
@@ -416,7 +416,7 @@ export function renderAdminPage({ publicKey = '', assetVersion = '' } = {}) {
|
|
|
416
416
|
<div style="padding:14px 16px;border-radius:12px;border:1px solid var(--line-strong);background:var(--field);margin-bottom:14px">
|
|
417
417
|
<div class="small muted">Вы вошли как</div>
|
|
418
418
|
<div style="font-size:16px;font-weight:700;margin-top:2px" id="admProfileEmail">—</div>
|
|
419
|
-
<div style="margin-top:6px"><span class="tag ok"
|
|
419
|
+
<div style="margin-top:6px"><span class="tag ok">Администратор портала</span></div>
|
|
420
420
|
</div>
|
|
421
421
|
<div style="display:flex;flex-direction:column;gap:8px">
|
|
422
422
|
<a class="btn btn-primary" href="/#addons" style="width:100%;text-decoration:none">Перейти в Личный кабинет</a>
|
package/src/server.js
CHANGED
|
@@ -940,7 +940,7 @@ export function createLicenseServerApp(options = {}) {
|
|
|
940
940
|
const body = await readJsonBody(req);
|
|
941
941
|
const tok = extractBearerToken(req, body, url);
|
|
942
942
|
if (!store.verifyAdminToken(tok))
|
|
943
|
-
return sendJson(res, 401, { error: 'Требуется авторизация администратора
|
|
943
|
+
return sendJson(res, 401, { error: 'Требуется авторизация администратора' });
|
|
944
944
|
const updated = store.setUserAdminRole(body.userId || body.email, body.isAdmin !== false);
|
|
945
945
|
return sendJson(res, 200, { ok: true, user: updated });
|
|
946
946
|
}
|
package/src/store.js
CHANGED
|
@@ -149,14 +149,14 @@ export class LicenseStore {
|
|
|
149
149
|
login: userFromTok.email,
|
|
150
150
|
email: userFromTok.email,
|
|
151
151
|
userId: userFromTok.userId,
|
|
152
|
-
role: '
|
|
152
|
+
role: 'ADMIN',
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
if (clean === this.adminPassword && this.adminPassword.length >= 6) {
|
|
156
156
|
return {
|
|
157
157
|
authenticated: true,
|
|
158
158
|
login: this.adminLogin,
|
|
159
|
-
role: '
|
|
159
|
+
role: 'ADMIN',
|
|
160
160
|
};
|
|
161
161
|
}
|
|
162
162
|
const sess = this.adminSessions.get(clean);
|
|
@@ -169,12 +169,12 @@ export class LicenseStore {
|
|
|
169
169
|
return {
|
|
170
170
|
authenticated: true,
|
|
171
171
|
login: sess.login || this.adminLogin,
|
|
172
|
-
role: '
|
|
172
|
+
role: 'ADMIN',
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
|
-
* Гарантировать, что первый зарегистрированный на сервере пользователь является
|
|
177
|
+
* Гарантировать, что первый зарегистрированный на сервере пользователь является администратором портала.
|
|
178
178
|
*/
|
|
179
179
|
_ensureBootstrapAdmin() {
|
|
180
180
|
const allUsers = Object.values(this.state.users || {});
|
|
@@ -182,19 +182,19 @@ export class LicenseStore {
|
|
|
182
182
|
return null;
|
|
183
183
|
const existingAdmin = allUsers.find(u => u.isAdmin);
|
|
184
184
|
if (existingAdmin) {
|
|
185
|
-
existingAdmin.role = '
|
|
185
|
+
existingAdmin.role = 'ADMIN';
|
|
186
186
|
return existingAdmin;
|
|
187
187
|
}
|
|
188
188
|
const firstUser = [...allUsers].sort((a, b) => (a.createdAt || 0) - (b.createdAt || 0))[0];
|
|
189
189
|
if (firstUser) {
|
|
190
190
|
firstUser.isAdmin = true;
|
|
191
|
-
firstUser.role = '
|
|
191
|
+
firstUser.role = 'ADMIN';
|
|
192
192
|
}
|
|
193
193
|
return firstUser || null;
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
/**
|
|
197
|
-
* Назначить или снять права администратора
|
|
197
|
+
* Назначить или снять права администратора у пользователя по Email или userId.
|
|
198
198
|
*/
|
|
199
199
|
setUserAdminRole(emailOrUserId = '', isAdmin = true) {
|
|
200
200
|
const key = String(emailOrUserId || '').trim();
|
|
@@ -207,11 +207,11 @@ export class LicenseStore {
|
|
|
207
207
|
if (!nextAdmin && user.isAdmin) {
|
|
208
208
|
const otherAdmins = Object.values(this.state.users || {}).filter(u => u.isAdmin && u.userId !== user.userId);
|
|
209
209
|
if (!otherAdmins.length) {
|
|
210
|
-
throw new Error('Нельзя снять права
|
|
210
|
+
throw new Error('Нельзя снять права у единственного администратора сервера');
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
user.isAdmin = nextAdmin;
|
|
214
|
-
user.role = nextAdmin ? '
|
|
214
|
+
user.role = nextAdmin ? 'ADMIN' : 'CLIENT';
|
|
215
215
|
this._save();
|
|
216
216
|
return this._formatUserResponse(user);
|
|
217
217
|
}
|
|
@@ -1229,7 +1229,7 @@ export class LicenseStore {
|
|
|
1229
1229
|
if (user) {
|
|
1230
1230
|
if (verifiedPayload.isAdmin) {
|
|
1231
1231
|
user.isAdmin = true;
|
|
1232
|
-
user.role = '
|
|
1232
|
+
user.role = 'ADMIN';
|
|
1233
1233
|
}
|
|
1234
1234
|
user.sessionKey = key;
|
|
1235
1235
|
try { this._save(); } catch { /* ignore */ }
|
|
@@ -1456,7 +1456,7 @@ export class LicenseStore {
|
|
|
1456
1456
|
userId: user.userId,
|
|
1457
1457
|
email: user.email,
|
|
1458
1458
|
isAdmin: Boolean(user.isAdmin),
|
|
1459
|
-
role: user.isAdmin ? '
|
|
1459
|
+
role: user.isAdmin ? 'ADMIN' : 'CLIENT',
|
|
1460
1460
|
authProvider: user.authProvider || 'email',
|
|
1461
1461
|
name: user.name,
|
|
1462
1462
|
position: user.position || '',
|
|
@@ -1590,7 +1590,7 @@ export class LicenseStore {
|
|
|
1590
1590
|
const isFirstAdmin = !Object.values(this.state.users || {}).some(u => u.isAdmin);
|
|
1591
1591
|
if (isFirstAdmin) {
|
|
1592
1592
|
user.isAdmin = true;
|
|
1593
|
-
user.role = '
|
|
1593
|
+
user.role = 'ADMIN';
|
|
1594
1594
|
}
|
|
1595
1595
|
|
|
1596
1596
|
// Выпускаем криптографически подписанный бессрочный токен сессии (wks_1...), устойчивый к перезапускам сервера
|