From 42a585d880751e2dcf00e654678f96c2d40f5d6c Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 11:47:40 +0000 Subject: [PATCH 1/9] feat: add user types with zod schemas --- surfsense_web/contracts/types/user.types.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 surfsense_web/contracts/types/user.types.ts diff --git a/surfsense_web/contracts/types/user.types.ts b/surfsense_web/contracts/types/user.types.ts new file mode 100644 index 000000000..f5df17694 --- /dev/null +++ b/surfsense_web/contracts/types/user.types.ts @@ -0,0 +1,19 @@ +import { z } from "zod"; + +export const user = z.object({ + id: z.string().uuid(), + email: z.string().email(), + is_active: z.boolean(), + is_superuser: z.boolean(), + is_verified: z.boolean(), + pages_limit: z.number(), + pages_used: z.number(), +}); + +/** + * Get current user + */ +export const getMeResponse = user; + +export type User = z.infer; +export type GetMeResponse = z.infer; From 8c5ecfd4e44ac170067dd5014c7a0e1e63d67dff Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 11:58:10 +0000 Subject: [PATCH 2/9] feat: add user API service --- surfsense_web/lib/apis/user-api.service.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 surfsense_web/lib/apis/user-api.service.ts diff --git a/surfsense_web/lib/apis/user-api.service.ts b/surfsense_web/lib/apis/user-api.service.ts new file mode 100644 index 000000000..ea46ac116 --- /dev/null +++ b/surfsense_web/lib/apis/user-api.service.ts @@ -0,0 +1,13 @@ +import { getMeResponse } from "@/contracts/types/user.types"; +import { baseApiService } from "./base-api.service"; + +class UserApiService { + /** + * Get current authenticated user + */ + getMe = async () => { + return baseApiService.get(`/users/me`, getMeResponse); + }; +} + +export const userApiService = new UserApiService(); From d5997a6d0cad6fadd63878c60a8d29dc6881b443 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 12:04:39 +0000 Subject: [PATCH 3/9] feat: add user cache keys --- surfsense_web/lib/query-client/cache-keys.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/surfsense_web/lib/query-client/cache-keys.ts b/surfsense_web/lib/query-client/cache-keys.ts index 797c40b65..ae8bb0f08 100644 --- a/surfsense_web/lib/query-client/cache-keys.ts +++ b/surfsense_web/lib/query-client/cache-keys.ts @@ -41,4 +41,7 @@ export const cacheKeys = { detail: (searchSpaceId: string) => ["search-spaces", searchSpaceId] as const, communityPrompts: ["search-spaces", "community-prompts"] as const, } -}; + user: { + current: () => ["user", "me"] as const, + }, +}; \ No newline at end of file From cee27f1263b57a3d4c934b2c5a4c6d1cb4584975 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 12:07:47 +0000 Subject: [PATCH 4/9] feat: add current user query atom --- surfsense_web/atoms/user/user-query.atoms.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 surfsense_web/atoms/user/user-query.atoms.ts diff --git a/surfsense_web/atoms/user/user-query.atoms.ts b/surfsense_web/atoms/user/user-query.atoms.ts new file mode 100644 index 000000000..ea3e7ec49 --- /dev/null +++ b/surfsense_web/atoms/user/user-query.atoms.ts @@ -0,0 +1,13 @@ +import { atomWithQuery } from "jotai-tanstack-query"; +import { userApiService } from "@/lib/apis/user-api.service"; +import { cacheKeys } from "@/lib/query-client/cache-keys"; + +export const currentUserAtom = atomWithQuery(() => { + return { + queryKey: cacheKeys.user.current(), + staleTime: 5 * 60 * 1000, // 5 minutes + queryFn: async () => { + return userApiService.getMe(); + }, + }; +}); From 6b266ff12898ac2adcc1a60f0d206093abe9e746 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 12:19:33 +0000 Subject: [PATCH 5/9] refactor: migrate dashboard page from useUser to currentUserAtom --- surfsense_web/app/dashboard/page.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index b1525a9db..d3d88a47a 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -35,10 +35,10 @@ import { } from "@/components/ui/card"; import { Spotlight } from "@/components/ui/spotlight"; import { Tilt } from "@/components/ui/tilt"; -import { useUser } from "@/hooks"; import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; import { authenticatedFetch } from "@/lib/auth-utils"; +import { currentUserAtom } from "@/atoms/user/user-query.atoms"; /** * Formats a date string into a readable format @@ -159,8 +159,7 @@ const DashboardPage = () => { const { data: searchSpaces = [], isLoading: loading, error, refetch: refreshSearchSpaces } = useAtomValue(searchSpacesAtom); const { mutateAsync: deleteSearchSpace } = useAtomValue(deleteSearchSpaceMutationAtom); - // Fetch user details - const { user, loading: isLoadingUser, error: userError } = useUser(); + const { data: user, isPending: isLoadingUser, error: userError } = useAtomValue(currentUserAtom); // Create user object for UserDropdown const customUser = { From 2e0f59522aff60e42e37b62e4453c62edf471c25 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 12:25:09 +0000 Subject: [PATCH 6/9] refactor: migrate AppSidebarProvider from useUser to currentUserAtom --- surfsense_web/components/sidebar/AppSidebarProvider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surfsense_web/components/sidebar/AppSidebarProvider.tsx b/surfsense_web/components/sidebar/AppSidebarProvider.tsx index fce8697f7..55bc8331c 100644 --- a/surfsense_web/components/sidebar/AppSidebarProvider.tsx +++ b/surfsense_web/components/sidebar/AppSidebarProvider.tsx @@ -17,10 +17,10 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { useUser } from "@/hooks"; import { useQuery } from "@tanstack/react-query"; import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; +import { currentUserAtom } from "@/atoms/user/user-query.atoms"; interface AppSidebarProviderProps { searchSpaceId: string; @@ -68,7 +68,7 @@ export function AppSidebarProvider({ enabled: !!searchSpaceId, }); - const { user } = useUser(); + const { data: user } = useAtomValue(currentUserAtom); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [chatToDelete, setChatToDelete] = useState<{ id: number; name: string } | null>(null); From 8691ba9d72cff145faed3236a82783f99e1acd6f Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 12:34:10 +0000 Subject: [PATCH 7/9] refactor: migrate app-sidebar from useUser to currentUserAtom --- surfsense_web/components/sidebar/app-sidebar.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/surfsense_web/components/sidebar/app-sidebar.tsx b/surfsense_web/components/sidebar/app-sidebar.tsx index cd42e6fe8..6f86d3808 100644 --- a/surfsense_web/components/sidebar/app-sidebar.tsx +++ b/surfsense_web/components/sidebar/app-sidebar.tsx @@ -38,7 +38,8 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { useUser } from "@/hooks/use-user"; +import { useAtomValue } from "jotai"; +import { currentUserAtom } from "@/atoms/user/user-query.atoms"; /** * Generates a consistent color based on a string (email) @@ -262,7 +263,7 @@ export const AppSidebar = memo(function AppSidebar({ }: AppSidebarProps) { const router = useRouter(); const { theme, setTheme } = useTheme(); - const { user, loading: isLoadingUser } = useUser(); + const { data: user, isPending: isLoadingUser } = useAtomValue(currentUserAtom); const [isClient, setIsClient] = useState(false); useEffect(() => { From 4bcb92c3ce4130904b11020b15068b281b5825d5 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 12:44:08 +0000 Subject: [PATCH 8/9] refactor: delete unused useUser hook after migration to currentUserAtom --- surfsense_web/hooks/index.ts | 1 - surfsense_web/hooks/use-user.ts | 53 --------------------------------- 2 files changed, 54 deletions(-) delete mode 100644 surfsense_web/hooks/use-user.ts diff --git a/surfsense_web/hooks/index.ts b/surfsense_web/hooks/index.ts index f7ef22534..d2a4ff6bf 100644 --- a/surfsense_web/hooks/index.ts +++ b/surfsense_web/hooks/index.ts @@ -1,4 +1,3 @@ export * from "./use-logs"; export * from "./use-rbac"; export * from "./use-search-source-connectors"; -export * from "./use-user"; diff --git a/surfsense_web/hooks/use-user.ts b/surfsense_web/hooks/use-user.ts deleted file mode 100644 index e81ac350b..000000000 --- a/surfsense_web/hooks/use-user.ts +++ /dev/null @@ -1,53 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { toast } from "sonner"; -import { authenticatedFetch } from "@/lib/auth-utils"; - -interface User { - id: string; - email: string; - is_active: boolean; - is_superuser: boolean; - is_verified: boolean; - pages_limit: number; - pages_used: number; -} - -export function useUser() { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchUser = async () => { - try { - // Only run on client-side - if (typeof window === "undefined") return; - - setLoading(true); - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/users/me`, - { method: "GET" } - ); - - if (!response.ok) { - throw new Error(`Failed to fetch user: ${response.status}`); - } - - const data = await response.json(); - setUser(data); - setError(null); - } catch (err: any) { - setError(err.message || "Failed to fetch user"); - console.error("Error fetching user:", err); - } finally { - setLoading(false); - } - }; - - fetchUser(); - }, []); - - return { user, loading, error }; -} From bc6f7e15f1b509edf961791a774fe75745dbedbd Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Tue, 16 Dec 2025 05:37:30 +0000 Subject: [PATCH 9/9] refact: rebase on upstream dev --- surfsense_web/lib/query-client/cache-keys.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surfsense_web/lib/query-client/cache-keys.ts b/surfsense_web/lib/query-client/cache-keys.ts index ae8bb0f08..eb2c4972a 100644 --- a/surfsense_web/lib/query-client/cache-keys.ts +++ b/surfsense_web/lib/query-client/cache-keys.ts @@ -40,7 +40,7 @@ export const cacheKeys = { ["search-spaces", ...(queries ? Object.values(queries) : [])] as const, detail: (searchSpaceId: string) => ["search-spaces", searchSpaceId] as const, communityPrompts: ["search-spaces", "community-prompts"] as const, - } + }, user: { current: () => ["user", "me"] as const, },