This commit is contained in:
+11
-28
@@ -7,13 +7,10 @@ import {
|
||||
LogOut,
|
||||
MonitorSmartphone,
|
||||
Puzzle,
|
||||
Users,
|
||||
} from "@lucide/vue";
|
||||
import {
|
||||
AUTH_INVALID_EVENT,
|
||||
clearStoredToken,
|
||||
getCurrentUser,
|
||||
hasTokenMode,
|
||||
logout,
|
||||
} from "./api";
|
||||
import type { CloudUser } from "./types";
|
||||
@@ -22,26 +19,23 @@ import PasswordChangeScreen from "./views/PasswordChangeScreen.vue";
|
||||
import TasksView from "./views/TasksView.vue";
|
||||
import DevicesView from "./views/DevicesView.vue";
|
||||
import PluginsView from "./views/PluginsView.vue";
|
||||
import UsersView from "./views/UsersView.vue";
|
||||
|
||||
type ViewId = "tasks" | "devices" | "plugins" | "users";
|
||||
type ViewId = "tasks" | "devices" | "plugins";
|
||||
|
||||
const activeView = ref<ViewId>("tasks");
|
||||
const currentUser = ref<CloudUser | null>(null);
|
||||
const tokenMode = ref(false);
|
||||
const loading = ref(true);
|
||||
const authMessage = ref("");
|
||||
|
||||
const isAdmin = computed(
|
||||
() => currentUser.value?.scopes.includes("*") || currentUser.value?.scopes.includes("users:admin"),
|
||||
);
|
||||
const canAdminPlugins = computed(
|
||||
() =>
|
||||
tokenMode.value ||
|
||||
currentUser.value?.scopes.includes("*") ||
|
||||
currentUser.value?.scopes.includes("plugins:admin"),
|
||||
);
|
||||
const isAuthenticated = computed(() => currentUser.value !== null || tokenMode.value);
|
||||
const isAuthenticated = computed(() => currentUser.value !== null);
|
||||
const currentUserLabel = computed(() =>
|
||||
currentUser.value ? `${currentUser.value.display_name} (${currentUser.value.role})` : "",
|
||||
);
|
||||
const mustChangePassword = computed(() => currentUser.value?.must_change_password ?? false);
|
||||
const navItems = computed<{ id: ViewId; label: string; icon: Component }[]>(() => {
|
||||
const items: { id: ViewId; label: string; icon: Component }[] = [
|
||||
@@ -49,20 +43,16 @@ const navItems = computed<{ id: ViewId; label: string; icon: Component }[]>(() =
|
||||
{ id: "devices", label: "Devices", icon: MonitorSmartphone },
|
||||
{ id: "plugins", label: "Plugins", icon: Puzzle },
|
||||
];
|
||||
if (isAdmin.value) items.push({ id: "users", label: "Users", icon: Users });
|
||||
return items;
|
||||
});
|
||||
|
||||
async function initializeAuthentication() {
|
||||
loading.value = true;
|
||||
currentUser.value = null;
|
||||
tokenMode.value = hasTokenMode();
|
||||
if (!tokenMode.value) {
|
||||
try {
|
||||
currentUser.value = await getCurrentUser();
|
||||
} catch {
|
||||
// A missing session is the normal initial state.
|
||||
}
|
||||
try {
|
||||
currentUser.value = await getCurrentUser();
|
||||
} catch {
|
||||
// A missing session is the normal initial state.
|
||||
}
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -74,8 +64,7 @@ async function onAuthenticated() {
|
||||
|
||||
function onAuthInvalid() {
|
||||
currentUser.value = null;
|
||||
tokenMode.value = false;
|
||||
authMessage.value = "your session expired or credentials were rejected. sign in again.";
|
||||
authMessage.value = "your session expired. sign in again.";
|
||||
}
|
||||
|
||||
async function signOut() {
|
||||
@@ -84,15 +73,12 @@ async function signOut() {
|
||||
} catch {
|
||||
// Local state must still be cleared when the already-expired session rejects logout.
|
||||
}
|
||||
clearStoredToken();
|
||||
currentUser.value = null;
|
||||
tokenMode.value = false;
|
||||
authMessage.value = "";
|
||||
}
|
||||
|
||||
function onPasswordChanged() {
|
||||
currentUser.value = null;
|
||||
tokenMode.value = false;
|
||||
authMessage.value = "password changed. sign in with the new password.";
|
||||
}
|
||||
|
||||
@@ -111,8 +97,6 @@ const activeComponent = computed(() => {
|
||||
return DevicesView;
|
||||
case "plugins":
|
||||
return PluginsView;
|
||||
case "users":
|
||||
return UsersView;
|
||||
default:
|
||||
return TasksView;
|
||||
}
|
||||
@@ -139,12 +123,11 @@ const activeComponent = computed(() => {
|
||||
<component :is="item.icon" :size="14" /> {{ item.label }}
|
||||
</button>
|
||||
<div class="spacer" />
|
||||
<div class="dim">{{ currentUser ? `${currentUser.display_name} (${currentUser.role})` : "API token" }}</div>
|
||||
<div class="dim">{{ currentUserLabel }}</div>
|
||||
<button @click="signOut"><LogOut :size="14" /> Sign out</button>
|
||||
</nav>
|
||||
<main class="app-main">
|
||||
<PluginsView v-if="activeView === 'plugins'" :can-admin="canAdminPlugins" />
|
||||
<UsersView v-else-if="activeView === 'users' && isAdmin" />
|
||||
<component v-else :is="activeComponent" />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user