rentease/internal/driver/storage/minio.go
Ruidy 9b2510460a
Some checks are pending
CI / checks (push) Waiting to run
feat: store invoice PDFs in minio
2026-03-20 23:58:57 +01:00

101 lines
2.4 KiB
Go

package storage
import (
"bytes"
"context"
"fmt"
"time"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/rjNemo/rentease/internal/config"
"github.com/rjNemo/rentease/internal/service/booking"
)
type MinioInvoiceStorage struct {
bucket string
client *minio.Client
}
func NewMinioInvoiceStorage(cfg *config.Config) (*MinioInvoiceStorage, error) {
if cfg.MinIOEndpoint == "" && cfg.MinIOAccessKey == "" && cfg.MinIOSecretKey == "" && cfg.MinIOBucket == "" {
return nil, nil
}
switch {
case cfg.MinIOEndpoint == "":
return nil, fmt.Errorf("minio endpoint is required")
case cfg.MinIOAccessKey == "":
return nil, fmt.Errorf("minio access key is required")
case cfg.MinIOSecretKey == "":
return nil, fmt.Errorf("minio secret key is required")
case cfg.MinIOBucket == "":
return nil, fmt.Errorf("minio bucket is required")
}
client, err := minio.New(cfg.MinIOEndpoint, &minio.Options{
Creds: credentials.NewStaticV4(cfg.MinIOAccessKey, cfg.MinIOSecretKey, ""),
Secure: cfg.MinIOUseSSL,
})
if err != nil {
return nil, fmt.Errorf("create minio client: %w", err)
}
return &MinioInvoiceStorage{
bucket: cfg.MinIOBucket,
client: client,
}, nil
}
func (s *MinioInvoiceStorage) StoreInvoice(
ctx context.Context,
objectKey string,
file booking.GeneratedFile,
shareURLTTL time.Duration,
) (*booking.StoredInvoiceFile, error) {
if err := s.ensureBucket(ctx); err != nil {
return nil, err
}
_, err := s.client.PutObject(
ctx,
s.bucket,
objectKey,
bytes.NewReader(file.Data),
int64(len(file.Data)),
minio.PutObjectOptions{
ContentType: file.ContentType,
},
)
if err != nil {
return nil, fmt.Errorf("upload object %q: %w", objectKey, err)
}
shareURL, err := s.client.PresignedGetObject(ctx, s.bucket, objectKey, shareURLTTL, nil)
if err != nil {
return nil, fmt.Errorf("presign object %q: %w", objectKey, err)
}
return &booking.StoredInvoiceFile{
ObjectKey: objectKey,
ShareURL: shareURL.String(),
ShareURLExpiresAt: time.Now().UTC().Add(shareURLTTL),
}, nil
}
func (s *MinioInvoiceStorage) ensureBucket(ctx context.Context) error {
exists, err := s.client.BucketExists(ctx, s.bucket)
if err != nil {
return fmt.Errorf("check bucket %q: %w", s.bucket, err)
}
if exists {
return nil
}
if err := s.client.MakeBucket(ctx, s.bucket, minio.MakeBucketOptions{}); err != nil {
return fmt.Errorf("create bucket %q: %w", s.bucket, err)
}
return nil
}