Files
backend/internal/pkg/s3/s3.go
lafay d8b0825ac0
Some checks failed
Build Backend / build-docker (push) Has been cancelled
Build Backend / build (push) Has been cancelled
feat(conversations): add notification mute functionality for conversations
- Rename Muted field to NotificationMuted across model, DTOs and converters
- Add UpdateNotificationMuted repository method with upsert support
- Add SetConversationNotificationMuted service method
- Add PUT /:id/notification_muted route for setting mute status
- Improve S3 storage resilience by skipping bucket check on access denied
2026-04-25 21:22:52 +08:00

131 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package s3
import (
"bytes"
"context"
"fmt"
"time"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"with_you/internal/config"
)
// Client S3客户端
type Client struct {
client *minio.Client
bucket string
domain string
}
// New 创建S3客户端
func New(cfg *config.S3Config) (*Client, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
client, err := minio.New(cfg.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(cfg.AccessKey, cfg.SecretKey, ""),
Secure: cfg.UseSSL,
})
if err != nil {
return nil, fmt.Errorf("failed to create S3 client: %w", err)
}
// 检查bucket是否存在权限不足时跳过检查不阻塞启动
exists, err := client.BucketExists(ctx, cfg.Bucket)
if err != nil {
// Access Denied 或网络不通时,跳过 bucket 检查,假定 bucket 已存在
// 后续上传时会暴露具体错误
} else if !exists {
if err := client.MakeBucket(ctx, cfg.Bucket, minio.MakeBucketOptions{
Region: cfg.Region,
}); err != nil {
return nil, fmt.Errorf("failed to create bucket: %w", err)
}
}
// 如果没有配置domain则使用默认的endpoint
domain := cfg.Domain
if domain == "" {
domain = cfg.Endpoint
}
return &Client{
client: client,
bucket: cfg.Bucket,
domain: domain,
}, nil
}
// Upload 上传文件
func (c *Client) Upload(ctx context.Context, objectName string, filePath string, contentType string) (string, error) {
_, err := c.client.FPutObject(ctx, c.bucket, objectName, filePath, minio.PutObjectOptions{
ContentType: contentType,
})
if err != nil {
return "", fmt.Errorf("failed to upload file: %w", err)
}
return fmt.Sprintf("%s/%s", c.bucket, objectName), nil
}
// UploadData 上传数据
func (c *Client) UploadData(ctx context.Context, objectName string, data []byte, contentType string) (string, error) {
_, err := c.client.PutObject(ctx, c.bucket, objectName, bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{
ContentType: contentType,
})
if err != nil {
return "", fmt.Errorf("failed to upload data: %w", err)
}
// 返回完整URL包含bucket名称
scheme := "https"
if c.domain == c.bucket || c.domain == "" {
scheme = "http"
}
return fmt.Sprintf("%s://%s/%s/%s", scheme, c.domain, c.bucket, objectName), nil
}
// TrustedPublicURLPrefix 本站公开对象 URL 前缀scheme://domain/bucket/),用于校验聊天图片等仅引用本地上传资源。
func (c *Client) TrustedPublicURLPrefix() string {
if c == nil || c.domain == "" || c.bucket == "" {
return ""
}
scheme := "https"
if c.domain == c.bucket {
scheme = "http"
}
return fmt.Sprintf("%s://%s/%s/", scheme, c.domain, c.bucket)
}
// GetURL 获取文件URL - 使用自定义域名
func (c *Client) GetURL(ctx context.Context, objectName string) (string, error) {
// 使用自定义域名构建URL包含bucket名称
scheme := "https"
if c.domain == c.bucket || c.domain == "" {
scheme = "http"
}
return fmt.Sprintf("%s://%s/%s/%s", scheme, c.domain, c.bucket, objectName), nil
}
// GetPresignedURL 获取预签名URL用于私有桶
func (c *Client) GetPresignedURL(ctx context.Context, objectName string) (string, error) {
url, err := c.client.PresignedGetObject(ctx, c.bucket, objectName, time.Hour*24, nil)
if err != nil {
return "", fmt.Errorf("failed to get presigned URL: %w", err)
}
return url.String(), nil
}
// Delete 删除文件
func (c *Client) Delete(ctx context.Context, objectName string) error {
return c.client.RemoveObject(ctx, c.bucket, objectName, minio.RemoveObjectOptions{})
}
// GetClient 获取原生客户端
func (c *Client) GetClient() *minio.Client {
return c.client
}