- Adiciona coluna `tipo_profissional` à tabela `usuarios` - Atualiza handlers e services do Backend Go para persistir o tipo - Atualiza registro no Frontend para enviar o nome da função (ex: "Cinegrafista") - Corrige uploads S3 para compatibilidade com Civo (PathStyle) - Script para definir política pública de leitura no bucket S3 - Adiciona fallback para imagens de avatar na Navbar
91 lines
3 KiB
Go
91 lines
3 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"photum-backend/internal/config"
|
|
"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"
|
|
)
|
|
|
|
type S3Service struct {
|
|
Client *s3.Client
|
|
PresignClient *s3.PresignClient
|
|
Bucket string
|
|
Region string
|
|
}
|
|
|
|
func NewS3Service(cfg *config.Config) *S3Service {
|
|
// Custom Resolver for Civo Object Store
|
|
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
|
return aws.Endpoint{
|
|
URL: cfg.S3Endpoint,
|
|
SigningRegion: region,
|
|
}, nil
|
|
})
|
|
|
|
awsCfg, err := awsConfig.LoadDefaultConfig(context.TODO(),
|
|
awsConfig.WithRegion(cfg.S3Region),
|
|
awsConfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(cfg.S3AccessKey, cfg.S3SecretKey, "")),
|
|
awsConfig.WithEndpointResolverWithOptions(customResolver),
|
|
)
|
|
if err != nil {
|
|
log.Fatalf("unable to load SDK config, %v", err)
|
|
}
|
|
|
|
client := s3.NewFromConfig(awsCfg, func(o *s3.Options) {
|
|
o.UsePathStyle = true
|
|
})
|
|
presignClient := s3.NewPresignClient(client)
|
|
|
|
return &S3Service{
|
|
Client: client,
|
|
PresignClient: presignClient,
|
|
Bucket: cfg.S3Bucket,
|
|
Region: cfg.S3Region,
|
|
}
|
|
}
|
|
|
|
// GeneratePresignedURL generates a PUT presigned URL for uploading a file
|
|
// returns (uploadUrl, publicUrl, error)
|
|
func (s *S3Service) GeneratePresignedURL(filename string, contentType string) (string, string, error) {
|
|
key := fmt.Sprintf("photum-dev/%d_%s", time.Now().Unix(), filename)
|
|
|
|
req, err := s.PresignClient.PresignPutObject(context.TODO(), &s3.PutObjectInput{
|
|
Bucket: aws.String(s.Bucket),
|
|
Key: aws.String(key),
|
|
ContentType: aws.String(contentType),
|
|
}, s3.WithPresignExpires(15*time.Minute))
|
|
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("failed to sign request: %v", err)
|
|
}
|
|
|
|
// Construct public URL - Path Style
|
|
// URL: https://objectstore.nyc1.civo.com/rede5/uploads/...
|
|
// We need to clean the endpoint string if it has https:// prefix for Sprintf if we construct manually,
|
|
// or just reuse the known endpoint structure.
|
|
// cfg.S3Endpoint is "https://objectstore.nyc1.civo.com"
|
|
|
|
// Assuming s.Client.Options().BaseEndpoint is not easily accessible here without plumbing,
|
|
// we will construct it based on the hardcoded knowledge of Civo or pass endpoint to struct.
|
|
// But simply: S3Endpoint + "/" + Bucket + "/" + key is the standard path style.
|
|
|
|
// Note: config.S3Endpoint includes "https://" based on .env
|
|
// We entered: S3_ENDPOINT=https://objectstore.nyc1.civo.com
|
|
|
|
// So: https://objectstore.nyc1.civo.com/rede5/key
|
|
|
|
// However, we don't have access to cfg here directly, but we rely on hardcoding for Civo in previous step or we should store Endpoint in struct.
|
|
// Better to store Endpoint in struct to be clean.
|
|
// For now, I'll use the domain directly as I did before, but path style.
|
|
|
|
publicURL := fmt.Sprintf("https://%s/%s/%s", "objectstore.nyc1.civo.com", s.Bucket, key)
|
|
|
|
return req.URL, publicURL, nil
|
|
}
|