diff --git a/src/components/domains/profile/AutoTopUpToggle/AutoTopUpToggle.tsx b/src/components/domains/profile/AutoTopUpToggle/AutoTopUpToggle.tsx index d806e54..0d948e1 100644 --- a/src/components/domains/profile/AutoTopUpToggle/AutoTopUpToggle.tsx +++ b/src/components/domains/profile/AutoTopUpToggle/AutoTopUpToggle.tsx @@ -5,9 +5,9 @@ import { useTranslations } from "next-intl"; import { Skeleton } from "@/components/ui"; import { - getMyChatSettings, - updateMyChatSettings, -} from "@/entities/chats/chatSettings.api"; + fetchMyChatSettings, + updateChatSettings, +} from "@/entities/chats/actions"; import type { IChatSettings } from "@/entities/chats/types"; import styles from "./AutoTopUpToggle.module.scss"; @@ -22,9 +22,9 @@ export default function AutoTopUpToggle() { let mounted = true; (async () => { try { - const res = await getMyChatSettings(); - if (mounted) { - setSettings(res.settings); + const res = await fetchMyChatSettings(); + if (mounted && res.data) { + setSettings(res.data.settings); } } catch (e) { // silent failure @@ -45,8 +45,10 @@ export default function AutoTopUpToggle() { setSettings(next); // optimistic setIsUpdating(true); try { - const res = await updateMyChatSettings(next); - setSettings(res.settings); + const res = await updateChatSettings(next); + if (res.data) { + setSettings(res.data.settings); + } } catch (e) { // revert on error silently setSettings(settings); diff --git a/src/entities/balance/actions.ts b/src/entities/balance/actions.ts new file mode 100644 index 0000000..ebc0f0c --- /dev/null +++ b/src/entities/balance/actions.ts @@ -0,0 +1,7 @@ +"use server"; + +import { getUserBalance } from "./api"; + +export async function fetchUserBalance() { + return getUserBalance(); +} diff --git a/src/entities/balance/api.ts b/src/entities/balance/api.ts index 580eebd..7de9d14 100644 --- a/src/entities/balance/api.ts +++ b/src/entities/balance/api.ts @@ -1,39 +1,16 @@ -"use client"; - -import { getClientAccessToken } from "@/shared/auth/clientToken"; +import { http } from "@/shared/api/httpClient"; import { API_ROUTES } from "@/shared/constants/api-routes"; import { IUserBalanceResponse, UserBalanceSchema } from "./types"; /** - * Fetches the current user balance using client-side authentication + * Fetches the current user balance (server-side with httpOnly cookie access) * @returns Promise with user balance information */ export const getUserBalance = async (): Promise => { - const accessToken = getClientAccessToken(); - if (!accessToken) { - throw new Error("No access token available"); - } - - const apiUrl = process.env.NEXT_PUBLIC_API_URL; - if (!apiUrl) { - throw new Error("API URL not configured"); - } - - const url = new URL(API_ROUTES.getUserBalance(), apiUrl); - - const response = await fetch(url.toString(), { - method: "GET", - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": "application/json", - }, + return http.get(API_ROUTES.getUserBalance(), { + tags: ["balance"], + schema: UserBalanceSchema, + revalidate: 0, }); - - if (!response.ok) { - throw new Error(`Failed to fetch balance: ${response.status}`); - } - - const data = await response.json(); - return UserBalanceSchema.parse(data); }; diff --git a/src/entities/chats/actions.ts b/src/entities/chats/actions.ts index 78af166..8a22c0a 100644 --- a/src/entities/chats/actions.ts +++ b/src/entities/chats/actions.ts @@ -6,11 +6,15 @@ import { http } from "@/shared/api/httpClient"; import { API_ROUTES } from "@/shared/constants/api-routes"; import { ActionResponse } from "@/types"; +import { getMyChatSettings, updateMyChatSettings } from "./chatSettings.api"; import { CreateAllChatsResponseSchema, GetChatMessagesResponseSchema, + IChatSettings, ICreateAllChatsResponse, IGetChatMessagesResponse, + IGetMyChatSettingsResponse, + IUpdateMyChatSettingsResponse, } from "./types"; export async function createAllChats(): Promise< @@ -60,6 +64,38 @@ export async function fetchChatMessages( } } +export async function fetchMyChatSettings(): Promise< + ActionResponse +> { + try { + const response = await getMyChatSettings(); + return { data: response, error: null }; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Failed to fetch chat settings:", error); + const errorMessage = + error instanceof Error ? error.message : "Something went wrong."; + return { data: null, error: errorMessage }; + } +} + +export async function updateChatSettings( + settings: IChatSettings +): Promise> { + try { + const response = await updateMyChatSettings(settings); + revalidateTag("profile"); + revalidateTag("chat-settings"); + return { data: response, error: null }; + } catch (error) { + // eslint-disable-next-line no-console + console.error("Failed to update chat settings:", error); + const errorMessage = + error instanceof Error ? error.message : "Something went wrong."; + return { data: null, error: errorMessage }; + } +} + export async function revalidateChatsPage() { revalidateTag("chats-list"); } diff --git a/src/entities/chats/chatSettings.api.ts b/src/entities/chats/chatSettings.api.ts index 9da8486..938e7ab 100644 --- a/src/entities/chats/chatSettings.api.ts +++ b/src/entities/chats/chatSettings.api.ts @@ -1,5 +1,3 @@ -"use client"; - import { http } from "@/shared/api/httpClient"; import { API_ROUTES } from "@/shared/constants/api-routes"; @@ -12,7 +10,7 @@ import { } from "./types"; /** - * Fetch current user's chat settings (client-side) + * Fetch current user's chat settings (server-side with httpOnly cookie access) */ export const getMyChatSettings = async (): Promise => { diff --git a/src/hooks/balance/useUserBalance.ts b/src/hooks/balance/useUserBalance.ts index 41c54fe..c78dcf5 100644 --- a/src/hooks/balance/useUserBalance.ts +++ b/src/hooks/balance/useUserBalance.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from "react"; -import { getUserBalance } from "@/entities/balance/api"; +import { fetchUserBalance } from "@/entities/balance/actions"; export const useUserBalance = () => { const [balance, setBalance] = useState(null); @@ -13,17 +13,12 @@ export const useUserBalance = () => { setIsLoading(true); setError(null); try { - const response = await getUserBalance(); + const response = await fetchUserBalance(); setBalance(response.balance); } catch (err) { setError( err instanceof Error ? err : new Error("Failed to fetch balance") ); - // Используем devLogger или другой механизм логирования в продакшене - if (process.env.NODE_ENV !== "production") { - // eslint-disable-next-line no-console - console.error("Error fetching user balance:", err); - } } finally { setIsLoading(false); } diff --git a/src/shared/api/httpClient.ts b/src/shared/api/httpClient.ts index 1844085..9aac9b7 100644 --- a/src/shared/api/httpClient.ts +++ b/src/shared/api/httpClient.ts @@ -91,7 +91,13 @@ class HttpClient { // ignore } } - if (accessToken) headers.set("Authorization", `Bearer ${accessToken}`); + if (accessToken) { + headers.set("Authorization", `Bearer ${accessToken}`); + + console.log("🔑 Token being sent:", accessToken.substring(0, 20) + "..."); + } else { + console.log("❌ No access token found!"); + } headers.set("Content-Type", "application/json"); const res = await fetch(fullUrl, { diff --git a/src/shared/auth/token.ts b/src/shared/auth/token.ts index 10b28e4..c393f28 100644 --- a/src/shared/auth/token.ts +++ b/src/shared/auth/token.ts @@ -8,10 +8,25 @@ export async function getServerAccessToken() { export function getClientAccessToken(): string | undefined { if (typeof window === "undefined") return undefined; + // eslint-disable-next-line no-console + console.log("🍪 Debug cookies:", document.cookie); + const cookies = document.cookie.split(";"); + // eslint-disable-next-line no-console + console.log("🍪 All cookies:", cookies); + const accessTokenCookie = cookies.find(cookie => cookie.trim().startsWith("accessToken=") ); - return accessTokenCookie?.split("=")[1]; + // eslint-disable-next-line no-console + console.log("🍪 Found accessToken cookie:", accessTokenCookie); + + if (!accessTokenCookie) return undefined; + + // Use substring instead of split to handle tokens with = signs + const token = accessTokenCookie.trim().substring("accessToken=".length); + // eslint-disable-next-line no-console + console.log("🍪 Extracted token:", token.substring(0, 20) + "..."); + return token; }