149 lines
3.8 KiB
Go
149 lines
3.8 KiB
Go
package S3
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/aws/aws-sdk-go-v2/aws"
|
||
awsConfig "github.com/aws/aws-sdk-go-v2/config"
|
||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||
)
|
||
|
||
// Получаем S3 клиент
|
||
func getS3Client() (*s3.Client, error) {
|
||
cfg, err := awsConfig.LoadDefaultConfig(context.TODO(),
|
||
awsConfig.WithRegion("ru-central1"),
|
||
awsConfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
|
||
"TJ946G2S1Z5FEI3I7DQQ",
|
||
"C2H2aITHRDpek8H921yhnrINZwDoADsjW3F6HURl",
|
||
"",
|
||
)),
|
||
awsConfig.WithEndpointResolver(aws.EndpointResolverFunc(
|
||
func(service, region string) (aws.Endpoint, error) {
|
||
return aws.Endpoint{
|
||
URL: "https://s3.regru.cloud",
|
||
SigningRegion: "ru-central1",
|
||
Source: aws.EndpointSourceCustom,
|
||
}, nil
|
||
},
|
||
)),
|
||
)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// Создаем клиент с кастомным эндпоинтом
|
||
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
|
||
o.UsePathStyle = true // Важно для некоторых S3 провайдеров
|
||
})
|
||
|
||
return client, nil
|
||
}
|
||
|
||
func DeleteFromS3(imageURL string) error {
|
||
const op = "s3.DeleteFromS3"
|
||
|
||
parts := strings.SplitN(strings.TrimPrefix(imageURL, "https://s3.regru.cloud/"), "/", 2)
|
||
if len(parts) != 2 {
|
||
return fmt.Errorf("%s: invalid S3 URL format", op)
|
||
}
|
||
bucket := parts[0]
|
||
key := parts[1]
|
||
|
||
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(bucket),
|
||
Key: aws.String(key),
|
||
})
|
||
if err != nil {
|
||
return fmt.Errorf("%s: failed to delete object from S3: %w", op, err)
|
||
}
|
||
|
||
// Ждем пока объект исчезнет
|
||
waiter := s3.NewObjectNotExistsWaiter(client)
|
||
err = waiter.Wait(context.TODO(), &s3.HeadObjectInput{
|
||
Bucket: aws.String(bucket),
|
||
Key: aws.String(key),
|
||
}, 30*time.Second)
|
||
if err != nil {
|
||
return fmt.Errorf("%s: failed to wait for object deletion: %w", op, err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func GeneratePresignedUploadURL(userID int, filename string) (string, string, error) {
|
||
const op = "s3.GeneratePresignedUploadURL"
|
||
|
||
client, err := getS3Client()
|
||
if err != nil {
|
||
return "", "", fmt.Errorf("%s: failed to create S3 client: %w", op, err)
|
||
}
|
||
|
||
uniqueKey := fmt.Sprintf("temp/%d/%d_%s", userID, time.Now().UnixNano(), filename)
|
||
|
||
presignClient := s3.NewPresignClient(client)
|
||
|
||
result, err := presignClient.PresignPutObject(context.TODO(),
|
||
&s3.PutObjectInput{
|
||
Bucket: aws.String("tailly"),
|
||
Key: aws.String(uniqueKey),
|
||
ContentLength: aws.Int64(10 * 1024 * 1024),
|
||
},
|
||
s3.WithPresignExpires(15*time.Minute),
|
||
)
|
||
if err != nil {
|
||
return "", "", fmt.Errorf("%s: failed to generate presigned URL: %w", op, err)
|
||
}
|
||
|
||
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
|
||
}
|