106 lines
2.8 KiB
Go
106 lines
2.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",
|
|
"",
|
|
)),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Используем virtual-hosted-style endpoint
|
|
client := s3.NewFromConfig(cfg, func(o *s3.Options) {
|
|
o.BaseEndpoint = aws.String("https://s3.regru.cloud")
|
|
o.UsePathStyle = false // Отключаем path-style
|
|
})
|
|
|
|
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("posts/%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),
|
|
// Добавляем CORS headers через метаданные
|
|
Metadata: map[string]string{
|
|
"Access-Control-Allow-Origin": "https://tailly.ru",
|
|
"Access-Control-Allow-Methods": "PUT, POST, DELETE",
|
|
"Access-Control-Allow-Headers": "*",
|
|
},
|
|
},
|
|
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
|
|
}
|