90ac0b81d1
Add full V2 architecture: identity, content, studio (.NET 10) and file, render, notification, gateway (Go) services with vendored deps, plus DB migrations, event/API contracts, and an init-db script. Wire the Next.js frontend to the gateway: server-side JWT auth routes (login/register/refresh/logout/me), gateway fetch helper, and session/ cookie/jwt helpers under src/lib. Containerize the stack via docker-compose.v2.yml and per-service Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via next/font/local to avoid Google Fonts (geo-blocked). Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
76 lines
2.0 KiB
Go
76 lines
2.0 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/minio/minio-go/v7"
|
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
)
|
|
|
|
type MinioClient struct {
|
|
client *minio.Client
|
|
endpoint string
|
|
}
|
|
|
|
type Config struct {
|
|
Endpoint string
|
|
AccessKeyID string
|
|
SecretAccessKey string
|
|
UseSSL bool
|
|
}
|
|
|
|
func NewMinioClient(cfg Config) (*MinioClient, error) {
|
|
client, err := minio.New(cfg.Endpoint, &minio.Options{
|
|
Creds: credentials.NewStaticV4(cfg.AccessKeyID, cfg.SecretAccessKey, ""),
|
|
Secure: cfg.UseSSL,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("minio client: %w", err)
|
|
}
|
|
return &MinioClient{client: client, endpoint: cfg.Endpoint}, nil
|
|
}
|
|
|
|
func (m *MinioClient) PresignedPutURL(ctx context.Context, bucket, key string, expiry time.Duration) (string, error) {
|
|
u, err := m.client.PresignedPutObject(ctx, bucket, key, expiry)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return u.String(), nil
|
|
}
|
|
|
|
func (m *MinioClient) PresignedGetURL(ctx context.Context, bucket, key string, expiry time.Duration) (string, error) {
|
|
reqParams := url.Values{}
|
|
u, err := m.client.PresignedGetObject(ctx, bucket, key, expiry, reqParams)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return u.String(), nil
|
|
}
|
|
|
|
func (m *MinioClient) GenerateKey(folder string) string {
|
|
return fmt.Sprintf("%s/%s", folder, uuid.New().String())
|
|
}
|
|
|
|
func (m *MinioClient) DeleteObject(ctx context.Context, bucket, key string) error {
|
|
return m.client.RemoveObject(ctx, bucket, key, minio.RemoveObjectOptions{})
|
|
}
|
|
|
|
func (m *MinioClient) GetObject(ctx context.Context, bucket, key string) (io.ReadCloser, error) {
|
|
obj, err := m.client.GetObject(ctx, bucket, key, minio.GetObjectOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return obj, nil
|
|
}
|
|
|
|
func (m *MinioClient) InitiateMultipartUpload(ctx context.Context, bucket, key string) (string, error) {
|
|
// MinIO SDK doesn't directly expose multipart — use presigned PUT per chunk instead.
|
|
// Return a generated upload ID for session tracking.
|
|
return uuid.New().String(), nil
|
|
}
|