From f6729210db9650daff8b909b788d00bc09b10dfd Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 21 May 2026 21:50:07 +0500 Subject: [PATCH] feat: public admin avatar endpoint, real admin avatar in user chat --- client/src/entities/user/api/user-api.ts | 11 +++++++++++ .../src/features/order-chat/ui/OrderChat.tsx | 17 ++++++++++++++++- server/prisma/prisma/dev.db | Bin 364544 -> 364544 bytes server/src/routes/api/admin-profile.js | 15 +++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/client/src/entities/user/api/user-api.ts b/client/src/entities/user/api/user-api.ts index 20fdf20..d65bbce 100644 --- a/client/src/entities/user/api/user-api.ts +++ b/client/src/entities/user/api/user-api.ts @@ -30,6 +30,17 @@ export async function updateAdminUser( return data } +export type AdminAvatarResponse = { + avatar: string | null + avatarType: string | null + avatarStyle: string | null +} + +export async function fetchAdminAvatar(): Promise { + const { data } = await apiClient.get('admin/avatar') + return data +} + export async function deleteAdminUser(id: string): Promise { await apiClient.delete(`admin/users/${id}`) } diff --git a/client/src/features/order-chat/ui/OrderChat.tsx b/client/src/features/order-chat/ui/OrderChat.tsx index 978c28f..27d1c8a 100644 --- a/client/src/features/order-chat/ui/OrderChat.tsx +++ b/client/src/features/order-chat/ui/OrderChat.tsx @@ -3,7 +3,9 @@ import Box from '@mui/material/Box' import Button from '@mui/material/Button' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' +import { useQuery } from '@tanstack/react-query' import { useUnit } from 'effector-react' +import { fetchAdminAvatar } from '@/entities/user/api/user-api' import { $user } from '@/shared/model/auth' import { ChatMessageBubble } from '@/shared/ui/ChatMessageBubble' import { OrderMessageBody } from '@/shared/ui/OrderMessageBody' @@ -29,6 +31,12 @@ export function OrderChat({ messages, isPending, onSend }: Props) { const canSend = text.replace(/<[^>]*>/g, ' ').trim().length > 0 const currentUser = useUnit($user) + const adminAvatarQuery = useQuery({ + queryKey: ['admin', 'avatar'], + queryFn: fetchAdminAvatar, + staleTime: 5 * 60 * 1000, + }) + const handleSend = () => { if (!canSend || isPending) return onSend(text.trim()) @@ -43,8 +51,15 @@ export function OrderChat({ messages, isPending, onSend }: Props) { {messages.map((m) => { const isAdminMsg = m.authorType === 'admin' + const adminAv = adminAvatarQuery.data const avatarNode = isAdminMsg ? ( - + ) : currentUser ? ( 6)=Q^l+1LdFI`LpiD?vQBO4PUw8KnW+R~ZQ@+gaT z<;ny_lO`rg7e*H@)Kpr<#7BZlB71j|=mHY{fC=6^Q?xqhP3Gk0%(>rreBUXS+luA3 zJMD&+=-rNCWM9raUTYyN1OdOmLwH!R`X9i_ZZJY-#f7XR9nNKAj$RNT=hI>;98WqJ zkH^RMgOK)L4zFk)?|Cp`j*3~wZ;z89%glGR-U)zhTDk}FnPf+hLLaKA>C$?veX4}rZ$yO;KPVAtXfHm#_d+Q9@kYtd%- z48~#my-?3U%{>ENG!LwTR^Hcbv)KZK(MS+#bDWe?P<}d-OU`C;3D3e(Rwz9ez_r%& zP#@|5S%PSOSc=BLDur%cwQn)NH5JAX)(D}V1}p^+iosZ(%*v81Nv>7H2|05?+)3C?CUBZSZ1cxTj$lYUQFcC>QKHF+ z3fYUqtTw!T9H&$KD{2R_Cl>WbH#+>Pg`zAq%5V4xFZ#$+ybFD1^BFTbMq@o2mqArY zcm?A5S&5UG2;*cqms-(n%~s3gKE&&ykFEMCiqRa5b4Ks`NALS@ zZerL^w>l4z_360>V{HujNTFZoBbu6MPd-EgTR6{kxraA}4;|dwVabS#r6EG3GB8Ee=~XBU_7c z^xeVK_24PLn5MHN5rW$A#4=RH$|W8N%|Y=cGMkkkH?$tPaPge9X*>q zcg1v(O+%ff(JX~#(d}vF$15Z}k`(zNXyFwoS%41qtltszbWw6=q%!3|ynOU8%^967T-q=j9 zY5XXF@QM>=FefOITn+Gt1EJ8tvLwiXP+uT~4M8=6hFF$zWS@NJG}R0mQcZM~5&Ylw zh$!U?rh5>^_Er^au*cp&_5e1;iWwBtOhGHV$BLBA%Ay;aUChcw_r`G3kMHkG1$dJF zh!beP{s7n7U&lP`?!lq&3~BbTEapX3EDNTXl~lHRc%Odz`Bc7Isy z<#p9*yj;cktY+Z|R}n!MNYKum>o|AOB!|&rHLOH))eF_I8fQ8|!X;w4ORJ9pd}Wz+GS^zt?rLKQ=8C%k|VEPQcL;P|zo&WbU1n(CAx(`Ah!RkGQ-V { + const adminEmail = normalizeEmail(process.env.ADMIN_EMAIL) + if (!adminEmail || !adminEmail.includes('@')) return reply.code(404).send({ error: 'Администратор не найден' }) + + const user = await prisma.user.findUnique({ where: { email: adminEmail } }) + if (!user) return reply.code(404).send({ error: 'Администратор не найден' }) + + return { + avatar: user.avatar, + avatarType: user.avatarType, + avatarStyle: user.avatarStyle, + } + }) + fastify.patch('/api/admin/profile', { preHandler: [fastify.verifyAdmin] }, async (request, reply) => { const userId = request.user.sub const nameRaw = request.body?.displayName