mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-06 02:36:49 +00:00
131 lines
3.2 KiB
Go
131 lines
3.2 KiB
Go
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"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")
|
|
}
|
|
|
|
endpoint, secure, err := normalizeEndpoint(cfg.MinIOEndpoint, cfg.MinIOUseSSL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("normalize minio endpoint: %w", err)
|
|
}
|
|
|
|
client, err := minio.New(endpoint, &minio.Options{
|
|
Creds: credentials.NewStaticV4(cfg.MinIOAccessKey, cfg.MinIOSecretKey, ""),
|
|
Secure: secure,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create minio client: %w", err)
|
|
}
|
|
|
|
return &MinioInvoiceStorage{
|
|
bucket: cfg.MinIOBucket,
|
|
client: client,
|
|
}, nil
|
|
}
|
|
|
|
func normalizeEndpoint(raw string, secure bool) (string, bool, error) {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" {
|
|
return "", secure, fmt.Errorf("endpoint is required")
|
|
}
|
|
|
|
if strings.HasPrefix(raw, "http://") || strings.HasPrefix(raw, "https://") {
|
|
u, err := url.Parse(raw)
|
|
if err != nil {
|
|
return "", secure, fmt.Errorf("parse endpoint: %w", err)
|
|
}
|
|
if u.Host == "" {
|
|
return "", secure, fmt.Errorf("endpoint host is required")
|
|
}
|
|
if u.Path != "" && u.Path != "/" {
|
|
return "", secure, fmt.Errorf("endpoint path is not supported")
|
|
}
|
|
return u.Host, u.Scheme == "https", nil
|
|
}
|
|
|
|
return raw, secure, 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
|
|
}
|