v0.0.27.3
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
admin 2025-08-28 12:19:17 +03:00
parent d71d79dfcc
commit 8e247cdb0d
2 changed files with 22 additions and 76 deletions

View File

@ -45,21 +45,21 @@ func NewPostService(postRepo repository.PostRepository) PostService {
return &postService{postRepo: postRepo} return &postService{postRepo: postRepo}
} }
func (s *postService) CreateWithS3Upload(ctx context.Context, authorID int, title, s3TempKey string) (*domain.Post, error) { func (s *postService) CreateWithS3Upload(ctx context.Context, authorID int, title, s3Key string) (*domain.Post, error) {
const op = "service/postService.CreateWithS3Upload" const op = "service/postService.CreateWithS3Upload"
// Валидация // Валидация
if s3TempKey == "" { if s3Key == "" {
return nil, errors.New("S3 temp key cannot be empty") return nil, errors.New("S3 key cannot be empty")
} }
// Проверяем что ключ принадлежит пользователю (дополнительная валидация) // Проверяем что ключ принадлежит пользователю и находится в правильной папке
if !isValidUserTempKey(authorID, s3TempKey) { if !isValidUserPostKey(authorID, s3Key) {
return nil, errors.New("invalid temp key for user") return nil, errors.New("invalid S3 key for user")
} }
// Генерируем временный URL для модерации // Формируем URL для модерации
tempURL := fmt.Sprintf("https://s3.regru.cloud/tailly/%s", s3TempKey) imageURL := fmt.Sprintf("https://s3.regru.cloud/tailly/%s", s3Key)
// Модерация изображения по URL // Модерация изображения по URL
modClient, err := moderation.NewModerationClient("tailly_censor:50051") modClient, err := moderation.NewModerationClient("tailly_censor:50051")
@ -68,29 +68,19 @@ func (s *postService) CreateWithS3Upload(ctx context.Context, authorID int, titl
} }
defer modClient.Close() defer modClient.Close()
allowed, err := modClient.CheckImageURL(ctx, tempURL) allowed, err := modClient.CheckImageURL(ctx, imageURL)
if err != nil { if err != nil {
// Удаляем временный файл при ошибке модерации // Удаляем файл при ошибке модерации
S3.DeleteTempFile(s3TempKey) S3.DeleteFromS3(imageURL)
return nil, fmt.Errorf("%s: image moderation failed: %w", op, err) return nil, fmt.Errorf("%s: image moderation failed: %w", op, err)
} }
if !allowed { if !allowed {
// Удаляем временный файл если не прошло модерацию // Удаляем файл если не прошло модерацию
S3.DeleteTempFile(s3TempKey) S3.DeleteFromS3(imageURL)
return nil, errors.New("image rejected by moderation service") return nil, errors.New("image rejected by moderation service")
} }
// Перемещаем файл из временной папки в постоянную // Создаем пост с уже готовым URL
permanentKey := fmt.Sprintf("posts/%d/%d_%s", authorID, time.Now().UnixNano(), getFilenameFromKey(s3TempKey))
err = S3.MoveFile(s3TempKey, permanentKey)
if err != nil {
S3.DeleteTempFile(s3TempKey) // Удаляем временный файл при ошибке
return nil, fmt.Errorf("%s: failed to move file to permanent location: %w", op, err)
}
imageURL := fmt.Sprintf("https://s3.regru.cloud/tailly/%s", permanentKey)
post := &domain.Post{ post := &domain.Post{
Title: title, Title: title,
Content: imageURL, Content: imageURL,
@ -101,22 +91,18 @@ func (s *postService) CreateWithS3Upload(ctx context.Context, authorID int, titl
if err := s.postRepo.Create(ctx, post); err != nil { if err := s.postRepo.Create(ctx, post); err != nil {
// Если не удалось сохранить пост, удаляем файл из S3 // Если не удалось сохранить пост, удаляем файл из S3
S3.DeleteTempFile(permanentKey) S3.DeleteFromS3(imageURL)
return nil, fmt.Errorf("%s: failed to save post: %w", op, err) return nil, fmt.Errorf("%s: failed to save post: %w", op, err)
} }
return post, nil return post, nil
} }
func isValidUserTempKey(userID int, key string) bool { // Новая функция проверки ключа
// Проверяем что ключ принадлежит пользователю func isValidUserPostKey(userID int, key string) bool {
// Формат: temp/{userID}/{timestamp}_{filename} // Проверяем что ключ принадлежит пользователю и находится в папке posts
return strings.Contains(key, fmt.Sprintf("temp/%d/", userID)) // Формат: posts/{userID}/{timestamp}_{filename}
} return strings.HasPrefix(key, fmt.Sprintf("posts/%d/", userID))
func getFilenameFromKey(key string) string {
parts := strings.Split(key, "/")
return parts[len(parts)-1]
} }
// Создание нового поста // Создание нового поста

View File

@ -87,7 +87,8 @@ func GeneratePresignedUploadURL(userID int, filename string) (string, string, er
return "", "", fmt.Errorf("%s: failed to create S3 client: %w", op, err) return "", "", fmt.Errorf("%s: failed to create S3 client: %w", op, err)
} }
uniqueKey := fmt.Sprintf("temp/%d/%d_%s", userID, time.Now().UnixNano(), filename) // Генерируем ключ сразу в конечной папке с уникальным именем
uniqueKey := fmt.Sprintf("posts/%d/%d_%s", userID, time.Now().UnixNano(), filename)
presignClient := s3.NewPresignClient(client) presignClient := s3.NewPresignClient(client)
@ -105,44 +106,3 @@ func GeneratePresignedUploadURL(userID int, filename string) (string, string, er
return result.URL, uniqueKey, nil return result.URL, uniqueKey, nil
} }
func MoveFile(sourceKey, destinationKey string) error {
const op = "s3.MoveFile"
client, err := getS3Client()
if err != nil {
return fmt.Errorf("%s: failed to create S3 client: %w", op, err)
}
// Копируем файл
_, err = client.CopyObject(context.TODO(), &s3.CopyObjectInput{
Bucket: aws.String("tailly"),
CopySource: aws.String(fmt.Sprintf("tailly/%s", sourceKey)),
Key: aws.String(destinationKey),
})
if err != nil {
return fmt.Errorf("%s: failed to copy file: %w", op, err)
}
// Удаляем исходный файл
return DeleteTempFile(sourceKey)
}
func DeleteTempFile(key string) error {
const op = "s3.DeleteTempFile"
client, err := getS3Client()
if err != nil {
return fmt.Errorf("%s: failed to create S3 client: %w", op, err)
}
_, err = client.DeleteObject(context.TODO(), &s3.DeleteObjectInput{
Bucket: aws.String("tailly"),
Key: aws.String(key),
})
if err != nil {
return fmt.Errorf("%s: failed to delete temp file: %w", op, err)
}
return nil
}