Fix API regressions and refresh project docs

This commit is contained in:
Artem Kokos
2026-05-15 23:12:28 +07:00
parent 654f64bb90
commit 13fba2fa44
19 changed files with 3258 additions and 964 deletions

View File

@@ -60,6 +60,7 @@
<h1 class="text-2xl font-black tracking-tight uppercase">Ignis<span class="text-orange-500">Core</span></h1>
<div class="flex items-center gap-2">
<span v-if="authName" class="text-[9px] mono text-slate-600">{{ authName }}</span>
<span v-if="isMaster" class="text-[9px] text-orange-500 font-bold uppercase">master</span>
<span v-if="!isAdmin" class="text-[9px] text-yellow-600 font-bold uppercase">гость</span>
<button @click="logout" class="text-[9px] text-slate-600 hover:text-red-400 uppercase font-bold tracking-widest transition-colors">выйти</button>
</div>
@@ -212,7 +213,7 @@
</section>
<!-- Гостевые API-ключи -->
<section class="glass p-6 rounded-2xl">
<section v-if="isMaster" class="glass p-6 rounded-2xl">
<h2 class="text-lg font-black mb-6 uppercase">Гостевые ключи</h2>
<!-- Создание -->
@@ -244,7 +245,7 @@
<span v-else class="text-[9px] text-slate-500 font-bold uppercase bg-slate-500/10 px-2 py-0.5 rounded">гость</span>
<span :class="k.active ? 'text-green-500' : 'text-red-500'" class="text-[9px] font-bold uppercase">{{ k.active ? 'активен' : 'отозван' }}</span>
</div>
<div class="mono text-[10px] text-slate-600 mt-1">{{ k.key.slice(0, 12) }}...{{ k.key.slice(-6) }}</div>
<div class="mono text-[10px] text-slate-600 mt-1">{{ k.display_key || (k.key.slice(0, 12) + '...' + k.key.slice(-6)) }}</div>
</div>
<div class="flex gap-1.5 opacity-0 group-hover:opacity-100 transition-opacity">
<button v-if="k.active" @click="revokeApiKey(k.key, k.name)" class="p-2 rounded-lg bg-red-900/20 hover:bg-red-900/40 text-red-400 text-xs font-bold transition-all" title="Отозвать">ОТОЗВАТЬ</button>
@@ -361,6 +362,7 @@
tempKey: '',
tab: 'control',
isAdmin: false,
isMaster: false,
authName: '',
groups: {}, devices: [], sliders: {},
newGroup: { id: '', name: '', macs: [] },
@@ -377,7 +379,7 @@
saveKey() {
if (this.tempKey) { this.apiKey = this.tempKey; localStorage.setItem('ignis_key', this.tempKey); this.initApp(); }
},
logout() { this.apiKey = ''; this.isAdmin = false; this.authName = ''; localStorage.removeItem('ignis_key'); location.reload(); },
logout() { this.apiKey = ''; this.isAdmin = false; this.isMaster = false; this.authName = ''; localStorage.removeItem('ignis_key'); location.reload(); },
toast(text, type = 'info', duration = 3000) {
const id = ++this.toastCounter; this.toasts.push({ id, text, type });
setTimeout(() => { this.toasts = this.toasts.filter(t => t.id !== id); }, duration);
@@ -391,7 +393,7 @@
const r = await fetch(url, { method, headers: { 'X-API-Key': this.apiKey, 'Content-Type': 'application/json' }, body: body ? JSON.stringify(body) : null });
if (r.status === 403) {
const err = await r.json().catch(() => ({}));
if (err.detail === 'Недостаточно прав') { this.toast('Нет прав', 'error'); return null; }
if (err.detail === 'Недостаточно прав' || err.detail === 'Требуется мастер-ключ') { this.toast('Нет прав', 'error'); return null; }
this.toast('Неверный API-ключ', 'error'); this.logout(); return null;
}
if (!r.ok) { const err = await r.json().catch(() => ({})); this.toast(err.detail || `Ошибка ${r.status}`, 'error'); return null; }
@@ -404,6 +406,7 @@
const auth = await this.request('/auth/me');
if (!auth) { this.isLoading = false; return; }
this.isAdmin = auth.is_admin;
this.isMaster = !!auth.is_master;
this.authName = auth.name;
await this.fetchData();
this.isLoading = false;
@@ -425,7 +428,7 @@
if (dData) this.devices = Array.isArray(dData) ? dData : Object.values(dData);
if (sData) this.allScenes = sData;
if (this.isAdmin) this.fetchTasks();
if (this.isAdmin) this.fetchApiKeys();
if (this.isMaster) this.fetchApiKeys();
} finally { this.isFetching = false; }
},
@@ -532,4 +535,4 @@
}).mount('#app')
</script>
</body>
</html>
</html>