diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index aa45a43..9e62c13 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -73,10 +73,8 @@ services: # ports: # - "8080:8080" depends_on: - postgres: - condition: service_started - rabbitmq: - condition: service_healthy + - postgres + - rabbitmq networks: - nyanimedb-network diff --git a/modules/frontend/nginx-default.conf b/modules/frontend/nginx-default.conf index 875c570..6075999 100644 --- a/modules/frontend/nginx-default.conf +++ b/modules/frontend/nginx-default.conf @@ -30,9 +30,6 @@ server { } location /media/ { - limit_except GET { - deny all; - } rewrite ^/media/(.*)$ /$1 break; proxy_pass http://nyanimedb-images:8000/; proxy_http_version 1.1; diff --git a/modules/frontend/src/App.tsx b/modules/frontend/src/App.tsx index f8ebdf5..de7101c 100644 --- a/modules/frontend/src/App.tsx +++ b/modules/frontend/src/App.tsx @@ -5,7 +5,6 @@ import UserPage from "./pages/UserPage/UserPage"; import TitlesPage from "./pages/TitlesPage/TitlesPage"; import TitlePage from "./pages/TitlePage/TitlePage"; import { LoginPage } from "./pages/LoginPage/LoginPage"; -import { SettingsPage } from "./pages/SettingsPage/SettingsPage"; import { Header } from "./components/Header/Header"; // import { OpenAPI } from "./api"; @@ -47,12 +46,6 @@ const App: React.FC = () => { element={userId ? : } /> - {/*settings*/} - : } - /> - {/* titles */} } /> } /> diff --git a/modules/frontend/src/api/AuthClient/AuthClient.ts b/modules/frontend/src/api/AuthClient/AuthClient.ts deleted file mode 100644 index c7c20dd..0000000 --- a/modules/frontend/src/api/AuthClient/AuthClient.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { createClient, createConfig } from "../client"; -import type { ClientOptions as ClientOptions2 } from '../types.gen'; -import type { Client, RequestOptions, RequestResult } from "../client"; -import { refreshTokens } from "../../auth"; -import type { ResponseStyle } from "../client"; - -let refreshPromise: Promise | null = null; - -async function getRefreshed(): Promise { - if (!refreshPromise) { - refreshPromise = (async () => { - try { - const res = await refreshTokens({ throwOnError: true }); - return !!res.data; - } catch { - return false; - } - })(); - } - return refreshPromise; -} - -const baseClient = createClient(createConfig({ baseUrl: '/api/v1' })); - -export const authClient: Client = { - ...baseClient, - - request: (async < - TData = unknown, - TError = unknown, - ThrowOnError extends boolean = false, - TResponseStyle extends ResponseStyle = 'fields', - >( - options: Omit, 'method'> & - Pick>, 'method'> - ): Promise> => { - - let result = await baseClient.request(options); - - // 1. Cast to a Record to allow the 'in' operator check on a generic - // We use 'unknown' instead of 'any' to maintain safety. - const resultObj = result as Record; - - // 2. Check if the object is valid and contains the error key - if (result && typeof result === 'object' && 'error' in resultObj) { - - // 3. Narrow the error property specifically - const error = resultObj.error as { response?: { status?: number } } | null | undefined; - - if (error?.response?.status === 401) { - const refreshed = await getRefreshed(); - - if (refreshed) { - result = await baseClient.request(options); - } else { - localStorage.clear(); - window.location.href = "/login"; - } - } - } - - return result; - }) as Client['request'], -}; \ No newline at end of file diff --git a/modules/frontend/src/components/Header/Header.tsx b/modules/frontend/src/components/Header/Header.tsx index 9c3b39a..36cbd5a 100644 --- a/modules/frontend/src/components/Header/Header.tsx +++ b/modules/frontend/src/components/Header/Header.tsx @@ -32,8 +32,6 @@ export const Header: React.FC = () => { localStorage.removeItem("user_id"); localStorage.removeItem("user_name"); setUsername(null); - setDropdownOpen(false); - setMenuOpen(false); navigate("/login"); } catch (err) { console.error(err); @@ -76,38 +74,14 @@ export const Header: React.FC = () => {
{dropdownOpen && ( -
- setDropdownOpen(false)} - > - Profile - - - {/* КНОПКА SETTINGS */} - setDropdownOpen(false)} - > - Settings - - -
- - +
+ setDropdownOpen(false)}>Profile +
)}
@@ -133,16 +107,11 @@ export const Header: React.FC = () => { setMenuOpen(false)}>Titles setMenuOpen(false)}>Users setMenuOpen(false)}>About - {username ? ( -
- setMenuOpen(false)}>Profile - - {/* SETTINGS (Mobile) */} - setMenuOpen(false)}>Settings - - -
+ <> + setMenuOpen(false)}>Profile + + ) : ( setMenuOpen(false)}>Login )} diff --git a/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx b/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx index f525baf..16c7e9e 100644 --- a/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx +++ b/modules/frontend/src/pages/SettingsPage/SettingsPage.tsx @@ -1,235 +1,154 @@ -import React, { useEffect, useState, useRef } from "react"; -import { updateUser, getUsersId } from "../../api"; -import { useNavigate } from "react-router-dom"; -import { useCookies } from 'react-cookie'; +// import React, { useEffect, useState } from "react"; +// import { updateUser, getUsersId } from "../../api"; +// import { useNavigate } from "react-router-dom"; -export const SettingsPage: React.FC = () => { - const [cookies] = useCookies(['xsrf_token']); - const xsrfToken = cookies['xsrf_token'] || null; +// export const SettingsPage: React.FC = () => { +// const navigate = useNavigate(); - const navigate = useNavigate(); - const fileInputRef = useRef(null); +// const userId = Number(localStorage.getItem("user_id")); +// const initialNickname = localStorage.getItem("user_name") || ""; +// const [mail, setMail] = useState(""); +// const [nickname, setNickname] = useState(initialNickname); +// const [dispName, setDispName] = useState(""); +// const [userDesc, setUserDesc] = useState(""); +// const [avatarId, setAvatarId] = useState(null); - const userId = Number(localStorage.getItem("user_id")); - - // Состояния для полей формы - const [mail, setMail] = useState(""); - const [nickname, setNickname] = useState(""); - const [dispName, setDispName] = useState(""); - const [userDesc, setUserDesc] = useState(""); - const [avatarId, setAvatarId] = useState(null); - const [avatarUrl, setAvatarUrl] = useState(null); +// const [loading, setLoading] = useState(false); +// const [success, setSuccess] = useState(null); +// const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); - const [uploading, setUploading] = useState(false); - const [success, setSuccess] = useState(null); - const [error, setError] = useState(null); +// useEffect(() => { +// const fetch = async () => { +// const res = await getUsersId({ +// path: { user_id: String(userId) }, +// }); - // Загружаем текущие данные пользователя при входе - useEffect(() => { - const fetchUserData = async () => { - try { - const res = await getUsersId({ - path: { user_id: String(userId) }, - }); - if (res.data) { - setMail(res.data.mail || ""); - setNickname(res.data.nickname || ""); - setDispName(res.data.disp_name || ""); - setUserDesc(res.data.user_desc || ""); - setAvatarId(res.data.image?.id ?? null); - const path = res.data.image?.image_path; - setAvatarUrl(path ? (path.startsWith('http') ? path : `/media/${path}`) : null); - } - } catch (err) { - console.error("Failed to fetch user:", err); - } - }; - fetchUserData(); - }, [userId]); +// setProfile(res.data); +// }; - // Обработка загрузки файла -const handleFileChange = async (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; +// fetch(); +// }, [userId]); - setUploading(true); - setError(null); - setSuccess(null); +// const saveSettings = async (e: React.FormEvent) => { +// e.preventDefault(); +// setLoading(true); +// setSuccess(null); +// setError(null); - const formData = new FormData(); - formData.append("image", file); +// try { +// const res = await updateUser({ +// path: { user_id: userId }, +// body: { +// ...(mail ? { mail } : {}), +// ...(nickname ? { nickname } : {}), +// ...(dispName ? { disp_name: dispName } : {}), +// ...(userDesc ? { user_desc: userDesc } : {}), +// ...(avatarId !== undefined ? { avatar_id: avatarId } : {}), +// } +// }); - try { - // 1. Загружаем файл на сервер (POST) - const uploadRes = await fetch("/api/v1/media/upload", { - method: "POST", - body: formData, - headers: { - "X-XSRF-TOKEN": xsrfToken || "", - }, - }); +// // Обновляем локальное отображение username +// if (nickname) { +// localStorage.setItem("user_name", nickname); +// window.dispatchEvent(new Event("storage")); // чтобы Header обновился +// } - if (!uploadRes.ok) throw new Error("Failed to upload image to storage"); +// setSuccess("Settings updated!"); +// setTimeout(() => navigate("/profile"), 800); - const uploadData = await uploadRes.json(); - const newAvatarId = uploadData.id; +// } catch (err: any) { +// console.error(err); +// setError(err?.message || "Failed to update settings"); +// } finally { +// setLoading(false); +// } +// }; - if (newAvatarId) { - // 2. СРАЗУ отправляем PATCH запрос для обновления профиля пользователя - await updateUser({ - path: { user_id: userId }, - body: { - avatar_id: newAvatarId, // Привязываем новый ID к юзеру - }, - headers: { "X-XSRF-TOKEN": xsrfToken }, - }); +// return ( +//
+//

