create and moove into new directories for BegushiyBashkir and

yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarbacreate and moove into new directories for BegushiyBashkir and
yalarba
This commit is contained in:
2025-10-24 05:22:44 +05:00
parent 358c14428f
commit 15357fd3c0
211 changed files with 3 additions and 3 deletions
@@ -0,0 +1,215 @@
// service/avatar_service.go
package service
import (
"api_bb/internal/repository"
"api_bb/pkg/logger"
"fmt"
"io"
"mime/multipart"
"os"
"path/filepath"
"strings"
"time"
"go.uber.org/zap"
)
type AvatarService interface {
UploadAvatar(userID uint, file multipart.File, header *multipart.FileHeader) (string, error)
DeleteAvatar(userID uint) error
GetAvatarPath(userID uint) (string, error)
GetAvatarFile(filename string) ([]byte, string, error)
ServeAvatarFile(w io.Writer, filename string) (string, error)
}
type avatarService struct {
userRepo repository.UserRepository
logger logger.LoggerInterface
}
func NewAvatarService(userRepo repository.UserRepository, log logger.LoggerInterface) AvatarService {
return &avatarService{
userRepo: userRepo,
logger: log.With(zap.String("service", "avatar")),
}
}
func (s *avatarService) UploadAvatar(userID uint, file multipart.File, header *multipart.FileHeader) (string, error) {
// Проверяем пользователя
user, err := s.userRepo.FindByID(userID)
if err != nil {
return "", fmt.Errorf("user not found")
}
// Создаем директорию для аватаров если не существует
uploadDir := "./uploads/avatars"
if err := os.MkdirAll(uploadDir, 0755); err != nil {
return "", fmt.Errorf("failed to create upload directory: %v", err)
}
// Генерируем уникальное имя файла
fileExt := filepath.Ext(header.Filename)
fileName := fmt.Sprintf("avatar_%d_%d%s", userID, time.Now().Unix(), fileExt)
filePath := filepath.Join(uploadDir, fileName)
// Создаем файл
dst, err := os.Create(filePath)
if err != nil {
return "", fmt.Errorf("failed to create file: %v", err)
}
defer dst.Close()
// Копируем содержимое
if _, err := io.Copy(dst, file); err != nil {
return "", fmt.Errorf("failed to save file: %v", err)
}
// Удаляем старый аватар если существует
if user.Avatar != "" {
oldPath := strings.TrimPrefix(user.Avatar, "/")
if _, err := os.Stat(oldPath); err == nil {
os.Remove(oldPath)
}
}
// Сохраняем путь в БД
avatarPath := "/uploads/avatars/" + fileName
if err := s.userRepo.UpdateAvatar(userID, avatarPath); err != nil {
// Если не удалось сохранить в БД, удаляем загруженный файл
os.Remove(filePath)
return "", fmt.Errorf("failed to update avatar in database: %v", err)
}
return avatarPath, nil
}
func (s *avatarService) DeleteAvatar(userID uint) error {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return fmt.Errorf("user not found")
}
if user.Avatar == "" {
return nil // Аватара нет, ничего не делаем
}
// Удаляем файл
filePath := strings.TrimPrefix(user.Avatar, "/")
if _, err := os.Stat(filePath); err == nil {
if err := os.Remove(filePath); err != nil {
s.logger.Warn("Failed to delete avatar file", zap.Error(err))
}
}
// Очищаем поле в БД
return s.userRepo.UpdateAvatar(userID, "")
}
func (s *avatarService) GetAvatarPath(userID uint) (string, error) {
user, err := s.userRepo.FindByID(userID)
if err != nil {
return "", err
}
return user.Avatar, nil
}
func (s *avatarService) GetAvatarFile(filename string) ([]byte, string, error) {
// Валидация имени файла
if filename == "" || strings.Contains(filename, "..") || strings.Contains(filename, "/") {
return nil, "", fmt.Errorf("invalid filename")
}
// Проверяем допустимые расширения
allowedExts := map[string]string{
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
}
fileExt := strings.ToLower(filepath.Ext(filename))
contentType, exists := allowedExts[fileExt]
if !exists {
return nil, "", fmt.Errorf("unsupported file format")
}
// Формируем путь к файлу
filePath := filepath.Join("./uploads/avatars", filename)
// Проверяем существование файла
fileInfo, err := os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
return nil, "", fmt.Errorf("avatar file not found")
}
return nil, "", fmt.Errorf("failed to access file: %v", err)
}
// Проверяем размер файла (максимум 10MB)
if fileInfo.Size() > 10*1024*1024 {
return nil, "", fmt.Errorf("file too large")
}
// Читаем файл
fileData, err := os.ReadFile(filePath)
if err != nil {
return nil, "", fmt.Errorf("failed to read file: %v", err)
}
return fileData, contentType, nil
}
func (s *avatarService) ServeAvatarFile(w io.Writer, filename string) (string, error) {
// Валидация имени файла
if filename == "" || strings.Contains(filename, "..") || strings.Contains(filename, "/") {
return "", fmt.Errorf("invalid filename")
}
// Проверяем допустимые расширения
allowedExts := map[string]string{
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
}
fileExt := strings.ToLower(filepath.Ext(filename))
contentType, exists := allowedExts[fileExt]
if !exists {
return "", fmt.Errorf("unsupported file format")
}
// Формируем путь к файлу
filePath := filepath.Join("./uploads/avatars", filename)
// Проверяем существование файла
fileInfo, err := os.Stat(filePath)
if err != nil {
if os.IsNotExist(err) {
return "", fmt.Errorf("avatar file not found")
}
return "", fmt.Errorf("failed to access file: %v", err)
}
// Проверяем размер файла
if fileInfo.Size() > 10*1024*1024 {
return "", fmt.Errorf("file too large")
}
// Открываем и копируем файл
file, err := os.Open(filePath)
if err != nil {
return "", fmt.Errorf("failed to open file: %v", err)
}
defer file.Close()
_, err = io.Copy(w, file)
if err != nil {
return "", fmt.Errorf("failed to serve file: %v", err)
}
return contentType, nil
}