diff --git a/awg-ui/mock-server.ts b/awg-ui/mock-server.ts index 2e1f5de..cabd5c8 100644 --- a/awg-ui/mock-server.ts +++ b/awg-ui/mock-server.ts @@ -129,7 +129,11 @@ app.post("/ui/masterkeys", (q, r) => { app.patch("/ui/masterkeys/:id", (q, r) => { const m = find(q.params.id); if (!m) return r.status(404).json({ error: "Не найден" }); if (q.body.servers !== undefined) { if (!Array.isArray(q.body.servers) || !q.body.servers.length) return r.status(400).json({ error: "Нужен хотя бы один сервер" }); m.servers = [...q.body.servers].sort(); } - if (q.body.label) m.label = q.body.label; if (q.body.device_limit) m.device_limit = q.body.device_limit; r.json(mkOut(m)); + if (q.body.label) m.label = q.body.label; if (q.body.device_limit) m.device_limit = q.body.device_limit; + // Как sub.ts: лишние устройства сверх лимита — отвязать, начиная с подключившихся последними. + const unbound = [...m.devs].sort((a, b) => b.created_at - a.created_at || b.id - a.id).slice(0, Math.max(0, m.devs.length - m.device_limit)).map(d => d.id); + m.devs = m.devs.filter(d => !unbound.includes(d.id)); + r.json({ ...mkOut(m), unbound }); }); app.post("/ui/masterkeys/:id/rotate", (q, r) => { const m = find(q.params.id); if (!m) return r.status(404).json({ error: "Не найден" }); m.secret = crypto.randomBytes(16); r.json({ id: m.id }); }); app.post("/ui/masterkeys/:id/rekey", (q, r) => { const m = find(q.params.id); if (!m) return r.status(404).json({ error: "Не найден" }); m.devs.forEach(d => d.rekey_requested = true); r.json({ id: m.id }); }); diff --git a/awg-ui/src/tabs/masterkeys/index.tsx b/awg-ui/src/tabs/masterkeys/index.tsx index 075cf9f..66d1335 100644 --- a/awg-ui/src/tabs/masterkeys/index.tsx +++ b/awg-ui/src/tabs/masterkeys/index.tsx @@ -103,6 +103,25 @@ export default function MasterKeysPage({ token, showMsg }: PageProps) { catch (e) { showMsg(errText(e)); } }, [fetchLink, showMsg]); + // Лимит ниже числа устройств: сервер сразу отвяжет лишних, начиная с подключившихся последними, — + // поэтому сперва спросить и назвать, кого именно. + const saveLimit = useCallback(async (k: MasterKey) => { + const limit = Number(limitEdit); + const list = devs[k.id] ?? []; + const extra = list.length - limit; + if (Number.isInteger(limit) && extra > 0) { + const names = [...list].sort((a, b) => b.created_at - a.created_at || b.id - a.id).slice(0, extra) + .map(d => `«${d.device_name || d.id}»`).join(', '); + if (!confirm(`Лимит ${limit} меньше, чем устройств (${list.length}). Будут отвязаны подключившиеся последними: ${names}. Продолжить?`)) return; + } + try { + const r = await apiFetch('PATCH', `/ui/masterkeys/${k.id}`, token, { device_limit: limit }); + const n: number = r.unbound?.length ?? 0; + showMsg(n ? `Лимит сохранён, отвязано устройств: ${n}` : 'Лимит сохранён'); + await load(); + } catch (e) { showMsg(errText(e)); } + }, [limitEdit, devs, token, load, showMsg]); + const fold = (id: number) => setFolded(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; }); @@ -159,8 +178,7 @@ export default function MasterKeysPage({ token, showMsg }: PageProps) {