modified: begushiybashkir/bbvue/src/router/index.js

modified:   begushiybashkir/bbvue/src/views/Login.vue
	new file:   begushiybashkir/bbvue/src/views/Logout.vue
	modified:   begushiybashkir/bbvue/src/views/News.vue
	modified:   begushiybashkir/bbvue/src/views/Profile.vue
	modified:   serv_nginx/api_bb/cmd/main.go
	modified:   serv_nginx/api_bb/go.mod
	modified:   serv_nginx/api_bb/go.sum
	new file:   serv_nginx/api_bb/internal/app/app.go
	new file:   serv_nginx/api_bb/internal/database/database.go
	new file:   serv_nginx/api_bb/internal/database/migrate.go
	new file:   serv_nginx/api_bb/internal/handlers/news_handler.go
	new file:   serv_nginx/api_bb/internal/models/news.go
	new file:   serv_nginx/api_bb/internal/repository/comment_repository.go
	new file:   serv_nginx/api_bb/internal/repository/news_repository.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	new file:   serv_nginx/api_bb/internal/service/news_service.go
	modified:   serv_nginx/api_bb/pkg/utils/utils.go
save router paths to login  logout profile from upsunction commit
This commit is contained in:
2025-10-12 21:38:50 +05:00
parent 12f805f9e1
commit 6bb475acb2
18 changed files with 1424 additions and 309 deletions
+64 -12
View File
@@ -48,7 +48,8 @@ const router = createRouter({
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
component: () => import('../views/Login.vue'),
meta: { guestOnly: true }
},
{
path: '/profile',
@@ -59,7 +60,8 @@ const router = createRouter({
{
path: '/register',
name: 'Register',
component: () => import('../views/Register.vue')
component: () => import('../views/Register.vue'),
meta: { guestOnly: true }
},
{
path: '/profile/edit',
@@ -76,33 +78,83 @@ const router = createRouter({
path: '/privacy',
name: 'PrivacyPolicy',
component: () => import('../views/PrivacyPolicy.vue')
},
// Добавляем маршрут для выхода
{
path: '/logout',
name: 'Logout',
component: () => import('../views/Logout.vue')
}
]
})
// Функция для показа уведомлений
function showNotification(message) {
// Создаем элемент уведомления
const notification = document.createElement('div')
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #2e8b57;
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
max-width: 300px;
font-family: Arial, sans-serif;
`
notification.textContent = message
document.body.appendChild(notification)
// Автоматически удаляем через 3 секунды
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification)
}
}, 3000)
}
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore()
// Если пользователь переходит на защищенные страницы и не авторизован
// Проверяем, требует ли маршрут аутентификации
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
// Проверяем, есть ли токен в localStorage
// Если есть токен, пробуем загрузить профиль
if (authStore.token) {
try {
// Пытаемся загрузить профиль
await authStore.fetchProfile()
next()
return
} catch (error) {
console.log(error)
console.log('Token validation failed:', error)
// Если токен невалидный, очищаем его и редиректим на логин
authStore.clearAuth()
next('/login')
return
}
} else {
// Если нет токена, редиректим на логин
next('/login')
return
}
} else {
next()
}
// Проверяем, предназначен ли маршрут только для гостей
if (to.meta.guestOnly && authStore.isAuthenticated) {
showNotification("Вы уже авторизованы. Перенаправляем в профиль...")
// Ждем немного чтобы пользователь увидел уведомление, затем редиректим
setTimeout(() => {
next('/profile')
}, 2000)
return
}
// Если все проверки пройдены, разрешаем навигацию
next()
})
export default router
export default router
+65 -2
View File
@@ -62,11 +62,74 @@ export default {
methods: {
async handleLogin() {
const result = await this.authStore.login(this.credentials)
alert("register success" + result.success + "| data: " + result.data)
if (result.success) {
this.$router.push('/profile')
// Показываем уведомление об успешном входе
this.showSuccessNotification()
// Редиректим после небольшой задержки
setTimeout(() => {
this.$router.push('/profile')
}, 1500)
}
},
showSuccessNotification() {
const notification = document.createElement('div')
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #2e8b57;
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
max-width: 300px;
font-family: Arial, sans-serif;
`
notification.textContent = '✅ Вход выполнен успешно!'
document.body.appendChild(notification)
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification)
}
}, 3000)
}
},
// Добавляем проверку при монтировании компонента
mounted() {
// Если пользователь уже авторизован, показываем уведомление
if (this.authStore.isAuthenticated) {
this.showAlreadyLoggedInNotification()
}
},
showAlreadyLoggedInNotification() {
const notification = document.createElement('div')
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #ffd700;
color: #333;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
max-width: 300px;
font-family: Arial, sans-serif;
`
notification.textContent = 'ℹ️ Вы уже авторизованы!'
document.body.appendChild(notification)
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification)
}
}, 3000)
}
}
</script>
@@ -0,0 +1,51 @@
<template>
<div class="page">
<h1>🚪 Выход из системы</h1>
<div class="logout-content">
<p>Выполняется выход из системы...</p>
<div class="loading-spinner"></div>
</div>
</div>
</template>
<script>
import { useAuthStore } from '../stores/auth'
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'Logout',
setup() {
const authStore = useAuthStore()
return { authStore }
},
async mounted() {
// Выполняем выход
await this.authStore.logout()
// Редиректим на главную страницу
this.$router.push('/')
}
}
</script>
<style scoped>
.logout-content {
text-align: center;
padding: 2rem;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #2e8b57;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
+39 -188
View File
@@ -221,182 +221,13 @@ export default {
showNewsModal: false,
selectedNews: null,
subscribeEmail: '',
news: [], // Теперь пустой массив
filters: [
{ value: 'all', label: 'Все новости' },
{ value: 'events', label: 'События' },
{ value: 'training', label: 'Тренировки' },
{ value: 'achievements', label: 'Достижения' },
{ value: 'community', label: 'Сообщество' }
],
news: [
{
id: 1,
title: 'Открыт набор в новую группу для начинающих',
excerpt: 'Приглашаем всех желающих начать свой беговой путь. Бесплатное пробное занятие и индивидуальный подход к каждому.',
date: '2025-01-15',
category: 'training',
image: 'news1.jpg',
views: 124,
comments: 8,
content: `
<p>Дорогие друзья! Мы рады сообщить об открытии новой группы для начинающих бегунов. Если вы всегда хотели начать бегать, но не знали как — это ваш шанс!</p>
<h3>Что вас ждет:</h3>
<ul>
<li>✅ Бесплатная первая тренировка</li>
<li>✅ Постепенное увеличение нагрузок</li>
<li>✅ Работа над правильной техникой</li>
<li>✅ Поддержка опытных участников</li>
</ul>
<p><strong>Первая тренировка:</strong> 20 января в 19:30 в Парке Якутова</p>
<p>Не упустите возможность начать свой беговой путь в дружеской атмосфере нашего клуба!</p>
`
},
{
id: 2,
title: 'Стартует программа подготовки к Уфимскому марафону',
excerpt: '16-недельная программа подготовки для тех, кто хочет успешно выступить на главном беговом событии весны.',
date: '2025-01-10',
category: 'events',
image: 'news2.jpg',
views: 89,
comments: 12,
content: `
<p>Внимание всем бегунам! Открывается запись на специальную программу подготовки к Уфимскому марафону 2025.</p>
<h3>Детали программы:</h3>
<ul>
<li>📅 Продолжительность: 16 недель</li>
<li>🏃‍♂️ Тренировки: 3-4 раза в неделю</li>
<li>🎯 Дистанции: от 5км до марафона</li>
<li>👨‍🏫 Индивидуальные планы от тренера</li>
</ul>
<p>Программа включает в себя все аспекты подготовки: беговые объемы, силовую подготовку, питание и восстановление.</p>
<p><strong>Старт программы:</strong> 1 февраля 2025</p>
<p><strong>Место:</strong> Основные тренировки в Парке Якутова и на стадионе Динамо</p>
`
},
{
id: 3,
title: 'Итоги забега РосХим Стерлитамак 2025',
excerpt: 'Наши участники показали блестящие результаты на зимнем забеге. Поздравляем всех финишеров и призеров!',
date: '2025-01-05',
category: 'achievements',
image: 'news3.jpg',
views: 156,
comments: 15,
content: `
<p>Гордимся нашими бегунами! На прошедшем забеге РосХим в Стерлитамаке участники клуба "Бегущий Башкир" показали отличные результаты.</p>
<h3>Лучшие результаты:</h3>
<ul>
<li>🥇 Сергей — 1 место в возрастной категории (10км - 36:52)</li>
<li>🥈 Ильгам — 2 место (10км - 37:59)</li>
<li>🥉 Данил — 3 место (21км - 1:30:40)</li>
</ul>
<p>Всего от нашего клуба в забеге участвовало 12 человек, и каждый показал достойный результат!</p>
<p>Особые поздравления нашим новичкам, которые впервые преодолели дистанцию 10 км. Вы большие молодцы!</p>
<p>Следующий старт — Уфимский полумарафон 15 февраля. Готовимся!</p>
`
},
{
id: 4,
title: 'Зимний спортивный фестиваль от клуба',
excerpt: 'Приглашаем всех на зимний фестиваль бега с мастер-классами, эстафетами и горячим чаем.',
date: '2024-12-28',
category: 'community',
image: 'news4.jpg',
views: 78,
comments: 6,
content: `
<p>Дорогие друзья! Приглашаем вас на наш традиционный зимний спортивный фестиваль.</p>
<h3>Программа мероприятия:</h3>
<ul>
<li>⏰ 10:00 — Регистрация участников</li>
<li>🏃‍♂️ 10:30 — Эстафеты для всех возрастов</li>
<li>🎯 11:30 — Мастер-класс по технике зимнего бега</li>
<li>🍵 12:30 — Чаепитие с угощениями</li>
<li>🎁 13:00 — Награждение и розыгрыш призов</li>
</ul>
<p>Мероприятие бесплатное для всех участников клуба. Приглашаем также друзей и семьи!</p>
<p><strong>Когда:</strong> 15 января 2025</p>
<p><strong>Где:</strong> Парк Якутова, главная аллея</p>
<p>Не забудьте теплую одежду и хорошее настроение!</p>
`
},
{
id: 5,
title: 'Новые тренировочные программы от тренера',
excerpt: 'Загир Аминев разработал новые программы тренировок для разных уровней подготовки.',
date: '2024-12-20',
category: 'training',
image: 'news5.jpg',
views: 92,
comments: 3,
content: `
<p>Наш тренер Загир Аминев подготовил новые тренировочные программы, которые уже доступны для всех участников клуба.</p>
<h3>Что нового:</h3>
<ul>
<li>📊 Программа для начинающих (0-3 месяца)</li>
<li>⚡ Программа для любителей (3-12 месяцев)</li>
<li>🏆 Программа для продвинутых (1+ год)</li>
<li>🏔️ Специальная программа для трейлраннинга</li>
</ul>
<p>Каждая программа включает:</p>
<ul>
<li>✅ Подробное расписание тренировок</li>
<li>✅ Рекомендации по питанию</li>
<li>✅ План восстановления</li>
<li>✅ Советы по экипировке</li>
</ul>
<p>Получить программу можно у тренера на любой тренировке или написав в Telegram.</p>
`
},
{
id: 6,
title: 'Набор волонтеров на Уфимский марафон',
excerpt: 'Приглашаем желающих помочь в организации главного бегового события весны в Уфе.',
date: '2024-12-15',
category: 'community',
image: 'news6.jpg',
views: 64,
comments: 4,
content: `
<p>Друзья! Организационный комитет Уфимского марафона начинает набор волонтеров, и мы приглашаем участников нашего клуба присоединиться.</p>
<h3>Чем могут помочь волонтеры:</h3>
<ul>
<li>📋 Регистрация участников</li>
<li>🎽 Выдача стартовых пакетов</li>
<li>💧 Организация пунктов питания</li>
<li>🏁 Помощь на финише</li>
<li>📢 Информационная поддержка</li>
</ul>
<p>Все волонтеры получат:</p>
<ul>
<li>✅ Фирменную футболку волонтера</li>
<li>✅ Питание в день мероприятия</li>
<li>✅ Благодарственное письмо</li>
<li>✅ Незабываемые эмоции</li>
</ul>
<p>Если хотите стать часть команды волонтеров, пишите Загиру в Telegram.</p>
`
}
]
}
},
@@ -417,6 +248,44 @@ export default {
}
},
methods: {
async fetchNews() {
try {
const response = await this.$http.get('/api/news', {
params: {
limit: 20,
offset: 0
}
})
this.news = response.data.news
} catch (error) {
console.error('Failed to fetch news:', error)
// Fallback на локальные данные если API недоступно
this.news = this.getFallbackNews()
}
},
async openNewsModal(newsItem) {
try {
// Получаем полную новость с API
const response = await this.$http.get(`/api/news/${newsItem.id}`)
this.selectedNews = response.data
} catch (error) {
console.error('Failed to fetch news details:', error)
this.selectedNews = newsItem
}
this.showNewsModal = true
document.body.style.overflow = 'hidden'
},
async handleSubscribe() {
try {
await this.$http.post('/api/subscribe', { email: this.subscribeEmail })
alert('Спасибо за подписку! Проверьте вашу почту для подтверждения.')
this.subscribeEmail = ''
} catch (error) {
console.error('Subscription failed:', error)
alert('Ошибка подписки. Попробуйте позже.')
}
},
setFilter(filter) {
this.activeFilter = filter
this.visibleNews = 6
@@ -424,14 +293,6 @@ export default {
loadMore() {
this.visibleNews += 3
},
openNewsModal(newsItem) {
this.selectedNews = newsItem
this.showNewsModal = true
document.body.style.overflow = 'hidden'
// Увеличиваем счетчик просмотров
newsItem.views++
},
closeNewsModal() {
this.showNewsModal = false
document.body.style.overflow = ''
@@ -449,16 +310,6 @@ export default {
alert('Ссылка скопирована в буфер обмена!')
}
},
handleSubscribe() {
// Здесь будет логика подписки
console.log('Подписка на email:', this.subscribeEmail)
alert('Спасибо за подписку! Проверьте вашу почту для подтверждения.')
this.subscribeEmail = ''
},
getImageUrl(imageName) {
// Заглушка для изображений
return `https://via.placeholder.com/400x250/2e8b57/ffffff?text=${encodeURIComponent(imageName)}`
},
getCategoryLabel(category) {
const labels = {
'events': 'События',
+1 -1
View File
@@ -209,7 +209,7 @@ export default {
},
async handleLogout() {
await this.authStore.logout()
this.$router.push('/login')
this.$router.push('/')
},
editProfile() {
this.$router.push('/profile/edit')