Skip to content

Commit

Permalink
add permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
zAlweNy26 committed Jul 9, 2024
1 parent 30caac7 commit 60f6d22
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 24 deletions.
32 changes: 23 additions & 9 deletions src/components/Header.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
<script setup lang="ts"></script>
<script setup lang="ts">
const { can } = usePerms()
const showSettings = computed(
() =>
can('READ', 'LLM') ||
can('READ', 'USERS') ||
can('READ', 'EMBEDDER') ||
can('READ', 'STATUS') ||
can('LIST', 'LLM') ||
can('LIST', 'USERS') ||
can('LIST', 'EMBEDDER') ||
can('LIST', 'STATUS'),
)
</script>

<template>
<div class="navbar sticky top-0 z-30 min-h-fit bg-base-100 font-medium shadow-md md:px-[5%] lg:px-[10%]">
Expand All @@ -21,16 +35,16 @@
<MenuItems
as="ul"
class="menu menu-md absolute left-0 z-50 mt-4 w-min origin-top-left gap-2 whitespace-nowrap rounded-md bg-base-100 shadow-xl">
<MenuItem as="li">
<MenuItem v-if="can('READ', 'CONVERSATION') || can('LIST', 'CONVERSATION')" as="li">
<RouterLink to="/"> <heroicons-home-20-solid class="size-4" /> Home </RouterLink>
</MenuItem>
<MenuItem as="li">
<MenuItem v-if="can('READ', 'MEMORY') || can('LIST', 'MEMORY')" as="li">
<RouterLink :key="$route.fullPath" :to="{ path: '/memory' }"> <ph-brain-fill class="size-4" /> Memory </RouterLink>
</MenuItem>
<MenuItem as="li">
<MenuItem v-if="can('READ', 'PLUGINS') || can('LIST', 'PLUGINS')" as="li">
<RouterLink :key="$route.fullPath" :to="{ path: '/plugins' }"> <ph-plug-fill class="size-4" /> Plugins </RouterLink>
</MenuItem>
<MenuItem as="li">
<MenuItem v-if="showSettings" as="li">
<RouterLink :key="$route.fullPath" :to="{ path: '/settings' }" :class="{ active: $route.path === '/settings' }">
<heroicons-cog-6-tooth-20-solid class="size-4" /> Settings
</RouterLink>
Expand All @@ -47,16 +61,16 @@
<img src="@assets/logo.svg" class="size-12 cursor-pointer dark:brightness-0 dark:invert md:hidden" />
</RouterLink>
<ul class="menu menu-horizontal menu-md hidden gap-4 p-0 md:flex">
<li>
<li v-if="can('READ', 'CONVERSATION') || can('LIST', 'CONVERSATION')">
<RouterLink to="/"> <heroicons-home-20-solid class="size-4" /> Home </RouterLink>
</li>
<li>
<li v-if="can('READ', 'MEMORY') || can('LIST', 'MEMORY')">
<RouterLink :key="$route.fullPath" :to="{ path: '/memory' }"> <ph-brain-fill class="size-4" /> Memory </RouterLink>
</li>
<li>
<li v-if="can('READ', 'PLUGINS') || can('LIST', 'PLUGINS')">
<RouterLink :key="$route.fullPath" :to="{ path: '/plugins' }"> <ph-plug-fill class="size-4" /> Plugins </RouterLink>
</li>
<li>
<li v-if="showSettings">
<RouterLink :key="$route.fullPath" :to="{ path: '/settings' }" :class="{ active: $route.path === '/settings' }">
<heroicons-cog-6-tooth-20-solid class="size-4" /> Settings
</RouterLink>
Expand Down
3 changes: 2 additions & 1 deletion src/views/EmbeddersView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEmbedderConfig } from '@stores/useEmbedderConfig'
const storeEmbedder = useEmbedderConfig()
const { getEmbedderSchema, getEmbedderSettings, setEmbedderSettings, refreshSettings } = storeEmbedder
const { currentState: embedderState, getAvailableEmbedders } = storeToRefs(storeEmbedder)
const { cannot } = usePerms()
const selectedEmbedder = ref(embedderState.value.selected)
const currentSchema = ref<JSONSettings>()
const currentSettings = ref<JSONSettings>({})
Expand Down Expand Up @@ -52,6 +52,7 @@ watchDeep(
<SelectBox
v-model="selectedEmbedder"
:list="getAvailableEmbedders.map(p => ({ label: p.humanReadableName ?? p.title, value: p.title }))"
:disabled="cannot('LIST', 'EMBEDDER')"
@update="(e: any) => updateProperties(e.value)" />
<div class="flex grow flex-col gap-4">
<div class="flex items-center gap-1 font-medium">
Expand Down
7 changes: 4 additions & 3 deletions src/views/HomeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const route = useRoute()
const messagesStore = useMessages()
const { dispatchMessage, selectRandomDefaultMessages } = messagesStore
const { currentState: messagesState } = storeToRefs(messagesStore)
const { can, cannot } = usePerms()
const userMessage = ref(''),
insertedURL = ref(''),
isScrollable = ref(false),
Expand All @@ -33,7 +33,7 @@ const { currentState: rabbitHoleState } = storeToRefs(useRabbitHole())
const { wipeConversation } = useMemory()
const inputDisabled = computed(() => {
return messagesState.value.loading || !messagesState.value.ready || Boolean(messagesState.value.error)
return messagesState.value.loading || !messagesState.value.ready || Boolean(messagesState.value.error) || cannot('WRITE', 'CONVERSATION')
})
const randomDefaultMessages = selectRandomDefaultMessages()
Expand Down Expand Up @@ -227,7 +227,7 @@ const scrollToBottom = () => {
</p>
</div>
</div>
<div v-else class="flex grow cursor-pointer flex-col items-center justify-center gap-4 p-4">
<div v-else-if="can('WRITE', 'CONVERSATION')" class="flex grow cursor-pointer flex-col items-center justify-center gap-4 p-4">
<div
v-for="(msg, index) in randomDefaultMessages"
:key="index"
Expand All @@ -236,6 +236,7 @@ const scrollToBottom = () => {
{{ msg }}
</div>
</div>
<div v-else class="grow" />
<div class="fixed bottom-0 left-0 flex w-full items-center justify-center bg-gradient-to-t from-base-200 px-2 py-4">
<div class="flex w-full max-w-screen-xl items-center gap-2 md:gap-4">
<div class="dropdown dropdown-top">
Expand Down
7 changes: 4 additions & 3 deletions src/views/MemoryView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { VectorsData } from 'ccat-api'
import type { MarkerData, PlotData } from '@models/Plot'
const { isDark } = storeToRefs(useSettings())
const { cannot } = usePerms()
const callText = ref(''),
callOutput = ref<VectorsData>(),
kMems = ref(10)
Expand Down Expand Up @@ -192,7 +192,7 @@ const wipeMemory = async () => {
placeholder="Enter a text..."
label="Search similar memories"
search
:disabled="Boolean(memoryState.error) || memoryState.loading"
:disabled="Boolean(memoryState.error) || memoryState.loading || cannot('READ', 'MEMORY')"
@send="recallMemory()" />
<div class="form-control">
<label class="label px-0">
Expand Down Expand Up @@ -359,7 +359,7 @@ const wipeMemory = async () => {
<div class="divider !my-0" />
<div class="join w-fit self-center shadow-xl">
<button
:disabled="Boolean(memoryState.error) || memoryState.loading"
:disabled="Boolean(memoryState.error) || memoryState.loading || cannot('WRITE', 'MEMORY')"
class="btn btn-primary join-item hover:border-error hover:bg-error"
@click="boxWipe?.toggleModal()">
<heroicons-trash-solid class="size-4" />
Expand Down Expand Up @@ -406,6 +406,7 @@ const wipeMemory = async () => {
<button
v-if="clickedPoint && !['procedural', 'query'].includes(clickedPoint.collection)"
class="btn btn-primary btn-sm mt-auto hover:btn-error"
:disabled="cannot('DELETE', 'MEMORY')"
@click="deleteMemoryMarker(clickedPoint.collection, clickedPoint.id)">
<heroicons-trash-solid class="size-4" />
Delete memory point
Expand Down
17 changes: 13 additions & 4 deletions src/views/PluginsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { type SchemaField, type JSONSettings } from '@models/JSONSchema'
const store = usePlugins()
const { togglePlugin, removePlugin, updateSettings, getSchema, getSettings, searchPlugin, installRegistryPlugin } = store
const { currentState: pluginsState } = storeToRefs(store)
const { can, cannot } = usePerms()
const { pluginsFilters } = storeToRefs(useSettings())
const { upload: uploadFile } = uploadContent()
Expand Down Expand Up @@ -122,7 +122,7 @@ watchEffect(() => {
Create plugin
</a>
<button
:disabled="pluginsState.loading || Boolean(pluginsState.error)"
:disabled="pluginsState.loading || Boolean(pluginsState.error) || cannot('WRITE', 'PLUGINS')"
class="btn btn-primary btn-sm rounded-md hover:shadow-lg"
@click="uploadFile('plugin')">
<ph-export-bold class="size-4" />
Expand All @@ -131,7 +131,7 @@ watchEffect(() => {
</div>
</div>
<ErrorBox v-if="pluginsState.loading || pluginsState.error" :load="pluginsState.loading" :error="pluginsState.error" />
<div v-else-if="filteredList.length > 0" class="flex flex-col gap-4">
<div v-else-if="filteredList.length > 0 || can('LIST', 'PLUGINS')" class="flex flex-col gap-4">
<Pagination v-slot="{ list }" :list="filteredList" :pageSize="selectedPageSize">
<div v-for="item in list" :key="item.url ?? item.id" class="flex gap-2 rounded-xl bg-base-100 p-2 shadow md:gap-4 md:p-4">
<UseImage :src="item.thumb" class="size-20 self-center rounded-lg object-contain">
Expand Down Expand Up @@ -160,17 +160,23 @@ watchEffect(() => {
<button
v-if="item.id !== 'core_plugin' && item.upgrade && item.plugin_url"
class="btn btn-primary btn-xs rounded-md uppercase"
:disabled="cannot('WRITE', 'PLUGINS')"
@click="installRegistryPlugin(item.plugin_url)">
<ph-export-bold class="size-4" />
Upgrade
</button>
<button v-if="item.url" class="btn btn-primary btn-xs rounded-md uppercase" @click="installRegistryPlugin(item.url)">
<button
v-if="item.url"
class="btn btn-primary btn-xs rounded-md uppercase"
:disabled="cannot('WRITE', 'PLUGINS')"
@click="installRegistryPlugin(item.url)">
<heroicons-cloud-arrow-down-solid class="size-4" />
Install
</button>
<button
v-else-if="item.id !== 'core_plugin'"
class="btn btn-error btn-xs rounded-md uppercase text-base-100"
:disabled="cannot('DELETE', 'PLUGINS')"
@click="openRemoveModal(item)">
<heroicons-trash-solid class="size-3" />
Delete
Expand Down Expand Up @@ -198,12 +204,14 @@ watchEffect(() => {
<button
v-if="item.id && !isEmpty(getSchema(item.id)) && item.active"
class="btn btn-circle btn-ghost btn-sm"
:disabled="cannot('READ', 'PLUGINS')"
@click="openSettings(item)">
<heroicons-cog-6-tooth-20-solid class="size-5" />
</button>
<button
v-if="(item.hooks && item.hooks.length > 0) || (item.tools && item.tools.length > 0)"
class="btn btn-circle btn-ghost btn-sm"
:disabled="cannot('READ', 'PLUGINS')"
@click="openInfo(item)">
<heroicons-information-circle-solid class="size-5" />
</button>
Expand All @@ -212,6 +220,7 @@ watchEffect(() => {
v-model="item.active"
type="checkbox"
class="!toggle !toggle-primary"
:disabled="cannot('WRITE', 'PLUGINS')"
@click="
async () => {
// TODO: Fix this workaround used to prevent checkbox switching when an error occurs
Expand Down
3 changes: 2 additions & 1 deletion src/views/ProvidersView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useLLMConfig } from '@stores/useLLMConfig'
const storeLLM = useLLMConfig()
const { getProviderSchema, setProviderSettings, getProviderSettings, refreshSettings } = storeLLM
const { currentState: llmState, getAvailableProviders } = storeToRefs(storeLLM)
const { cannot } = usePerms()
const selectedProvider = ref(llmState.value.selected)
const currentSchema = ref<JSONSettings>()
const currentSettings = ref<JSONSettings>({})
Expand Down Expand Up @@ -48,6 +48,7 @@ watchDeep(
<SelectBox
v-model="selectedProvider"
:list="getAvailableProviders.map(p => ({ label: p.humanReadableName ?? p.title, value: p.title }))"
:disabled="cannot('LIST', 'LLM')"
@update="(e: any) => updateProperties(e.value)" />
<div class="flex grow flex-col gap-4">
<div class="flex items-center gap-1 font-medium">
Expand Down
19 changes: 16 additions & 3 deletions src/views/SettingsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,36 @@ const canSave = computed(() => {

<template>
<div class="grid w-full auto-rows-min gap-8 self-center md:w-3/4 md:grid-cols-2">
<div class="col-span-2 flex flex-col items-center justify-center gap-2 rounded-md p-4">
<div v-if="can('READ', 'STATUS')" class="col-span-2 flex flex-col items-center justify-center gap-2 rounded-md p-4">
<p class="text-lg font-bold">
Cheshire Cat AI - Version
<span class="text-primary">
{{ cat ? cat.version : 'unknown' }}
</span>
</p>
<span v-if="cat">{{ cat.status }}</span>
</div>
<div class="col-span-2 flex flex-col items-center justify-between gap-8 rounded-lg bg-base-100 p-4 shadow-md md:col-span-1">
<p class="text-xl font-bold">Large Language Model</p>
<p class="text-center">Choose and configure your favourite LLM from a list of supported providers</p>
<RouterLink :to="{ name: 'providers' }" class="btn btn-primary btn-sm" @click="openSidePanel('llm')"> Configure </RouterLink>
<RouterLink
:to="{ name: 'providers' }"
class="btn btn-primary btn-sm"
:class="{ 'btn-disabled': cannot('WRITE', 'LLM') }"
@click="openSidePanel('llm')">
Configure
</RouterLink>
</div>
<div class="col-span-2 flex flex-col items-center justify-between gap-8 rounded-lg bg-base-100 p-4 shadow-md md:col-span-1">
<p class="text-xl font-bold">Embedder</p>
<p class="text-center">Choose a language embedder to help the Cat remember conversations and documents</p>
<RouterLink :to="{ name: 'embedders' }" class="btn btn-primary btn-sm" @click="openSidePanel('embedder')"> Configure </RouterLink>
<RouterLink
:to="{ name: 'embedders' }"
class="btn btn-primary btn-sm"
:class="{ 'btn-disabled': cannot('WRITE', 'EMBEDDER') }"
@click="openSidePanel('embedder')">
Configure
</RouterLink>
</div>
<div v-if="can('LIST', 'USERS')" class="col-span-2 w-full overflow-x-auto rounded-md">
<div class="flex items-center justify-between gap-4">
Expand Down

0 comments on commit 60f6d22

Please sign in to comment.