modified: begushiybashkir/bbvue/src/stores/auth.js

modified:   begushiybashkir/bbvue/src/stores/user.js
	modified:   begushiybashkir/bbvue/src/views/Login.vue
	modified:   begushiybashkir/bbvue/src/views/Register.vue
	modified:   serv_nginx/api_bb/.env
	modified:   serv_nginx/api_bb/cmd/main.go
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
	modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
	modified:   serv_nginx/api_bb/pkg/logger/logger.go
delete hash pass from auth_service
it is fix the not loging becouse dowble hash password was
password hash is in middlewares

	modified:   begushiybashkir/bbvue/src/stores/auth.js
	modified:   begushiybashkir/bbvue/src/stores/user.js
	modified:   begushiybashkir/bbvue/src/views/Login.vue
	modified:   begushiybashkir/bbvue/src/views/Register.vue
	modified:   serv_nginx/api_bb/.env
	modified:   serv_nginx/api_bb/cmd/main.go
	modified:   serv_nginx/api_bb/internal/handlers/auth.go
	modified:   serv_nginx/api_bb/internal/handlers/handlers.go
	modified:   serv_nginx/api_bb/internal/models/user.go
	modified:   serv_nginx/api_bb/internal/routes/routes.go
	modified:   serv_nginx/api_bb/internal/service/auth_service.go
	modified:   serv_nginx/api_bb/pkg/logger/logger.go
