modified: begushiybashkir/bbvue/src/main.js
modified: begushiybashkir/bbvue/src/stores/auth.js new file: begushiybashkir/bbvue/src/stores/helpers/api.js new file: begushiybashkir/bbvue/src/stores/index.js new file: begushiybashkir/bbvue/src/stores/plugins/persistence.js modified: begushiybashkir/bbvue/src/stores/user.js modified: serv_nginx/api_bb/internal/models/user.go new file: serv_nginx/api_bb/internal/models/workout.go fix save store, add helpers, add new models
This commit is contained in:
@@ -1,44 +1,14 @@
|
|||||||
import './assets/main.css'
|
import './assets/main.css'
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import pinia from './stores'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
// Глобальная конфигурация axios
|
|
||||||
axios.defaults.baseURL = 'https://begushiybashkir.ru/api/v1'
|
|
||||||
axios.defaults.withCredentials = true // Для работы с куками
|
|
||||||
|
|
||||||
// Интерцептор для автоматического добавления токена
|
|
||||||
axios.interceptors.request.use((config) => {
|
|
||||||
const token = localStorage.getItem('auth_token')
|
|
||||||
if (token) {
|
|
||||||
config.headers.Authorization = `Bearer ${token}`
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
})
|
|
||||||
|
|
||||||
// Интерцептор для обработки ошибок авторизации
|
|
||||||
axios.interceptors.response.use(
|
|
||||||
(response) => response,
|
|
||||||
(error) => {
|
|
||||||
if (error.response?.status === 401) {
|
|
||||||
// Токен истек или невалиден
|
|
||||||
const authStore = useAuthStore()
|
|
||||||
authStore.clearToken()
|
|
||||||
authStore.clearUser()
|
|
||||||
window.location.href = '/login'
|
|
||||||
}
|
|
||||||
return Promise.reject(error)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(createPinia())
|
app.use(pinia)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
|
|
||||||
// Инициализация auth store после создания app
|
// Инициализация auth store после создания app
|
||||||
|
|||||||
@@ -1,57 +1,45 @@
|
|||||||
|
// stores/auth.js
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import axios from 'axios'
|
import { apiClient, withLoading } from './helpers/api'
|
||||||
|
|
||||||
const AUTH_API_URL = 'https://begushiybashkir.ru/api/v1/auth'
|
|
||||||
|
|
||||||
export const useAuthStore = defineStore('auth', () => {
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
|
// State
|
||||||
const user = ref(null)
|
const user = ref(null)
|
||||||
const token = ref(localStorage.getItem('auth_token') || '')
|
const token = ref(localStorage.getItem('auth_token') || '')
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
|
const initialized = ref(false)
|
||||||
|
|
||||||
// Computed свойства
|
// Getters
|
||||||
const isAuthenticated = computed(() => !!token.value && !!user.value)
|
const isAuthenticated = computed(() => !!token.value && !!user.value)
|
||||||
const userFullName = computed(() => {
|
const userFullName = computed(() =>
|
||||||
if (!user.value) return ''
|
user.value ? `${user.value.firstName} ${user.value.lastName}` : ''
|
||||||
return `${user.value.firstName} ${user.value.lastName}`
|
)
|
||||||
})
|
|
||||||
|
|
||||||
// Установка токена
|
// Actions
|
||||||
const setToken = (newToken) => {
|
const setToken = (newToken) => {
|
||||||
token.value = newToken
|
token.value = newToken
|
||||||
localStorage.setItem('auth_token', newToken)
|
localStorage.setItem('auth_token', newToken)
|
||||||
// Устанавливаем токен в заголовки axios по умолчанию
|
|
||||||
axios.defaults.headers.common['Authorization'] = `Bearer ${newToken}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Очистка токена
|
const clearAuth = () => {
|
||||||
const clearToken = () => {
|
|
||||||
token.value = ''
|
token.value = ''
|
||||||
|
user.value = null
|
||||||
localStorage.removeItem('auth_token')
|
localStorage.removeItem('auth_token')
|
||||||
delete axios.defaults.headers.common['Authorization']
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Установка пользователя
|
|
||||||
const setUser = (userData) => {
|
const setUser = (userData) => {
|
||||||
user.value = userData
|
user.value = userData
|
||||||
}
|
}
|
||||||
|
|
||||||
// Очистка пользователя
|
|
||||||
const clearUser = () => {
|
|
||||||
user.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Регистрация
|
|
||||||
const register = async (userData) => {
|
const register = async (userData) => {
|
||||||
loading.value = true
|
// Передаем store объект с loading и error
|
||||||
error.value = ''
|
return withLoading({ loading, error }, async () => {
|
||||||
|
await apiClient.post('/auth/register', userData)
|
||||||
|
|
||||||
try {
|
// Auto-login after registration
|
||||||
const response = await axios.post(`${AUTH_API_URL}/register`, userData)
|
const loginResponse = await apiClient.post('/auth/login', {
|
||||||
|
|
||||||
// После успешной регистрации автоматически логинимся
|
|
||||||
const loginResponse = await axios.post(`${AUTH_API_URL}/login`, {
|
|
||||||
email: userData.email,
|
email: userData.email,
|
||||||
password: userData.password
|
password: userData.password
|
||||||
})
|
})
|
||||||
@@ -60,89 +48,62 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
setToken(authToken)
|
setToken(authToken)
|
||||||
setUser(userInfo)
|
setUser(userInfo)
|
||||||
|
|
||||||
return { success: true, data: response.data }
|
return { success: true }
|
||||||
} catch (err) {
|
})
|
||||||
error.value = err.response?.data?.message || err.message || 'Ошибка регистрации'
|
|
||||||
return { success: false, error: error.value }
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Логин
|
|
||||||
const login = async (credentials) => {
|
const login = async (credentials) => {
|
||||||
loading.value = true
|
return withLoading({ loading, error }, async () => {
|
||||||
error.value = ''
|
const response = await apiClient.post('/auth/login', credentials)
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await axios.post(`${AUTH_API_URL}/login`, credentials)
|
|
||||||
const { token: authToken, user: userInfo } = response.data
|
const { token: authToken, user: userInfo } = response.data
|
||||||
console.log("authToken: " + authToken + "userInfo: " + userInfo)
|
|
||||||
|
|
||||||
setToken(authToken)
|
setToken(authToken)
|
||||||
setUser(userInfo)
|
setUser(userInfo)
|
||||||
|
|
||||||
return { success: true, data: response.data }
|
return { success: true, data: response.data }
|
||||||
} catch (err) {
|
})
|
||||||
error.value = err.response?.data?.message || 'Ошибка входа'
|
|
||||||
return { success: false, error: error.value }
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Выход
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
loading.value = true
|
return withLoading({ loading, error }, async () => {
|
||||||
|
try {
|
||||||
try {
|
await apiClient.post('/auth/logout')
|
||||||
await axios.post(`${AUTH_API_URL}/logout`, {}, {
|
} catch (err) {
|
||||||
headers: {
|
console.error('Logout error:', err)
|
||||||
'Authorization': `Bearer ${token.value}`
|
} finally {
|
||||||
}
|
clearAuth()
|
||||||
})
|
}
|
||||||
} catch (err) {
|
return { success: true }
|
||||||
console.error('Ошибка при выходе:', err)
|
})
|
||||||
} finally {
|
|
||||||
clearToken()
|
|
||||||
clearUser()
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получение профиля
|
|
||||||
const fetchProfile = async () => {
|
const fetchProfile = async () => {
|
||||||
loading.value = true
|
return withLoading({ loading, error }, async () => {
|
||||||
error.value = ''
|
const response = await apiClient.get('/user/profile')
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await axios.get(`${AUTH_API_URL}/profile`)
|
|
||||||
setUser(response.data)
|
setUser(response.data)
|
||||||
return { success: true, data: response.data }
|
return { success: true, data: response.data }
|
||||||
} catch (err) {
|
})
|
||||||
error.value = err.response?.data?.message || err.message || 'Ошибка загрузки профиля'
|
}
|
||||||
clearToken()
|
|
||||||
clearUser()
|
const updateProfile = async (profileData) => {
|
||||||
return { success: false, error: error.value }
|
return withLoading({ loading, error }, async () => {
|
||||||
} finally {
|
const response = await apiClient.post('/user/editProfile', profileData)
|
||||||
loading.value = false
|
setUser(response.data)
|
||||||
}
|
return { success: true, data: response.data }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация при загрузке приложения
|
|
||||||
const initializeAuth = async () => {
|
const initializeAuth = async () => {
|
||||||
if (token.value) {
|
if (initialized.value || !token.value) return
|
||||||
// Восстанавливаем заголовок авторизации
|
|
||||||
axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}`
|
initialized.value = true
|
||||||
try {
|
|
||||||
// Загружаем данные пользователя
|
try {
|
||||||
await fetchProfile()
|
await fetchProfile()
|
||||||
} catch (error) {
|
console.log('Auth restored successfully')
|
||||||
console.error('Ошибка инициализации авторизации:', error)
|
} catch (err) {
|
||||||
// Если токен невалидный, очищаем его
|
console.error('Auth restoration failed:', err)
|
||||||
clearToken()
|
clearAuth()
|
||||||
clearUser()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +113,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
token,
|
token,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
|
initialized,
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
@@ -162,9 +124,8 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
fetchProfile,
|
fetchProfile,
|
||||||
|
updateProfile,
|
||||||
initializeAuth,
|
initializeAuth,
|
||||||
setToken,
|
clearAuth
|
||||||
clearToken,
|
|
||||||
clearUser
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// stores/helpers/api.js
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const API_BASE_URL = 'https://begushiybashkir.ru/api/v1'
|
||||||
|
|
||||||
|
// Создаем экземпляр axios с базовой конфигурацией
|
||||||
|
export const apiClient = axios.create({
|
||||||
|
baseURL: API_BASE_URL,
|
||||||
|
withCredentials: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Интерцептор для автоматического добавления токена
|
||||||
|
apiClient.interceptors.request.use((config) => {
|
||||||
|
const token = localStorage.getItem('auth_token')
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
})
|
||||||
|
|
||||||
|
// Интерцептор для обработки ошибок
|
||||||
|
apiClient.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
if (error.response?.status === 401) {
|
||||||
|
localStorage.removeItem('auth_token')
|
||||||
|
window.location.href = '/login'
|
||||||
|
}
|
||||||
|
return Promise.reject(error)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Утилита для обработки ошибок
|
||||||
|
export const handleApiError = (error) => {
|
||||||
|
const message = error.response?.data?.message || error.message || 'Произошла ошибка'
|
||||||
|
return { success: false, error: message }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Утилита для выполнения запросов с loading state
|
||||||
|
export const withLoading = async (store, fn) => {
|
||||||
|
store.loading = true
|
||||||
|
store.error = ''
|
||||||
|
try {
|
||||||
|
return await fn()
|
||||||
|
} catch (error) {
|
||||||
|
const result = handleApiError(error)
|
||||||
|
store.error = result.error
|
||||||
|
return result
|
||||||
|
} finally {
|
||||||
|
store.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createLoadingHandler = (store) => {
|
||||||
|
return async (fn) => {
|
||||||
|
if (store && typeof store.loading !== 'undefined') {
|
||||||
|
store.loading = true
|
||||||
|
}
|
||||||
|
if (store && typeof store.error !== 'undefined') {
|
||||||
|
store.error = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await fn()
|
||||||
|
} catch (error) {
|
||||||
|
const result = handleApiError(error)
|
||||||
|
if (store && typeof store.error !== 'undefined') {
|
||||||
|
store.error = result.error
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} finally {
|
||||||
|
if (store && typeof store.loading !== 'undefined') {
|
||||||
|
store.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// stores/index.js
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
const pinia = createPinia()
|
||||||
|
|
||||||
|
export default pinia
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// stores/plugins/persistence.js
|
||||||
|
export const authPersistPlugin = ({ store }) => {
|
||||||
|
// Восстанавливаем состояние при инициализации
|
||||||
|
if (store.$id === 'auth') {
|
||||||
|
store.initializeAuth()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,53 @@
|
|||||||
|
// stores/user.js
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
// import axios from 'axios'
|
import { handleApiError } from './helpers/api'
|
||||||
|
|
||||||
// const API_BASE_URL = 'https://begushiybashkir.ru/api/v1'
|
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', () => {
|
export const useUserStore = defineStore('user', () => {
|
||||||
|
// State
|
||||||
const userStats = ref(null)
|
const userStats = ref(null)
|
||||||
const userTraining = ref(null)
|
const userTraining = ref(null)
|
||||||
const userAchievements = ref([])
|
const userAchievements = ref([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
|
|
||||||
// Получение статистики пользователя
|
// Getters
|
||||||
const fetchUserStats = async () => {
|
const completedAchievements = computed(() =>
|
||||||
|
userAchievements.value.filter(achievement => achievement.achieved)
|
||||||
|
)
|
||||||
|
|
||||||
|
const pendingAchievements = computed(() =>
|
||||||
|
userAchievements.value.filter(achievement => !achievement.achieved)
|
||||||
|
)
|
||||||
|
|
||||||
|
const achievementProgress = computed(() => {
|
||||||
|
if (!userAchievements.value.length) return 0
|
||||||
|
return Math.round((completedAchievements.value.length / userAchievements.value.length) * 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Вспомогательная функция для обработки loading/error
|
||||||
|
const withStoreLoading = async (fn) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
error.value = ''
|
error.value = ''
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// const response = await axios.get(`${API_BASE_URL}/user/stats`)
|
return await fn()
|
||||||
|
} catch (err) {
|
||||||
|
const result = handleApiError(err)
|
||||||
|
error.value = result.error
|
||||||
|
return result
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Временные данные для демонстрации
|
// Actions
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
const fetchUserStats = async () => {
|
||||||
|
return withStoreLoading(async () => {
|
||||||
|
// TODO: Заменить на реальный endpoint когда будет готов
|
||||||
|
// const response = await apiClient.get('/user/stats')
|
||||||
|
|
||||||
|
// Временные мок данные
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
|
||||||
userStats.value = {
|
userStats.value = {
|
||||||
totalDistance: 245,
|
totalDistance: 245,
|
||||||
@@ -33,24 +60,15 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return { success: true, data: userStats.value }
|
return { success: true, data: userStats.value }
|
||||||
} catch (err) {
|
})
|
||||||
error.value = err.response?.data?.message || 'Ошибка загрузки статистики'
|
|
||||||
return { success: false, error: error.value }
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получение плана тренировок
|
|
||||||
const fetchUserTraining = async () => {
|
const fetchUserTraining = async () => {
|
||||||
loading.value = true
|
return withStoreLoading(async () => {
|
||||||
error.value = ''
|
// TODO: Заменить на реальный endpoint когда будет готов
|
||||||
|
// const response = await apiClient.get('/user/training')
|
||||||
|
|
||||||
try {
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
// TODO: Заменить на реальный endpoint
|
|
||||||
// const response = await axios.get(`${API_BASE_URL}/user/training`)
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
|
|
||||||
userTraining.value = {
|
userTraining.value = {
|
||||||
currentWeek: 4,
|
currentWeek: 4,
|
||||||
@@ -64,24 +82,15 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return { success: true, data: userTraining.value }
|
return { success: true, data: userTraining.value }
|
||||||
} catch (err) {
|
})
|
||||||
error.value = err.response?.data?.message || 'Ошибка загрузки плана тренировок'
|
|
||||||
return { success: false, error: error.value }
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получение достижений
|
|
||||||
const fetchUserAchievements = async () => {
|
const fetchUserAchievements = async () => {
|
||||||
loading.value = true
|
return withStoreLoading(async () => {
|
||||||
error.value = ''
|
// TODO: Заменить на реальный endpoint когда будет готов
|
||||||
|
// const response = await apiClient.get('/user/achievements')
|
||||||
|
|
||||||
try {
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
// TODO: Заменить на реальный endpoint
|
|
||||||
// const response = await axios.get(`${API_BASE_URL}/user/achievements`)
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
|
|
||||||
userAchievements.value = [
|
userAchievements.value = [
|
||||||
{ id: 1, name: 'Первый забег', description: 'Пробежать первую 5км', achieved: true, date: '2024-01-20' },
|
{ id: 1, name: 'Первый забег', description: 'Пробежать первую 5км', achieved: true, date: '2024-01-20' },
|
||||||
@@ -92,27 +101,20 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return { success: true, data: userAchievements.value }
|
return { success: true, data: userAchievements.value }
|
||||||
} catch (err) {
|
})
|
||||||
error.value = err.response?.data?.message || 'Ошибка загрузки достижений'
|
|
||||||
return { success: false, error: error.value }
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computed свойства
|
// Пакетная загрузка всех данных пользователя
|
||||||
const completedAchievements = computed(() =>
|
const fetchAllUserData = async () => {
|
||||||
userAchievements.value.filter(achievement => achievement.achieved)
|
return withStoreLoading(async () => {
|
||||||
)
|
await Promise.all([
|
||||||
|
fetchUserStats(),
|
||||||
const pendingAchievements = computed(() =>
|
fetchUserTraining(),
|
||||||
userAchievements.value.filter(achievement => !achievement.achieved)
|
fetchUserAchievements()
|
||||||
)
|
])
|
||||||
|
return { success: true }
|
||||||
const achievementProgress = computed(() => {
|
})
|
||||||
if (!userAchievements.value.length) return 0
|
}
|
||||||
return Math.round((completedAchievements.value.length / userAchievements.value.length) * 100)
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
@@ -130,6 +132,7 @@ export const useUserStore = defineStore('user', () => {
|
|||||||
// Actions
|
// Actions
|
||||||
fetchUserStats,
|
fetchUserStats,
|
||||||
fetchUserTraining,
|
fetchUserTraining,
|
||||||
fetchUserAchievements
|
fetchUserAchievements,
|
||||||
|
fetchAllUserData
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -6,23 +6,22 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID uint `json:"id" gorm:"primaryKey"`
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
Email string `json:"email" gorm:"uniqueIndex;not null"`
|
Email string `json:"email" gorm:"uniqueIndex;not null"`
|
||||||
Password string `json:"-" gorm:"not null"`
|
Password string `json:"-" gorm:"not null"`
|
||||||
FirstName string `json:"first_name" gorm:"not null"`
|
FirstName string `json:"first_name" gorm:"not null"`
|
||||||
LastName string `json:"last_name" gorm:"not null"`
|
LastName string `json:"last_name" gorm:"not null"`
|
||||||
Phone string `json:"phone"`
|
Phone string `json:"phone"`
|
||||||
Experience string `json:"experience"`
|
Experience string `json:"experience"`
|
||||||
Goals string `json:"goals"`
|
Goals string `json:"goals"`
|
||||||
Newsletter bool `json:"newsletter"`
|
Newsletter bool `json:"newsletter"`
|
||||||
Role string `json:"role" gorm:"default:user"`
|
Role string `json:"role" gorm:"default:user"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashPassword хеширует пароль перед сохранением
|
// HashPassword хеширует пароль перед сохранением
|
||||||
@@ -37,8 +36,8 @@ func (u *User) HashPassword() error {
|
|||||||
|
|
||||||
// CheckPassword проверяет пароль
|
// CheckPassword проверяет пароль
|
||||||
func (u *User) CheckPassword(password string) bool {
|
func (u *User) CheckPassword(password string) bool {
|
||||||
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeCreate hook для GORM
|
// BeforeCreate hook для GORM
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Workout struct {
|
||||||
|
ID uint
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user