User Settings

- // 3. Обновляем локальный стейт для отображения - setAvatarId(newAvatarId); - const path = uploadData.image_path; - setAvatarUrl(path ? (path.startsWith("/") ? path : `/media/${path}`) : null); - - setSuccess("Avatar updated successfully!"); - } - } catch (err: any) { - console.error("Upload & Patch error:", err); - setError(err.message || "Failed to update avatar"); - } finally { - setUploading(false); - } -}; +// {success &&
{success}
} +// {error &&
{error}
} - const saveSettings = async (e: React.FormEvent) => { - e.preventDefault(); - setLoading(true); - setSuccess(null); - setError(null); +//
+// {/* Email */} +//
+// +// setMail(e.target.value)} +// placeholder="example@mail.com" +// className="w-full px-4 py-2 border rounded-lg" +// /> +//
- try { - await updateUser({ - path: { user_id: userId }, - body: { - mail: mail || undefined, - nickname: nickname || undefined, - disp_name: dispName || undefined, - user_desc: userDesc || undefined, - avatar_id: avatarId, // Может быть числом или null для удаления - }, - headers: { "X-XSRF-TOKEN": xsrfToken }, - }); +// {/* Nickname */} +//
+// +// setNickname(e.target.value)} +// className="w-full px-4 py-2 border rounded-lg" +// /> +//
- localStorage.setItem("user_name", nickname); - window.dispatchEvent(new Event("storage")); +// {/* Display name */} +//
+// +// setDispName(e.target.value)} +// placeholder="Shown name" +// className="w-full px-4 py-2 border rounded-lg" +// /> +//
- setSuccess("Settings updated successfully!"); - setTimeout(() => navigate("/profile"), 1000); - } catch (err: any) { - setError(err?.message || "Failed to update settings"); - } finally { - setLoading(false); - } - }; +// {/* Bio */} +//
+// +//