diff --git a/API/app/contollers/profiles_router.py b/API/app/contollers/profiles_router.py index 58d980a..0c4f4b4 100644 --- a/API/app/contollers/profiles_router.py +++ b/API/app/contollers/profiles_router.py @@ -1,6 +1,6 @@ from typing import Optional -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.database.session import get_db @@ -55,3 +55,22 @@ async def delete_profile( ): profiles_service = ProfilesService(db) return await profiles_service.delete(profile_id, user) + + +@router.get( + '/user/{user_id}/', + response_model=Optional[ProfileEntity], + summary='Get profile by user ID', + description='Retrieve profile data by user ID', +) +async def get_profile_by_user_id( + user_id: int, + db: AsyncSession = Depends(get_db), + user=Depends(get_current_user), +): + profiles_service = ProfilesService(db) + profile = await profiles_service.get_by_user_id(user_id) + + return profile + + diff --git a/API/app/infrastructure/profiles_service.py b/API/app/infrastructure/profiles_service.py index 8a1e695..4b4336b 100644 --- a/API/app/infrastructure/profiles_service.py +++ b/API/app/infrastructure/profiles_service.py @@ -102,6 +102,17 @@ class ProfilesService: return self.model_to_entity(result) + async def get_by_user_id(self, user_id: int) -> Optional[ProfileEntity]: + user = await self.users_repository.get_by_id(user_id) + if user is None: + raise HTTPException(status_code=404, detail='User not found') + + profile_model = await self.profiles_repository.get_by_id(user.profile_id) + if not profile_model: + raise HTTPException(status_code=404, detail='Profile not found') + + return self.model_to_entity(profile_model) + @staticmethod def model_to_entity(profile_model: Profile) -> ProfileEntity: return ProfileEntity( diff --git a/API/app/infrastructure/teams_service.py b/API/app/infrastructure/teams_service.py index dd703cc..317a927 100644 --- a/API/app/infrastructure/teams_service.py +++ b/API/app/infrastructure/teams_service.py @@ -33,8 +33,8 @@ class TeamsService: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Team not found") team_model.title = team.title - team_model.description = team_model.description - team_model.git_url = team_model.git_url + team_model.description = team.description + team_model.git_url = team.git_url await self.teams_repository.update(team_model) diff --git a/WEB/src/api/auth/loginRequest.js b/WEB/src/api/auth/loginRequest.js index 2e81cb1..f49d27d 100644 --- a/WEB/src/api/auth/loginRequest.js +++ b/WEB/src/api/auth/loginRequest.js @@ -6,10 +6,13 @@ const loginUser = async (loginData) => { const response = await axios.post(`${CONFIG.BASE_URL}/auth/`, loginData, { withCredentials: true, }); - return response.data.access_token; + + const { access_token, user_id } = response.data; + + return { access_token, user_id }; } catch (error) { - if (error.status === 401) { - throw new Error("Неверное имя пользователя или пароль") + if (error.response?.status === 401) { + throw new Error("Неверное имя пользователя или пароль"); } throw new Error(error.message); diff --git a/WEB/src/api/contests/getContests.js b/WEB/src/api/contests/getContests.js new file mode 100644 index 0000000..01ca95e --- /dev/null +++ b/WEB/src/api/contests/getContests.js @@ -0,0 +1,18 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const fetchContests = async () => { + try { + const response = await axios.get(`${CONFIG.BASE_URL}/contests`, { + withCredentials: true, + }); + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа к конкурсам (401)"); + } + throw new Error(error.message); + } +}; + +export default fetchContests; diff --git a/WEB/src/api/profiles/getUserProfile.js b/WEB/src/api/profiles/getUserProfile.js new file mode 100644 index 0000000..9070314 --- /dev/null +++ b/WEB/src/api/profiles/getUserProfile.js @@ -0,0 +1,17 @@ +import axios from 'axios' +import CONFIG from '../../core/config.js' + +const getUserProfile = async (user_id, token) => { + try { + const response = await axios.get(`${CONFIG.BASE_URL}/profiles/${user_id}/`, { + headers: { + Authorization: `Bearer ${token}` + } + }) + return response.data + } catch (error) { + throw new Error(error.response?.data?.detail || 'Ошибка получения профиля') + } +} + +export default getUserProfile diff --git a/WEB/src/api/projects/getProjects.js b/WEB/src/api/projects/getProjects.js new file mode 100644 index 0000000..d0c5fdf --- /dev/null +++ b/WEB/src/api/projects/getProjects.js @@ -0,0 +1,18 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const fetchProjects = async () => { + try { + const response = await axios.get(`${CONFIG.BASE_URL}/projects`, { + withCredentials: true, + }); + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа к проектам (401)"); + } + throw new Error(error.message); + } +}; + +export default fetchProjects; diff --git a/WEB/src/api/teams/getTeams.js b/WEB/src/api/teams/getTeams.js new file mode 100644 index 0000000..b6b6650 --- /dev/null +++ b/WEB/src/api/teams/getTeams.js @@ -0,0 +1,24 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const fetchTeams = async () => { + try { + const token = localStorage.getItem("access_token"); + const response = await axios.get(`${CONFIG.BASE_URL}/teams`, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа к командам (401)"); + } else if (error.response?.status === 403) { + throw new Error("Доступ запрещён (403)"); + } + throw new Error(error.message); + } +}; + +export default fetchTeams; diff --git a/WEB/src/api/teams/updateTeam.js b/WEB/src/api/teams/updateTeam.js new file mode 100644 index 0000000..3cfd514 --- /dev/null +++ b/WEB/src/api/teams/updateTeam.js @@ -0,0 +1,17 @@ +import axios from 'axios' +import CONFIG from '@/core/config.js' + +const updateTeam = async (team) => { + try { + const response = await axios.put( + `${CONFIG.BASE_URL}/teams/${team.id}`, + team, + { withCredentials: true } + ) + return response.data + } catch (error) { + throw new Error(error.response?.data?.detail || error.message) + } +} + +export default updateTeam diff --git a/WEB/src/api/users/getUsers.js b/WEB/src/api/users/getUsers.js new file mode 100644 index 0000000..5411f49 --- /dev/null +++ b/WEB/src/api/users/getUsers.js @@ -0,0 +1,18 @@ +import axios from "axios"; +import CONFIG from "@/core/config.js"; + +const fetchUsers = async () => { + try { + const response = await axios.get(`${CONFIG.BASE_URL}/users`, { + withCredentials: true, + }); + return response.data; + } catch (error) { + if (error.response?.status === 401) { + throw new Error("Нет доступа к пользователям (401)"); + } + throw new Error(error.message); + } +}; + +export default fetchUsers; diff --git a/WEB/src/components/HelloWorld.vue b/WEB/src/components/HelloWorld.vue deleted file mode 100644 index 546ebbc..0000000 --- a/WEB/src/components/HelloWorld.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - - - diff --git a/WEB/src/main.js b/WEB/src/main.js index 337e9ff..31ad124 100644 --- a/WEB/src/main.js +++ b/WEB/src/main.js @@ -1,8 +1,33 @@ -import { createApp } from 'vue' +import {createApp} from 'vue' import App from './App.vue' -import { Quasar, Notify, Dialog, Loading, QInput, QBtn, QForm, QCard, QCardSection } from 'quasar' import router from './router' +import { + Quasar, + Notify, + Dialog, + Loading, + QInput, + QBtn, + QForm, + QCard, + QCardSection, + QLayout, + QPageContainer, + QPage, + QTabs, + QTab, + QTabPanels, + QTabPanel, + QHeader, + QTable, + QSeparator, + QCardActions, + QDialog, + QIcon +} from 'quasar' + + import '@quasar/extras/material-icons/material-icons.css' @@ -11,8 +36,13 @@ import 'quasar/src/css/index.sass' const app = createApp(App) app.use(Quasar, { - plugins: { Notify, Dialog, Loading }, // уведомления, диалоги, загрузка - components: { QInput, QBtn, QForm, QCard, QCardSection } // обязательно указать используемые компоненты + plugins: {Notify, Dialog, Loading}, + components: { + QInput, QBtn, QForm, QCard, QCardSection, + QLayout, QPageContainer, QPage, + QTabs, QTab, QTabPanels, QTabPanel, QHeader,QTable, + QSeparator, QCardActions, QDialog, QIcon + } }) app.use(router) diff --git a/WEB/src/pages/AdminPage.vue b/WEB/src/pages/AdminPage.vue new file mode 100644 index 0000000..e412d94 --- /dev/null +++ b/WEB/src/pages/AdminPage.vue @@ -0,0 +1,271 @@ + + + + + diff --git a/WEB/src/pages/HomePage.vue b/WEB/src/pages/HomePage.vue index c5a6685..0e81331 100644 --- a/WEB/src/pages/HomePage.vue +++ b/WEB/src/pages/HomePage.vue @@ -43,4 +43,4 @@ const logout = () => { min-height: 100vh; gap: 20px; } - + \ No newline at end of file diff --git a/WEB/src/pages/LoginPage.vue b/WEB/src/pages/LoginPage.vue index 9a58d59..5ae0083 100644 --- a/WEB/src/pages/LoginPage.vue +++ b/WEB/src/pages/LoginPage.vue @@ -2,7 +2,11 @@ import { ref, onMounted, onBeforeUnmount } from 'vue' import { useRouter } from 'vue-router' import { Notify } from 'quasar' -import loginUser from "../api/auth/loginRequest.js" +import axios from 'axios' + +import CONFIG from '../core/config.js' +import loginUser from '../api/auth/loginRequest.js' + const router = useRouter() const login = ref('') @@ -11,14 +15,12 @@ const loading = ref(false) const marqueeTrack = ref(null) const marqueeText = ref(null) -const animationDuration = ref(7) // секунд +const animationDuration = ref(7) -// Динамически рассчитываем длительность анимации в зависимости от ширины текста const updateAnimation = () => { if (marqueeTrack.value && marqueeText.value) { const textWidth = marqueeText.value.offsetWidth const containerWidth = marqueeTrack.value.offsetWidth / 2 - // Скорость: 100px/sec (можно подстроить) const speed = 100 animationDuration.value = (textWidth + containerWidth) / speed marqueeTrack.value.style.setProperty('--marquee-width', `${textWidth}px`) @@ -27,7 +29,7 @@ const updateAnimation = () => { } onMounted(() => { - setTimeout(updateAnimation, 100) // Дать DOM отрисоваться + setTimeout(updateAnimation, 100) window.addEventListener('resize', updateAnimation) }) onBeforeUnmount(() => { @@ -37,12 +39,21 @@ onBeforeUnmount(() => { const authorisation = async () => { loading.value = true try { - const accessToken = await loginUser({ + const { access_token, user_id } = await loginUser({ login: login.value, password: password.value }) - localStorage.setItem('access_token', accessToken) + localStorage.setItem('access_token', access_token) + localStorage.setItem('user_id', user_id) + + const profileResponse = await axios.get(`${CONFIG.BASE_URL}/profiles/user/${user_id}/`, { + headers: { Authorization: `Bearer ${access_token}` } + }) + + const roleId = profileResponse.data.role_id + + Notify.create({ type: 'positive', @@ -50,7 +61,21 @@ const authorisation = async () => { icon: 'check_circle' }) - router.push('/') + console.log('Role ID:', roleId) + if (roleId === 1) { + console.log('Переход на /admin') + router.push('/admin') + } else { + console.log('Переход на /') + router.push('/') + } + + + if (roleId === 1) { + router.push('/admin') + } else { + router.push('/') + } } catch (error) { Notify.create({ type: 'negative', @@ -63,6 +88,7 @@ const authorisation = async () => { } +