diff --git a/surfsense_web/components/settings/image-model-manager.tsx b/surfsense_web/components/settings/image-model-manager.tsx index 445d87a2a..cba2295c6 100644 --- a/surfsense_web/components/settings/image-model-manager.tsx +++ b/surfsense_web/components/settings/image-model-manager.tsx @@ -19,7 +19,7 @@ import { AnimatePresence, motion } from "motion/react"; import Image from "next/image"; import { useCallback, useEffect, useMemo, useState } from "react"; import { toast } from "sonner"; -import { membersAtom } from "@/atoms/members/members-query.atoms"; +import { membersAtom, myAccessAtom } from "@/atoms/members/members-query.atoms"; import { createImageGenConfigMutationAtom, deleteImageGenConfigMutationAtom, @@ -148,6 +148,22 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) { return map; }, [members]); + // Permissions + const { data: access } = useAtomValue(myAccessAtom); + const canCreate = useMemo(() => { + if (!access) return false; + if (access.is_owner) return true; + return access.permissions?.includes("image_generations:create") ?? false; + }, [access]); + const canDelete = useMemo(() => { + if (!access) return false; + if (access.is_owner) return true; + return access.permissions?.includes("image_generations:delete") ?? false; + }, [access]); + // Backend uses image_generations:create for update as well + const canUpdate = canCreate; + const isReadOnly = !canCreate && !canDelete; + // Local state const [isDialogOpen, setIsDialogOpen] = useState(false); const [editingConfig, setEditingConfig] = useState(null); @@ -344,6 +360,34 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) { ))} + {/* Read-only / Limited permissions notice */} + {access && !isLoading && isReadOnly && ( + + + + + You have read-only access to image generation + configurations. Contact a space owner to request additional permissions. + + + + )} + {access && !isLoading && !isReadOnly && (!canCreate || !canDelete) && ( + + + + + You can{" "} + {[canCreate && "create and edit", canDelete && "delete"] + .filter(Boolean) + .join(" and ")}{" "} + image model configurations + {!canDelete && ", but cannot delete them"}. + + + + )} + {/* Global info */} {globalConfigs.filter((g) => !("is_auto_mode" in g && g.is_auto_mode)).length > 0 && ( @@ -530,12 +574,14 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {

Your Image Models

- + {canCreate && ( + + )}
{(userConfigs?.length ?? 0) === 0 ? ( @@ -546,12 +592,16 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {

No Image Models Yet

- Add your own image generation model (DALL-E 3, GPT Image 1, etc.) + {canCreate + ? "Add your own image generation model (DALL-E 3, GPT Image 1, etc.)" + : "No image models have been added to this space yet. Contact a space owner to add one."}

- + {canCreate && ( + + )} ) : ( @@ -586,38 +636,44 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) {

)} + {(canUpdate || canDelete) && (
- - - - - - Edit - - - - - - - - Delete - - + {canUpdate && ( + + + + + + Edit + + + )} + {canDelete && ( + + + + + + Delete + + + )}
+ )} {/* Provider + Model */} diff --git a/surfsense_web/components/settings/model-config-manager.tsx b/surfsense_web/components/settings/model-config-manager.tsx index 238286a96..1666fae20 100644 --- a/surfsense_web/components/settings/model-config-manager.tsx +++ b/surfsense_web/components/settings/model-config-manager.tsx @@ -15,7 +15,7 @@ import { import { AnimatePresence, motion } from "motion/react"; import Image from "next/image"; import { useCallback, useMemo, useState } from "react"; -import { membersAtom } from "@/atoms/members/members-query.atoms"; +import { membersAtom, myAccessAtom } from "@/atoms/members/members-query.atoms"; import { createNewLLMConfigMutationAtom, deleteNewLLMConfigMutationAtom, @@ -120,6 +120,25 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) { return map; }, [members]); + // Permissions + const { data: access } = useAtomValue(myAccessAtom); + const canCreate = useMemo(() => { + if (!access) return false; + if (access.is_owner) return true; + return access.permissions?.includes("llm_configs:create") ?? false; + }, [access]); + const canUpdate = useMemo(() => { + if (!access) return false; + if (access.is_owner) return true; + return access.permissions?.includes("llm_configs:update") ?? false; + }, [access]); + const canDelete = useMemo(() => { + if (!access) return false; + if (access.is_owner) return true; + return access.permissions?.includes("llm_configs:delete") ?? false; + }, [access]); + const isReadOnly = !canCreate && !canUpdate && !canDelete; + // Local state const [isDialogOpen, setIsDialogOpen] = useState(false); const [editingConfig, setEditingConfig] = useState(null); @@ -187,13 +206,15 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) { Refresh - + {canCreate && ( + + )} {/* Fetch Error Alert */} @@ -215,6 +236,34 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) { )} + {/* Read-only / Limited permissions notice */} + {access && !isLoading && isReadOnly && ( + + + + + You have read-only access to LLM configurations. + Contact a space owner to request additional permissions. + + + + )} + {access && !isLoading && !isReadOnly && (!canCreate || !canUpdate || !canDelete) && ( + + + + + You can{" "} + {[canCreate && "create", canUpdate && "edit", canDelete && "delete"] + .filter(Boolean) + .join(" and ")}{" "} + configurations + {!canDelete && ", but cannot delete them"}. + + + + )} + {/* Global Configs Info */} {globalConfigs.length > 0 && ( @@ -279,17 +328,21 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {

No Configurations Yet

- Create your first AI configuration to customize how your agent responds + {canCreate + ? "Create your first AI configuration to customize how your agent responds" + : "No AI configurations have been added to this space yet. Contact a space owner to add one."}

- + {canCreate && ( + + )}
@@ -325,38 +378,44 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {

)} + {(canUpdate || canDelete) && (
- - - - - - Edit - - - - - - - - Delete - - + {canUpdate && ( + + + + + + Edit + + + )} + {canDelete && ( + + + + + + Delete + + + )}
+ )} {/* Provider + Model */}