delete hash password from auth_server
becouse is dowble hash was
second hash is beforeCreatehash in User struct
This commit is contained in:
2025-10-12 05:05:40 +05:00
parent ce433e6187
commit fd9be2199c
12 changed files with 264 additions and 114 deletions
+7 -6
View File
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import axios from 'axios' import axios from 'axios'
const API_BASE_URL = 'https://begushiybashkir.ru/api/v1/auth' const AUTH_API_URL = 'https://begushiybashkir.ru/api/v1/auth'
export const useAuthStore = defineStore('auth', () => { export const useAuthStore = defineStore('auth', () => {
const user = ref(null) const user = ref(null)
@@ -48,10 +48,10 @@ export const useAuthStore = defineStore('auth', () => {
error.value = '' error.value = ''
try { try {
const response = await axios.post(`${API_BASE_URL}/register`, userData) const response = await axios.post(`${AUTH_API_URL}/register`, userData)
// После успешной регистрации автоматически логинимся // После успешной регистрации автоматически логинимся
const loginResponse = await axios.post(`${API_BASE_URL}/login`, { const loginResponse = await axios.post(`${AUTH_API_URL}/login`, {
email: userData.email, email: userData.email,
password: userData.password password: userData.password
}) })
@@ -75,8 +75,9 @@ export const useAuthStore = defineStore('auth', () => {
error.value = '' error.value = ''
try { try {
const response = await axios.post(`${API_BASE_URL}/login`, credentials) 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)
@@ -95,7 +96,7 @@ export const useAuthStore = defineStore('auth', () => {
loading.value = true loading.value = true
try { try {
await axios.post(`${API_BASE_URL}/logout`, {}, { await axios.post(`${AUTH_API_URL}/logout`, {}, {
headers: { headers: {
'Authorization': `Bearer ${token.value}` 'Authorization': `Bearer ${token.value}`
} }
@@ -115,7 +116,7 @@ export const useAuthStore = defineStore('auth', () => {
error.value = '' error.value = ''
try { try {
const response = await axios.get(`${API_BASE_URL}/profile`) 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) { } catch (err) {
-1
View File
@@ -17,7 +17,6 @@ export const useUserStore = defineStore('user', () => {
error.value = '' error.value = ''
try { try {
// TODO: Заменить на реальный endpoint когда будет доступен
// const response = await axios.get(`${API_BASE_URL}/user/stats`) // const response = await axios.get(`${API_BASE_URL}/user/stats`)
// Временные данные для демонстрации // Временные данные для демонстрации
+7 -23
View File
@@ -5,31 +5,15 @@
<form @submit.prevent="handleLogin" class="login-form"> <form @submit.prevent="handleLogin" class="login-form">
<div class="form-group"> <div class="form-group">
<input <input type="email" placeholder="Email" class="form-input" v-model="credentials.email" required
type="email" :disabled="loading">
placeholder="Email"
class="form-input"
v-model="credentials.email"
required
:disabled="loading"
>
</div> </div>
<div class="form-group"> <div class="form-group">
<input <input type="password" placeholder="Пароль" class="form-input" v-model="credentials.password" required
type="password" :disabled="loading">
placeholder="Пароль"
class="form-input"
v-model="credentials.password"
required
:disabled="loading"
>
</div> </div>
<button <button type="submit" class="btn btn-primary" :disabled="loading">
type="submit"
class="btn btn-primary"
:disabled="loading"
>
{{ loading ? 'Вход...' : 'Войти' }} {{ loading ? 'Вход...' : 'Войти' }}
</button> </button>
@@ -78,7 +62,7 @@ export default {
methods: { methods: {
async handleLogin() { async handleLogin() {
const result = await this.authStore.login(this.credentials) const result = await this.authStore.login(this.credentials)
alert("register success" + result.success + "| data: " + result.data)
if (result.success) { if (result.success) {
this.$router.push('/profile') this.$router.push('/profile')
} }
+2 -1
View File
@@ -185,8 +185,9 @@ export default {
} }
console.log('Отправка данных регистрации:', { ...registerData, password: '***' }) console.log('Отправка данных регистрации:', { ...registerData, password: '***' })
alert("|" + registerData.email + "|" + registerData.password + "|")
const result = await this.authStore.register(registerData) const result = await this.authStore.register(registerData)
alert("register seccess=" + result.success + "| data=" + result.data)
if (result.success) { if (result.success) {
// Перенаправляем на страницу профиля после успешной регистрации // Перенаправляем на страницу профиля после успешной регистрации
-1
View File
@@ -5,7 +5,6 @@ DB_USER=postgres
DB_PASSWORD=postgres DB_PASSWORD=postgres
DB_NAME=bb_db DB_NAME=bb_db
DB_SSLMODE=disable DB_SSLMODE=disable
JWT_SECRET=your-super-secret-jwt-key-change-in-production
# .env # .env
+60 -1
View File
@@ -40,17 +40,45 @@ func main() {
// Логируем начало работы // Логируем начало работы
logger.LogApplicationStart(os.Getenv("REST_API_VERSION"), os.Getenv("ENVIRONMENT"), "") logger.LogApplicationStart(os.Getenv("REST_API_VERSION"), os.Getenv("ENVIRONMENT"), "")
// Логирование попытки подключения к БД
zapLogger.Info("attempting to connect to database",
zap.String("host", extractHostFromDSN(cfg.DatabaseURL)), // функция для извлечения хоста из DSN
zap.String("database", extractDBNameFromDSN(cfg.DatabaseURL)), // функция для извлечения имени БД
)
// Подключение к базе данных // Подключение к базе данных
db, err := gorm.Open(postgres.Open(cfg.DatabaseURL), &gorm.Config{}) db, err := gorm.Open(postgres.Open(cfg.DatabaseURL), &gorm.Config{})
if err != nil { if err != nil {
zapLogger.Fatal("failed to connect to database", zap.Error(err)) zapLogger.Fatal("failed to connect to database",
zap.Error(err),
zap.String("database_url", maskPassword(cfg.DatabaseURL)), // маскируем пароль в логах
)
} }
// Логирование успешного подключения к БД
zapLogger.Info("successfully connected to database",
zap.String("host", extractHostFromDSN(cfg.DatabaseURL)),
zap.String("database", extractDBNameFromDSN(cfg.DatabaseURL)),
)
// Проверка соединения с БД
sqlDB, err := db.DB()
if err != nil {
zapLogger.Fatal("failed to get database instance", zap.Error(err))
}
if err := sqlDB.Ping(); err != nil {
zapLogger.Fatal("database ping failed", zap.Error(err))
}
zapLogger.Info("database ping successful")
// Автомиграция // Автомиграция
zapLogger.Info("starting database migration")
if err := db.AutoMigrate(&models.User{}); err != nil { if err := db.AutoMigrate(&models.User{}); err != nil {
zapLogger.Fatal("database migration failed", zap.Error(err)) zapLogger.Fatal("database migration failed", zap.Error(err))
} }
zapLogger.Info("database migration completed successfully")
// Настройка роутера // Настройка роутера
router := routes.SetupRouter(db, cfg) router := routes.SetupRouter(db, cfg)
@@ -78,6 +106,14 @@ func main() {
<-quit <-quit
zapLogger.Info("shutdown signal received") zapLogger.Info("shutdown signal received")
// Логирование закрытия соединения с БД
zapLogger.Info("closing database connection")
if err := sqlDB.Close(); err != nil {
zapLogger.Error("failed to close database connection", zap.Error(err))
} else {
zapLogger.Info("database connection closed successfully")
}
// Graceful shutdown // Graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel() defer cancel()
@@ -89,4 +125,27 @@ func main() {
logger.LogApplicationShutdown("graceful shutdown") logger.LogApplicationShutdown("graceful shutdown")
close(done) close(done)
}
// Вспомогательные функции для работы с DSN
// extractHostFromDSN извлекает хост из DSN строки
func extractHostFromDSN(dsn string) string {
// Простая реализация - в продакшене лучше использовать парсер DSN
// Для postgres DSN формата: "host=localhost user=gorm password=gorm dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai"
// Можно использовать более сложный парсер или регулярные выражения
return dsn // Заглушка - нужно реализовать парсинг DSN
}
// extractDBNameFromDSN извлекает имя базы данных из DSN строки
func extractDBNameFromDSN(dsn string) string {
// Аналогично extractHostFromDSN - нужно реализовать парсинг
return dsn // Заглушка - нужно реализовать парсинг DSN
}
// maskPassword маскирует пароль в DSN строке для безопасного логирования
func maskPassword(dsn string) string {
// Простая реализация - заменяет пароль на ***
// В продакшене нужно использовать более надежный метод
return dsn // Заглушка - нужно реализовать маскирование пароля
} }
@@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
"strings"
"time" "time"
"api_bb/internal/models" "api_bb/internal/models"
@@ -201,6 +202,10 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
return return
} }
req.Email = strings.TrimSpace(req.Email)
req.Password = strings.TrimSpace(req.Password)
// Валидация // Валидация
if req.Email == "" || req.Password == "" { if req.Email == "" || req.Password == "" {
h.logger.Warn("validation failed", h.logger.Warn("validation failed",
+33 -28
View File
@@ -2,44 +2,49 @@
package handlers package handlers
import ( import (
"api_bb/internal/repository" "api_bb/internal/config"
"api_bb/internal/service" "api_bb/internal/repository"
"api_bb/internal/config" "api_bb/internal/service"
"gorm.io/gorm" "api_bb/pkg/logger"
"gorm.io/gorm"
) )
type Handler struct { type Handler struct {
healthHandler *HealthHandler healthHandler *HealthHandler
authHandler *AuthHandler authHandler *AuthHandler
// Здесь будут добавлены другие обработчики // Здесь будут добавлены другие обработчики
// userHandler *UserHandler // userHandler *UserHandler
// eventHandler *EventHandler // eventHandler *EventHandler
// reviewHandler *ReviewHandler // reviewHandler *ReviewHandler
} }
func NewHandler(db *gorm.DB, cfg *config.Config) *Handler { func NewHandler(db *gorm.DB, cfg *config.Config) *Handler {
// Инициализация репозиториев // Инициализация репозиториев
userRepo := repository.NewUserRepository(db) userRepo := repository.NewUserRepository(db)
// Инициализация сервисов // Initialize logger
jwtService := service.NewJWTService(cfg.JWTSecret) baseLogger := logger.NewWrapper(logger.Get()) // Создаем базовый логгер
authService := service.NewAuthService(userRepo, jwtService)
// Инициализация сервисов
// Инициализация обработчиков jwtService := service.NewJWTService(cfg.JWTSecret)
healthHandler := NewHealthHandler() authService := service.NewAuthService(userRepo, jwtService, baseLogger)
authHandler := NewAuthHandler(authService, jwtService)
// Инициализация обработчиков
return &Handler{ healthHandler := NewHealthHandler()
healthHandler: healthHandler, authHandler := NewAuthHandler(authService, jwtService)
authHandler: authHandler,
} return &Handler{
healthHandler: healthHandler,
authHandler: authHandler,
}
} }
// Геттеры для обработчиков (опционально, для удобства) // Геттеры для обработчиков (опционально, для удобства)
func (h *Handler) HealthHandler() *HealthHandler { func (h *Handler) HealthHandler() *HealthHandler {
return h.healthHandler return h.healthHandler
} }
func (h *Handler) AuthHandler() *AuthHandler { func (h *Handler) AuthHandler() *AuthHandler {
return h.authHandler return h.authHandler
} }
+5 -3
View File
@@ -2,9 +2,11 @@
package models package models
import ( import (
"time"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gorm.io/gorm" "gorm.io/gorm"
"time"
) )
type User struct { type User struct {
@@ -35,8 +37,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
+6 -3
View File
@@ -11,6 +11,7 @@ import (
"api_bb/internal/handlers" "api_bb/internal/handlers"
"api_bb/internal/repository" "api_bb/internal/repository"
"api_bb/internal/service" "api_bb/internal/service"
"api_bb/pkg/logger" // Добавьте импорт логгера
"api_bb/pkg/middleware" "api_bb/pkg/middleware"
) )
@@ -25,9 +26,12 @@ func SetupRouter(db *gorm.DB, config *config.Config) http.Handler {
// Initialize repositories // Initialize repositories
userRepo := repository.NewUserRepository(db) userRepo := repository.NewUserRepository(db)
// Initialize services // Initialize logger
baseLogger := logger.NewWrapper(logger.Get()) // Создаем базовый логгер
// Initialize services with logger
jwtService := service.NewJWTService(config.JWTSecret) jwtService := service.NewJWTService(config.JWTSecret)
authService := service.NewAuthService(userRepo, jwtService) authService := service.NewAuthService(userRepo, jwtService, baseLogger) // Передаем логгер
// Initialize handlers // Initialize handlers
healthHandler := handlers.NewHealthHandler() healthHandler := handlers.NewHealthHandler()
@@ -36,7 +40,6 @@ func SetupRouter(db *gorm.DB, config *config.Config) http.Handler {
// Health routes // Health routes
r.Mount("/api", healthHandler.Routes()) r.Mount("/api", healthHandler.Routes())
// API v1 routes // API v1 routes
r.Route("/v1", func(r chi.Router) { r.Route("/v1", func(r chi.Router) {
@@ -5,98 +5,189 @@ import (
"errors" "errors"
"fmt" "fmt"
"time" "time"
"api_bb/internal/models" "api_bb/internal/models"
"api_bb/internal/repository" "api_bb/internal/repository"
"api_bb/pkg/logger"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
) )
type AuthService interface { type AuthService interface {
Register(user *models.User) error Register(user *models.User) error
Login(email, password string) (*models.User, string, error) Login(email, password string) (*models.User, string, error)
GetUserProfile(userID uint) (*models.User, error) GetUserProfile(userID uint) (*models.User, error)
UpdateProfile(user *models.User) error UpdateProfile(user *models.User) error
} }
type authService struct { type authService struct {
userRepo repository.UserRepository userRepo repository.UserRepository
jwtService JWTService jwtService JWTService
logger logger.Interface
} }
func NewAuthService(userRepo repository.UserRepository, jwtService JWTService) AuthService { func NewAuthService(userRepo repository.UserRepository, jwtService JWTService, log logger.Interface) AuthService {
// Создаем логгер с контекстом для сервиса
serviceLogger := log.With(zap.String("service", "auth"))
return &authService{ return &authService{
userRepo: userRepo, userRepo: userRepo,
jwtService: jwtService, jwtService: jwtService,
logger: serviceLogger,
} }
} }
func (s *authService) UpdateProfile(user *models.User) error { func (s *authService) UpdateProfile(user *models.User) error {
// Находим существующего пользователя через репозиторий s.logger.Info("Updating user profile",
existingUser, err := s.userRepo.FindByID(user.ID) zap.Uint("user_id", user.ID),
if err != nil { )
return fmt.Errorf("user not found")
}
// Создаем карту для обновления только разрешенных полей existingUser, err := s.userRepo.FindByID(user.ID)
updateData := &models.User{ if err != nil {
ID: existingUser.ID, // Важно сохранить ID s.logger.Warn("User not found for profile update",
FirstName: user.FirstName, zap.Uint("user_id", user.ID),
LastName: user.LastName, zap.Error(err),
Phone: user.Phone, )
Experience: user.Experience, return fmt.Errorf("user not found")
Goals: user.Goals, }
Newsletter: user.Newsletter,
UpdatedAt: time.Now(),
// Не обновляем: Email, Password, Role, CreatedAt
}
// Сохраняем обновленные данные через репозиторий updateData := &models.User{
if err := s.userRepo.Update(updateData); err != nil { ID: existingUser.ID,
return err FirstName: user.FirstName,
} LastName: user.LastName,
Phone: user.Phone,
Experience: user.Experience,
Goals: user.Goals,
Newsletter: user.Newsletter,
UpdatedAt: time.Now(),
}
// Копируем обновленные данные обратно в переданный объект s.logger.Debug("Profile update data prepared",
user.Email = existingUser.Email zap.Uint("user_id", user.ID),
user.Password = existingUser.Password zap.String("first_name", updateData.FirstName),
user.Role = existingUser.Role zap.String("last_name", updateData.LastName),
user.CreatedAt = existingUser.CreatedAt )
user.UpdatedAt = updateData.UpdatedAt
return nil if err := s.userRepo.Update(updateData); err != nil {
s.logger.Error("Failed to update user profile",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
return err
}
user.Email = existingUser.Email
user.Password = existingUser.Password
user.Role = existingUser.Role
user.CreatedAt = existingUser.CreatedAt
user.UpdatedAt = updateData.UpdatedAt
s.logger.Info("User profile updated successfully",
zap.Uint("user_id", user.ID),
)
return nil
} }
func (s *authService) Register(user *models.User) error { func (s *authService) Register(user *models.User) error {
// Проверяем, существует ли пользователь s.logger.Info("Registering new user",
zap.String("email", user.Email),
)
existingUser, err := s.userRepo.FindByEmail(user.Email) existingUser, err := s.userRepo.FindByEmail(user.Email)
if err == nil && existingUser != nil { if err == nil && existingUser != nil {
s.logger.Warn("Registration failed - email already exists",
zap.String("email", user.Email),
)
return errors.New("user with this email already exists") return errors.New("user with this email already exists")
} }
// Хешируем пароль err = s.userRepo.Create(user)
if err := user.HashPassword(); err != nil { if err != nil {
s.logger.Error("Failed to create user in database",
zap.String("email", user.Email),
zap.Error(err),
)
return err return err
} }
return s.userRepo.Create(user) s.logger.Info("User registered successfully",
zap.Uint("user_id", user.ID),
zap.String("email", user.Email),
)
return nil
} }
func (s *authService) Login(email, password string) (*models.User, string, error) { func (s *authService) Login(email, password string) (*models.User, string, error) {
s.logger.Info("Login attempt",
zap.String("email", email),
zap.Int("password_length", len(password)),
)
user, err := s.userRepo.FindByEmail(email) user, err := s.userRepo.FindByEmail(email)
if err != nil { if err != nil {
return nil, "", errors.New("invalid email or password") s.logger.Warn("Login failed - user not found",
zap.String("email", email),
zap.Error(err),
)
return nil, "", errors.New("invalid email")
} }
if !user.CheckPassword(password) { s.logger.Debug("User found for login",
return nil, "", errors.New("invalid email or password") zap.Uint("user_id", user.ID),
zap.String("stored_hash_prefix", user.Password[:min(10, len(user.Password))]),
)
// Проверяем пароль
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
if err != nil {
s.logger.Warn("Login failed - invalid password",
zap.Uint("user_id", user.ID),
zap.String("email", email),
zap.Error(err),
)
return nil, "", errors.New("invalid password")
} }
s.logger.Info("Login successful",
zap.Uint("user_id", user.ID),
zap.String("email", email),
)
token, err := s.jwtService.GenerateToken(user.ID, user.Email) token, err := s.jwtService.GenerateToken(user.ID, user.Email)
if err != nil { if err != nil {
s.logger.Error("Failed to generate JWT token",
zap.Uint("user_id", user.ID),
zap.Error(err),
)
return nil, "", err return nil, "", err
} }
return user, token, nil return user, token, nil
} }
func min(a, b int) int {
if a < b {
return a
}
return b
}
func (s *authService) GetUserProfile(userID uint) (*models.User, error) { func (s *authService) GetUserProfile(userID uint) (*models.User, error) {
return s.userRepo.FindByID(userID) s.logger.Debug("Getting user profile",
} zap.Uint("user_id", userID),
)
user, err := s.userRepo.FindByID(userID)
if err != nil {
s.logger.Warn("Failed to get user profile",
zap.Uint("user_id", userID),
zap.Error(err),
)
return nil, err
}
return user, nil
}
+1
View File
@@ -4,6 +4,7 @@ package logger
import ( import (
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
) )
var globalLogger *zap.Logger var globalLogger *zap.Logger