work-license-server 1.0.18 → 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 +7 -7
- package/assets/landing.js +4 -4
- 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
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
ind.style.width = active.offsetWidth + 'px';
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
function showAdminApp(login = '
|
|
128
|
+
function showAdminApp(login = 'Администратор') {
|
|
129
129
|
$('#adminLoginScreen').style.display = 'none';
|
|
130
130
|
$('#adminAppShell').style.display = '';
|
|
131
131
|
if ($('#admUserLabel')) $('#admUserLabel').textContent = login;
|
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
throw new Error(user.error || 'Неверный код подтверждения');
|
|
192
192
|
}
|
|
193
193
|
if (!user.isAdmin) {
|
|
194
|
-
throw new Error(`Аккаунт ${user.email} не обладает правами администратора
|
|
194
|
+
throw new Error(`Аккаунт ${user.email} не обладает правами администратора сервера лицензирования.`);
|
|
195
195
|
}
|
|
196
196
|
setStoredToken(user.sessionKey, user.email, user);
|
|
197
197
|
resetAdmStep();
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
if ($('#navAdmServers')) $('#navAdmServers').textContent = `Серверы и лицензии (${licenses.length})`;
|
|
310
310
|
if ($('#navAdmFranchises')) $('#navAdmFranchises').textContent = `Франшизы (${activeFranchises.length})`;
|
|
311
311
|
if ($('#navAdmOrders')) $('#navAdmOrders').textContent = `Заказы и счета (${orders.length})`;
|
|
312
|
-
if ($('#navAdmUsers')) $('#navAdmUsers').textContent = `Клиенты и
|
|
312
|
+
if ($('#navAdmUsers')) $('#navAdmUsers').textContent = `Клиенты и администраторы (${users.length})`;
|
|
313
313
|
if ($('#navAdmTickets')) $('#navAdmTickets').textContent = `Тикеты поддержки (${openTickets.length})`;
|
|
314
314
|
|
|
315
315
|
renderServersTable(licenses);
|
|
@@ -351,7 +351,7 @@
|
|
|
351
351
|
const baseHtml = l.baseActivated
|
|
352
352
|
? `<span class="tag ok">✓ Активна (${l.baseSeats || 10} вечн. + ИИ)</span>
|
|
353
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>`
|
|
354
|
-
: `<span class="tag">Не активирована (1 сессия
|
|
354
|
+
: `<span class="tag">Не активирована (1 сессия)</span>
|
|
355
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>`;
|
|
356
356
|
|
|
357
357
|
const hwidHtml = hwidBound
|
|
@@ -543,7 +543,7 @@
|
|
|
543
543
|
}).join('') || '<tr><td colspan="6" class="muted">Заказов по заданному фильтру не найдено</td></tr>';
|
|
544
544
|
}
|
|
545
545
|
|
|
546
|
-
/* 4. Таблица клиентов и
|
|
546
|
+
/* 4. Таблица клиентов и администраторов */
|
|
547
547
|
function renderUsersTable(users = []) {
|
|
548
548
|
const tbody = $('#admUsersBody');
|
|
549
549
|
if (!tbody) return;
|
|
@@ -562,8 +562,8 @@
|
|
|
562
562
|
<td><b>${esc(u.email)}</b><div class="small muted"><code>${esc(u.userId)}</code></div></td>
|
|
563
563
|
<td>
|
|
564
564
|
${u.isAdmin
|
|
565
|
-
? `<span class="tag ok"
|
|
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"
|
|
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>`}
|
|
567
567
|
</td>
|
|
568
568
|
<td><b>${esc(u.companyName || '—')}</b><div class="small muted">ИНН ${esc(u.inn || '—')}</div></td>
|
|
569
569
|
<td>Серверов: <b>${srvCount}</b> · Франшиз: <b>${frCount}</b></td>
|
package/assets/landing.js
CHANGED
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
if ($('#authLoggedEmail')) $('#authLoggedEmail').textContent = currentUser.email || '';
|
|
237
237
|
if ($('#authLoggedRoleBadge')) {
|
|
238
238
|
$('#authLoggedRoleBadge').innerHTML = currentUser.isAdmin
|
|
239
|
-
? '<span class="tag ok"
|
|
239
|
+
? '<span class="tag ok">Администратор портала</span>'
|
|
240
240
|
: '<span class="tag">Клиент ODANT-WORK</span>';
|
|
241
241
|
}
|
|
242
242
|
if ($('#btnGoAdminModal')) $('#btnGoAdminModal').style.display = currentUser.isAdmin ? '' : 'none';
|
|
@@ -420,8 +420,8 @@
|
|
|
420
420
|
if ($('#authOfertaCheck')) $('#authOfertaCheck').checked = Boolean(currentUser.ofertaAccepted);
|
|
421
421
|
if ($('#userBadgeBox')) {
|
|
422
422
|
const orgInfo = currentUser.companyName ? `${esc(currentUser.companyName)}${currentUser.inn ? ' (ИНН ' + esc(currentUser.inn) + ')' : ''} · ` : '';
|
|
423
|
-
const
|
|
424
|
-
$('#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>`;
|
|
425
425
|
}
|
|
426
426
|
syncClientServerPicker();
|
|
427
427
|
renderCabinetServers();
|
|
@@ -506,7 +506,7 @@
|
|
|
506
506
|
|
|
507
507
|
const baseBadge = s.baseActivated
|
|
508
508
|
? `<span class="tag ok">✓ Базовая: ${s.baseSeats || 10} вечн. мест + ИИ</span>`
|
|
509
|
-
: `<span class="tag">Базовая не
|
|
509
|
+
: `<span class="tag">Базовая не активирована (1 сессия)</span>`;
|
|
510
510
|
|
|
511
511
|
const files = s.licenseFiles || [];
|
|
512
512
|
const activeAddons = s.activeAddons || [];
|
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...), устойчивый к перезапускам сервера
|