feat: multi-user UI login with admin and monitor roles

Add sac_users table, JWT roles, settings/users API guarded for admin, Users page in UI, and sac_manage_user.py CLI.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-01 09:27:14 +10:00
parent bc77f82307
commit 56c469ddbd
20 changed files with 859 additions and 85 deletions
+19 -10
View File
@@ -29,8 +29,9 @@
</template>
<script setup lang="ts">
import { computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { clearToken } from "../api";
import { clearToken, isAdmin } from "../api";
import { APP_NAME, APP_VERSION } from "../version";
const appName = APP_NAME;
@@ -38,16 +39,24 @@ const appVersion = APP_VERSION;
const route = useRoute();
const router = useRouter();
const navItems = [
{ to: "/dashboard", label: "Обзор", icon: "▦", match: "exact" as const },
{ to: "/events", label: "События", icon: "", match: "prefix" as const },
{ to: "/reports", label: "Отчёты", icon: "", match: "prefix" as const },
{ to: "/problems", label: "Проблемы", icon: "!", match: "prefix" as const },
{ to: "/hosts", label: "Хосты", icon: "", match: "exact" as const },
{ to: "/settings", label: "Настройки", icon: "", match: "exact" as const },
];
const navItems = computed(() => {
const items = [
{ to: "/dashboard", label: "Обзор", icon: "", match: "exact" as const },
{ to: "/events", label: "События", icon: "", match: "prefix" as const },
{ to: "/reports", label: "Отчёты", icon: "", match: "prefix" as const },
{ to: "/problems", label: "Проблемы", icon: "!", match: "prefix" as const },
{ to: "/hosts", label: "Хосты", icon: "", match: "exact" as const },
];
if (isAdmin()) {
items.push(
{ to: "/settings", label: "Настройки", icon: "⚙", match: "exact" as const },
{ to: "/users", label: "Пользователи", icon: "👤", match: "exact" as const },
);
}
return items;
});
function isActive(item: (typeof navItems)[number]) {
function isActive(item: (typeof navItems.value)[number]) {
if (item.match === "exact") {
return route.path === item.to;
}